README.md in strong_migrations-0.4.0 vs README.md in strong_migrations-0.4.1

- old
+ new

@@ -1,8 +1,8 @@ # Strong Migrations -Catch unsafe migrations at dev time +Catch unsafe migrations in development :tangerine: Battle-tested at [Instacart](https://www.instacart.com/opensource) [![Build Status](https://travis-ci.org/ankane/strong_migrations.svg?branch=master)](https://travis-ci.org/ankane/strong_migrations) @@ -23,11 +23,11 @@ ## Dangerous Operations The following operations can cause downtime or errors: - [[+]](#removing-a-column) removing a column -- [[+]](#adding-a-column-with-a-default-value) adding a column with a non-null default value to an existing table +- [[+]](#adding-a-column-with-a-default-value) adding a column with a default value - [[+]](#backfilling-data) backfilling data - [[+]](#adding-an-index) adding an index non-concurrently - [[+]](#adding-a-reference) adding a reference - [[+]](#adding-a-foreign-key) adding a foreign key - [[+]](#renaming-or-changing-the-type-of-a-column) changing the type of a column @@ -37,11 +37,11 @@ - [[+]](#using-change_column_null-with-a-default-value) using `change_column_null` with a default value - [[+]](#adding-a-json-column) adding a `json` column Also checks for best practices: -- [[+]](#) keeping non-unique indexes to three columns or less +- [[+]](#keeping-non-unique-indexes-to-three-columns-or-less) keeping non-unique indexes to three columns or less ## The Zero Downtime Way ### Removing a column @@ -82,11 +82,11 @@ ### Adding a column with a default value #### Bad -Adding a column with a non-null default causes the entire table to be rewritten. +Adding a column with a default value to an existing table causes the entire table to be rewritten. ```ruby class AddSomeColumnToUsers < ActiveRecord::Migration[5.2] def change add_column :users, :some_column, :text, default: "default_value" @@ -139,11 +139,11 @@ ```ruby class BackfillSomeColumn < ActiveRecord::Migration[5.2] disable_ddl_transaction! def change - User.in_batches do |relation| + User.unscoped.in_batches do |relation| relation.update_all some_column: "default_value" sleep(0.1) # throttle end end end @@ -151,11 +151,11 @@ ### Adding an index #### Bad -In Postgres, adding a non-concurrent indexes lock the table. +In Postgres, adding a non-concurrent index locks the table. ```ruby class AddSomeIndexToUsers < ActiveRecord::Migration[5.2] def change add_index :users, :some_column @@ -328,11 +328,11 @@ 3. Backfill data from the old table to new table 4. Move reads from the old table to the new table 5. Stop writing to the old table 6. Drop the old table -### Creating a table with the `force` option +### Creating a table with the force option #### Bad The `force` option can drop an existing table. @@ -358,11 +358,11 @@ end end end ``` -### Using `change_column_null` with a default value +### Using change_column_null with a default value #### Bad This generates a single `UPDATE` statement to set the default value. @@ -416,11 +416,11 @@ ### Keeping non-unique indexes to three columns or less #### Bad -Adding an index with more than three columns only helps on extremely large tables. +Adding a non-unique index with more than three columns rarely improves performance. ```ruby class AddSomeIndexToUsers < ActiveRecord::Migration[5.2] def change add_index :users, [:a, :b, :c, :d] @@ -428,14 +428,16 @@ end ``` #### Good +Instead, start an index with columns that narrow down the results the most. + ```ruby class AddSomeIndexToUsers < ActiveRecord::Migration[5.2] def change - add_index :users, [:a, :b, :c] + add_index :users, [:b, :d] end end ``` > For Postgres, be sure to add them concurrently @@ -480,14 +482,14 @@ Use the version from your latest migration. ## Dangerous Tasks -For safety, dangerous rake tasks are disabled in production - `db:drop`, `db:reset`, `db:schema:load`, and `db:structure:load`. To get around this, use: +For safety, dangerous database tasks are disabled in production - `db:drop`, `db:reset`, `db:schema:load`, and `db:structure:load`. To get around this, use: ```sh -SAFETY_ASSURED=1 rake db:drop +SAFETY_ASSURED=1 rails db:drop ``` ## Faster Migrations Only dump the schema when adding a new migration. If you use Git, create an initializer with: @@ -513,35 +515,43 @@ StrongMigrations.error_messages[:add_column_default] = "Your custom instructions" ``` Check the [source code](https://github.com/ankane/strong_migrations/blob/master/lib/strong_migrations.rb) for the list of keys. -## Analyze Tables (Postgres) +## Postgres-Specific Features +### Analyze Tables + Analyze tables automatically (to update planner statistics) after an index is added. Create an initializer with: ```ruby StrongMigrations.auto_analyze = true ``` -## Lock Timeout (Postgres) +### Lock Timeout It’s a good idea to set a lock timeout for the database user that runs migrations. This way, if migrations can’t acquire a lock in a timely manner, other statements won’t be stuck behind it. Here’s a great explanation of [how lock queues work](https://www.citusdata.com/blog/2018/02/15/when-postgresql-blocks/). ```sql ALTER ROLE myuser SET lock_timeout = '10s'; ``` There’s also [a gem](https://github.com/gocardless/activerecord-safer_migrations) you can use for this. -## Bigint Primary Keys (Postgres & MySQL) +### Target Version -Rails 5.1+ uses `bigint` for primary keys to keep you from running out of ids. To get this in earlier versions of Rails, check out [rails-bigint-primarykey](https://github.com/Shopify/rails-bigint-primarykey). +If your development database version is different from production, you can specify the production version so the right checks are run in development. +```ruby +StrongMigrations.target_postgresql_version = 10 # or 9.6, etc +``` + +For safety, this option only affects development and test environments. In other environments, the actual server version is always used. + ## Additional Reading - [Rails Migrations with No Downtime](https://pedro.herokuapp.com/past/2011/7/13/rails_migrations_with_no_downtime/) -- [Safe Operations For High Volume PostgreSQL](https://www.braintreepayments.com/blog/safe-operations-for-high-volume-postgresql/) +- [PostgreSQL at Scale: Database Schema Changes Without Downtime](https://medium.com/braintree-product-technology/postgresql-at-scale-database-schema-changes-without-downtime-20d3749ed680) ## Credits Thanks to Bob Remeika and David Waller for the [original code](https://github.com/foobarfighter/safe-migrations) and [Sean Huber](https://github.com/LendingHome/zero_downtime_migrations) for the bad/good readme format.