Search code examples
rubyrails-migrations

strategy for rails data and model migration


I need to migrate some data. I need to decrypt some fields (and that's no problem) but when I'm done I need to update the model to use a different strategy for the data access.

Is there a way to avoid two deployments? (One for the migration and one for the model update after the migration is complete?) I need to use the old model in the migration, but then I need the new one.


Solution

  • You can add a dummy ActiveRecord model within your migration. Then your migration does not depend on the original model and its implementation anymore. That allows you to update the code in the original model.

    Something like this:

    class MigrateFooOnBar < ActiveRecord::Migration
      class Bar < ActiveRecord::Base
        def foo_migration
          self.foo = some_deprecated_code
        end
      end
    
      def up
        add_column :bars, :foo
        Bar.find_each do |bar|
          bar.foo_migrate
          bar.save!
        end
      end
    
    # ...
    end
    

    But this not solve the core problem: A migration never runs at the exact same time when your application is deployed. When you deploy an application, the steps that does the migration runs before or after switching to the new code and the Rails app`s restart.

    Depending on how long your migration takes - and it could run for minutes (and hours) on big tables - your app will face the situation in which it runs old code on a newer database schema, or has to run new code on an old database schema.

    To avoid taking the app offline when running such migrations you will have to deploy multiple steps:

    1. Deploy a migration that add a new columns to the database
    2. Deploy the code changes that can run with both versions of the schema
    3. Run data transfermation and backfill tasks for old data
    4. Deploy code that removes backwards compability
    5. Run a migration removing the old columns