Drop a table in production
In this post, we will see an approach how to safely drop a table from a production application without running into weird issues.
As an example, we will use a simple blog that allows adding comments to each post and also has a simple stats table where it tracks likes, views, and shares of posts.
The model diagram for this example is the following:
Class diagram
From the application, we are interested in the schema file and the Stat class:
# db/schema.rb
ActiveRecord::Schema.define(version: 2018_08_08_153342) do
create_table "comments", force: :cascade do |t|
t.string "username"
t.string "content"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.integer "post_id"
t.index ["post_id"], name: "index_comments_on_post_id"
end
create_table "posts", force: :cascade do |t|
t.string "title"
t.string "content"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
create_table "stats", force: :cascade do |t|
t.integer "likes"
t.integer "views"
t.integer "shares"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.integer "post_id"
t.index ["post_id"], name: "index_stats_on_post_id"
end
end
The stat class:
# app/models/stat.rb
class Stat < ApplicationRecord
belongs_to :post
class << self
def on_like(post_id)
stat = Stat.find_by(post_id: post_id)
stat.increment!(:likes)
end
def on_view(post_id)
stat = Stat.find_by(post_id: post_id)
stat.increment!(:views)
end
def on_share(post_id)
stat = Stat.find_by(post_id: post_id)
stat.increment!(:shares)
end
end
end
Our task is to remove the stats table because we will be using a third party provider for tracking. So let’s get started:
1. Search and Update
This is the most crucial step, it depends on the project size and it requires patience and precision.
Generally, we want to perform the search using this order:
- The class name to delete
- Possible association of the model
- Method names of the model
2. Testing
At this point, we will have some failing tests, so we need to update those tests to make the suite green again!.
We should keep in mind that we should also do some manual testing too. It’s not just clicking through the application but we need to keep an eye on our logs to check for warnings.
3. Check background jobs
This step depends on the project, in our example let’s pretend that we have a background job that processes our blog stats weekly to generate a report.
# app/jobs/generate_stats_report_worker.rb
class GenerateStatsReportWorker
include Sidekiq::Worker
def perform(stat_ids)
stats = Stat.where(id: stat_ids)
reports = generate_report(stats)
send_reports
end
end
This worker will be scheduled to execute with an array of Stat ids as parameters. It’s clear that we will have a lot of errors if we drop the Stat model while this job is still in the scheduled queue. So what to do in this case?
There is no concrete answer to this question, it depends on the importance of the background job, the best tip for this case is to check with your project manager or your senior colleagues. But the main two options are:
- Delete the enqueued jobs
- Run the enqueued jobs before deploying the changes.
4. Deployment
Finally, we reached the last step in the process, we are ready now to deploy our changes.
So it’s time to add the migration:
class DropStatsTable < ActiveRecord::Migration[5.2]
def change
drop_table :stats
end
end
There are different recipes in deployment, so to be sure that we don’t end up in a state where our code in production relies on the existence of Stats table while its dropped, we need to submit two pull requests.
- The first pull request will include only code changes without dropping the table
- The second pull request will include the drop migration
That’s it, we saw together a four steps process to delete a table from production with confidence! So have fun and keep coding 🙂