Often, we rename a column and deploy to find out the Honeybadger screaming at us with the errors accessing column with the old name!
The Problem
ActiveRecord caches the columns and uses column names from the cache to build INSERT
and UPDATE
queries.
So, when a record is updated,
activerecord tries to update all the columns,
including the one which we recently renamed.
This is our migration to rename f_name
column to first_name
.
Upon deployment,
the old processes which are still referring to the f_name
column in the cache
will try to access/write it to the database and raise 500
error.
Solution
The solution involves multiple migrations + deployments for a single change. This definitely sounds over-engineered than showing a maintenance page but that is what we will need to do in-order to make it smooth and deploy without affecting the online users.
To make it seamless, we deploy after each of the following steps.
1. Add a new column
2. Migrate the data
Here, we migrated existing data. But until our next deployment, in-order to save the data in new column, we will add a setter methods as following.
class User
attr_accessible :f_name
def f_name=(val)
self.first_name = val
end
end
3. Remove the old column
In this step, we have all the data in first_name
column. So we can get rid of the f_name
.
This works fine, but if you are removing a column from a frequently updated table, you might want to add following script to your model so that Rails does not try to update the old column.
class User
def self.columns
super.reject { |c| c.name == "f_name" }
end
end
Remove this in your next deployment.
Finally,
In this post, we saw how we can deploy a simple migration to rename a column name with zero-downtime.
I will be posting more on Zero-Downtime use cases like adding a not null column or adding indexes(which locks the table) etc in the future.