diff options
Diffstat (limited to 'doc/development/migration_style_guide.md')
-rw-r--r-- | doc/development/migration_style_guide.md | 95 |
1 files changed, 59 insertions, 36 deletions
diff --git a/doc/development/migration_style_guide.md b/doc/development/migration_style_guide.md index 3e993243855..4cf546173de 100644 --- a/doc/development/migration_style_guide.md +++ b/doc/development/migration_style_guide.md @@ -35,16 +35,36 @@ and post-deployment migrations (`db/post_migrate`) are run after the deployment ## Schema Changes -Migrations that make changes to the database schema (e.g. adding a column) can -only be added in the monthly release, patch releases may only contain data -migrations _unless_ schema changes are absolutely required to solve a problem. +Changes to the schema should be commited to `db/structure.sql`. This +file is automatically generated by Rails, so you normally should not +edit this file by hand. If your migration is adding a column to a +table, that column will be added at the bottom. Please do not reorder +columns manually for existing tables as this will cause confusing to +other people using `db/structure.sql` generated by Rails. + +When your local database in your GDK is diverging from the schema from +`master` it might be hard to cleanly commit the schema changes to +Git. In that case you can use the `scripts/regenerate-schema` script to +regenerate a clean `db/structure.sql` for the migrations you're +adding. This script will apply all migrations found in `db/migrate` +or `db/post_migrate`, so if there are any migrations you don't want to +commit to the schema, rename or remove them. If your branch is not +targetting `master` you can set the `TARGET` environment variable. + +```shell +# Regenerate schema against `master` +scripts/regenerate-schema + +# Regenerate schema against `12-9-stable-ee` +TARGET=12-9-stable-ee scripts/regenerate-schema +``` ## What Requires Downtime? The document ["What Requires Downtime?"](what_requires_downtime.md) specifies various database operations, such as -- [adding, dropping, and renaming columns](what_requires_downtime.md#adding-columns) +- [dropping and renaming columns](what_requires_downtime.md#dropping-columns) - [changing column constraints and types](what_requires_downtime.md#changing-column-constraints) - [adding and dropping indexes, tables, and foreign keys](what_requires_downtime.md#adding-indexes) @@ -307,6 +327,34 @@ def down end ``` +**Usage with `disable_ddl_transaction!`** + +Generally the `with_lock_retries` helper should work with `disabled_ddl_transaction!`. A custom RuboCop rule ensures that only allowed methods can be placed within the lock retries block. + +```ruby +disable_ddl_transaction! + +def up + with_lock_retries do + add_column :users, :name, :text + end + + add_text_limit :users, :name, 255 # Includes constraint validation (full table scan) +end +``` + +The RuboCop rule generally allows standard Rails migration methods, listed below. This example will cause a rubocop offense: + +```ruby +disabled_ddl_transaction! + +def up + with_lock_retries do + add_concurrent_index :users, :name + end +end +``` + ### When to use the helper method The `with_lock_retries` helper method can be used when you normally use @@ -330,8 +378,6 @@ Example changes: - `change_column_default` - `create_table` / `drop_table` -**Note:** `with_lock_retries` method **cannot** be used with `disable_ddl_transaction!`. - **Note:** `with_lock_retries` method **cannot** be used within the `change` method, you must manually define the `up` and `down` methods to make the migration reversible. ### How the helper method works @@ -506,34 +552,12 @@ You can read more about adding [foreign key constraints to an existing column](d ## Adding Columns With Default Values -When adding columns with default values to non-empty tables, you must use -`add_column_with_default`. This method ensures the table is updated without -requiring downtime. This method is not reversible so you must manually define -the `up` and `down` methods in your migration class. - -For example, to add the column `foo` to the `projects` table with a default -value of `10` you'd write the following: - -```ruby -class MyMigration < ActiveRecord::Migration[4.2] - include Gitlab::Database::MigrationHelpers - disable_ddl_transaction! - - def up - add_column_with_default(:projects, :foo, :integer, default: 10) - end - - def down - remove_column(:projects, :foo) - end -end -``` +With PostgreSQL 11 being the minimum version since GitLab 13.0, adding columns with default values has become much easier and +the standard `add_column` helper should be used in all cases. -Keep in mind that this operation can easily take 10-15 minutes to complete on -larger installations (e.g. GitLab.com). As a result, you should only add -default values if absolutely necessary. There is a RuboCop cop that will fail if -this method is used on some tables that are very large on GitLab.com, which -would cause other issues. +Before PostgreSQL 11, adding a column with a default was problematic as it would +have caused a full table rewrite. The corresponding helper `add_column_with_default` +has been deprecated and will be removed in a later release. ## Changing the column default @@ -574,8 +598,7 @@ without requiring `disable_ddl_transaction!`. ## Updating an existing column To update an existing column to a particular value, you can use -`update_column_in_batches` (`add_column_with_default` uses this internally to -fill in the default value). This will split the updates into batches, so we +`update_column_in_batches`. This will split the updates into batches, so we don't update too many rows at in a single statement. This updates the column `foo` in the `projects` table to 10, where `some_column` @@ -701,7 +724,7 @@ set the limit to 8-bytes. This will allow the column to hold a value up to Rails migration example: ```ruby -add_column_with_default(:projects, :foo, :integer, default: 10, limit: 8) +add_column(:projects, :foo, :integer, default: 10, limit: 8) ``` ## Timestamp column type |