Robust migrations

July 27th, 2007

I often end up creating migrations which fail in the middle of setup, resulting in a well-known mess: you fix the bug, revert to previous version, re-migrate and realize that the table/column modification cannot be done because it has already been done (error occurred later). So I ended up with the following idiom, which is easy enough to abstract away in another library to have consistent, robust migrations. Here’s the code without further ado:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class MyFancyMigration < ActiveRecord::Migration
  def self.up
    # add_column / alter_table or similar structural change
    begin
      say_with_time "doing post-migration stuff, please be patient" do
        # post-migration code, where errors usually occur
      end
    rescue => e
      down # revert structural changes
      raise # re-raise errors
    end
  end

  def self.down
    # remove_column / alter table or similar reverting structural change (if possible)
  end
end

Now, omitting the trivial case where everything goes fine, there are basically two interesting cases; either the structural change or post-migration code fails. If the former fails, you get normal traceback and after fixing the bug you won’t have problems (because no changes were done). In the latter case where post-migration, custom code fails, rescue code is executed and structural changes are reverted. Only then we re-raise the error. Now remigrating works because the database is in the same state as before the first run of new migration.

Caveat: the migration could fail bad if structural changes contain more than one statement. However, as migrations tend to do only one change at a time I’ve never had such cases so far, so it shouldn’t be too severe a problem.

1 Response to “Robust migrations”

  1. EdvardM Says:

    See http://dev.rubyonrails.org/ticket/5470 for possibly better solution, in short it wraps migration to transaction if used database adapter is known to support transactions.

Sorry, comments are closed for this article.