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.