name: squash-migrations description: Guidelines for squashing old Canvas database migrations allowed-tools: Bash(pg_dump*), Bash(git diff*), Bash(git status*), Bash(ls *), Bash(git mv *), Bash(git rm *), Edit, Glob, Grep, Read, Write
Squashing migrations is the process of going through individual migrations in db/migrate by date, and "squashing" them into the InitCanvasDb migration, then deleting the original.
Canvas' policy is that we squash migrations quarterly, with a one quarter lag. In other words, migrations from Q1 are squashed beginning in Q3. Do one month's worth of migration per commit, to ease review complexity.
Plugins in gems/plugins/*/db/migrate might also have migrations that need squashed into a base migration for that plugin, similar to InitCanvasDb. Plugin migrations use a single commit for the entire quarter, since they rarely have migrations.
Rules
change_tableblocks that add new structures can be moved into the correspondingcreate_tableblock.- Individual DDL statements such as
add_index,add_reference, etc. should also be moved to the correspondingcreate_tableblock, and modified as appropriate if their arguments differ. remove_-style statements should result in the removal of the correspondingadd_-style structure fromInitCanvasDb. Check the model file for any removed columns, and if the column has been ignored there, remove it from the list. Remove theignored_columnsline completely if the list is empty.create_tableblocks should be moved intoInitCanvasDbcompletely, putting it into its properly alphabetized position (using the non-plural form of the table name, so that for examplediscussion_topicsis placed beforediscussion_topic_replies)set_replication_identitycalls are moved into theSetReplicaIdentiesmigration, in their same alphabetized position.- Any options (such as
algorithm: :concurrently,if_not_exists: true,validate: false,validate_constraint) used to make the original migration idempotent are not necessary inInitCanvasDb, and should be removed. - Options that are already the default should not be specified:
default: nilnull: trueindex: trueont.referencescallsindex: falseon non-reference column addition calls
- Keep the statements within
create_tableblocks organized, with a blank line between each section:- Column additions (including
t.timestamps) are the first section, preserving their original order they were added - Additional constraints are the next section, preserving their original order they were added
- Additional indexes are the final section, preserving their original order they were added, with the exception that the
t.replica_identity_indexis first.
- Column additions (including
- If the migration queues a
DataFixup, find the file defining it, and any associated spec file. If the DataFixup is not referenced by any other migration, just delete the spec file, theDataFixupfile, and the migration file. create_initial_partitionscalls can be squashed into theCreateInitialPartitionsmigration.
Validation Process
Before making any modifications, reset the test database and store a baseline:
RAILS_ENV=test bin/rake db:test:reset
pg_dump -s --restrict-key=MQTD3FxKJiJ5XiNN2cfyqy9ctUI0Tt9i3SWn8wZ7l2dYLJGctear9gqS1IRbdO5 canvas_test > original.sql
After making modifications, reset the test database again, dump it, and confirm no structural changes:
RAILS_ENV=test bin/rake db:test:reset
pg_dump -s --restrict-key=MQTD3FxKJiJ5XiNN2cfyqy9ctUI0Tt9i3SWn8wZ7l2dYLJGctear9gqS1IRbdO5 canvas_test > modified.sql
diff -u original.sql modified.sql
The diff output should be empty. Exceptions are allowed if the order of columns has changed because a squashed column is now earlier in the table than a column added by a migration in gems/plugins/*/db/migrate/*.
Finishing Up
- Alter
ValidateMigrationIntegrityby replacing the timestamp inlast_squashed_migration_versionwith the value from the last deleted migration, and increment the version number in the filename. - Run
script/rlint -ato fix any formatting issues.