Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save kohheepeace/2a3397e5992c97ded4dbad531b734790 to your computer and use it in GitHub Desktop.
Save kohheepeace/2a3397e5992c97ded4dbad531b734790 to your computer and use it in GitHub Desktop.

Revisions

  1. Helio Campos Mello de Andrade created this gist Sep 28, 2017.
    119 changes: 119 additions & 0 deletions database_migration_best_practices.md
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,119 @@
    # Database migration best practices for Rails

    ## Never ever change data on schema changes! [1]

    ## Use rake tasks to change the data! [1]
    This decouples a deployment from completed migrations.
    Give us control of the data manipulation proccess by encapsulatin it in on place.
    need to remember to:
    1. Run it in one of the ways bellow:
    a. Add this rake task the deployment script or;
    b. Run it mannually after the deployment.
    2. Need to clean up after the end of the data manipulation;
    3. Need to remove the rake task after the deployment is implemented;
    Example of rake task:
    ```ruby
    # lib/tasks/temporary/users.rake
    namespace :users do
    desc "Update confirmed users to receive newsletter"
    task set_newsletter: :environment do
    users = User.confirmed
    puts "Going to update #{users.count} users"

    ActiveRecord::Base.transaction do
    users.each do |user|
    user.mark_newsletter_received!
    print "."
    end
    end

    puts " All done now!"
    end
    end
    ```

    ## Read-Only Models [2]
    If you don't need to save the model, then make it read-only with:
    ```ruby
    class Role < ActiveRecord::Base
    def readonly?
    true
    end
    end
    ```
    This way the model can be modified without concerns.

    ## Removing columns [2]
    If you need to remove a column, first deploy an update so the ActiveRecord cache ignore it like this:
    ```ruby
    class User
    def self.columns
    super.reject { |c| c.name == "notes" }
    end
    end
    ```
    Then you may safelly remove the column in the next deploy.

    ## Renaming columns [2]
    Do it i three steps:
    1. Add the new column (first_name) and read/write from/to both columns (first_name failling back to fname). Don't change any queries yet!
    ```ruby
    def first_name
    super || attributes["fname"]
    end
    ```
    2. Populate the new column with data from the old column and update the queries.
    3. Delete the old column.

    ## Adding NOT NULL constraint [2]
    1. Make sure you are writing to the columnn that will receive the constraint
    ```ruby
    before_save :assign_defaults

    def assign_defaults
    self.admin ||= false
    end
    ```
    2. Update all existing records that have the column as null.
    3. Add the constraint.

    ## Creating indexes [2]
    1. Rails don't create indexes concurrently and, if the table receives lots os writes, you need it. the only way is to use raw SQL.
    ```ruby
    class IndexUsersEmails < ActiveRecord::Migration
    def ddl_transaction(&block)
    block.call # do not start a transaction
    end

    def self.up
    execute "CREATE INDEX CONCURRENTLY index_users_on_email ON users(email)"
    end
    end
    ```
    Rails will dump the index to a Ruby schema normally.

    ## Cheat sheet [2]
    ### Adding columns
    – Safe for readonly models
    – Safe when there are no constraints on the column
    ### Removing columns
    – Safe for readonly models
    – Tell AR to ignore the column first
    ### Renaming columns
    – Not safe
    – First add a new column, then remove the old one
    – When the column is used on SQL queries you’ll need to split this in three steps
    ### Creating tables
    – Safe
    ### Removing tables
    – Safe
    ### Creating indexes
    – Safe only for readonly models
    – Otherwise make sure you create indexes concurrently
    ### Removing indexes
    – Safe

    ## Bibliografia
    1. https://robots.thoughtbot.com/data-migrations-in-rails
    2. https://blog.codeship.com/rails-migrations-zero-downtime/
    3. http://engineering.harrys.com/2014/06/09/seed-migrations.html