FIX: Make non-transactional migration idempotent (#13608)

Since disable_ddl_transaction! is disabled for this migration, it needs to be idempotent. Any error during the migration (e.g. a timeout) will cause ActiveRecord to fail the migration, and try again on the next run. If the index had already been created during the first run, then an 'already exists' error will be raised, with no way to recover.

Unfortunately an [ActiveRecord bug](https://github.com/rails/rails/pull/41490) prevents us from using `if_not_exists: true` alongside `algorithm: :concurrently`, so we have to drop to raw SQL.
This commit is contained in:
David Taylor 2021-07-01 19:12:38 +01:00 committed by GitHub
parent 4a2b173188
commit 9428a669b5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
1 changed files with 16 additions and 4 deletions

View File

@ -3,9 +3,21 @@
class AddPartialIndexPinnedUntil < ActiveRecord::Migration[6.1]
disable_ddl_transaction!
def change
add_index :topics, :pinned_until,
where: 'pinned_until IS NOT NULL',
algorithm: :concurrently
# Dropping to raw SQL here due to an ActiveRecord bug which prevents
# using `algorithm: :concurrently` and `if_not_exists: true`
# https://github.com/rails/rails/pull/41490
def up
execute <<~SQL
CREATE INDEX CONCURRENTLY IF NOT EXISTS "index_topics_on_pinned_until"
ON "topics" ("pinned_until")
WHERE pinned_until IS NOT NULL
SQL
end
def down
execute <<~SQL
DROP INDEX CONCURRENTLY IF EXISTS "index_topics_on_pinned_until"
SQL
end
end