image by Marcos Mayer
Use Multiple Migrations When Adding Database Constraints
Adding constraints to your application at the database level is a good idea as it provides an extra layer of quality control on top of the data powering your application.
One way to do that is to add default values or constraints—like making sure a field can’t be blank. This is easy to do with Rails’ migrations. But beware…
…adding a column and stipulating a default non-null value all in one go:
class AddComposerToSongs < ActiveRecord::Migration[5.2] def change add_column :songs, :composer, :string, default: "Lin-Manuel Miranda", null: false end end
…a ‘multiple migration’ strategy to add a field with a constraint to your databases.
Add a column
class AddComposerToSongs < ActiveRecord::Migration[5.2] def change add_column :songs, :composer, :string end end
Deploy code that sets default, in your application
class Song < ApplicationRecord before_save :set_default_composer def set_default_composer self.composer = "Lin-Manuel Miranda" if composer.nil? end end
This will catch any code that writes to the database during this intermediate stage.
Update records with nil values
rake task or using
Song.where(composer: nil).find_each do |song| song.update(composer: "Lin-Manuel Miranda") end
Change the column to have a constraint
class AddRecommendedIndexes < ActiveRecord::Migration[5.1] def change change_column :songs, :composer, :string, default: "Lin-Manuel Miranda", null: false end end
…and then you can delete the Ruby code that sets a default.
If you take the “all in one” approach you run the risk of significant downtime for your application.
If you set
null: false in your migration, the database will rewrite the whole table, locking it whilst doing so. This may take a significant amount of time, depending on how much data you already have stored. Locking your database’s table will likely cause write timeouts for any users trying to write to your database at the same time.
The multi-stage deployment is a bit of a pain, but it enables you to keep the application available for your users during any migration of data tables.
If you’re starting a new app or have a very limited dataset.
You might get away with a whole table rewrite and write-lock if you have low amounts of traffic writing to the database. But you would be getting away with it. Chances are you’re going to have some downtime.
If you don’t mind putting your application into maintenance mode, where users are blocked from using the site, you could run the migration with downtime.
Last updated on January 13th, 2019 by @andycroll
An email newsletter, with one Ruby/Rails technique delivered with a ‘why?’ and a ‘how?’ every two weeks. It’s deliberately brief, focussed & opinionated.