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”
Sorry, comments are closed for this article.

August 1st, 2007 at 07:21
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.