Faster ActiveRecord Migrations Using change_table :bulk

A migration:

def up
  add_column :table, :useful_foreign_key, :integer
  add_index :table, :useful_foreign_key
  add_column :table, :summary, :string
end

Executed on a large table:

==  Table: migrating ===============================
-- add_column(:table, :useful_foreign_key, :integer)
  -> 2731.1005s
-- add_index(:table, :useful_foreign_key)
  -> 2704.8428s
-- add_column(:table, :summary, :string)
  -> 2819.9803s
==  Table: migrated (8255.9236s) ======================

According to my made up but based on having done this a bunch of times numbers that’s almost 2.5 hours! Production is going to have to go down for hours to run this. Co-workers will unsuspectingly migrate and you’ll be blamed for a massive loss of productivity.

The wise know that grouping alter statements can significantly speed up a migration (at least in MySQL). Time to break out execute and do it by hand, right?

Nope, there’s a better option.

def up
  change_table :table, :bulk => true do |t| 
    t.integer :useful_foreign_key
    t.index   :useful_foreign_key
    t.string  :summary
  end
end

Instead of individual alter statements we combine them inside change_table and pass :bulk => true.

Now when we execute:

==  Table: migrating =================
-- change_table(:table, {:bulk=>true})
  -> 2774.1011s
==  Table: migrated (2774.1011s) ========

The alter statements have been combined into one statement and the total time to migrate is slightly more than running just one of the alters (again this is with MySQL).

I’m not sure what kind of improvement you’ll get on non-MySQL databases but I suspect it’ll be faster for any database that allows grouped alter statements. As a side note, :bulk will be ignored if the database can’t handle it. Which makes me wonder why it’s not true by default.

Happy migrating!

About

Codewright (mostly Ruby), gin drinker, football watcher, husband and father.

AaronLasseigne
@AaronLasseigne