Sandip Mane
by Sandip Mane
1 min read

Categories

  • Ruby on Rails

Tags

  • migrations
  • deployment

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.

And as always, thanks for reading!😃