From 9428a669b52ab313f0ead3b20ad12f114b17d621 Mon Sep 17 00:00:00 2001 From: David Taylor Date: Thu, 1 Jul 2021 19:12:38 +0100 Subject: [PATCH] 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. --- ...24080131_add_partial_index_pinned_until.rb | 20 +++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/db/migrate/20210624080131_add_partial_index_pinned_until.rb b/db/migrate/20210624080131_add_partial_index_pinned_until.rb index c91cdb45236..14bd0d4b2e8 100644 --- a/db/migrate/20210624080131_add_partial_index_pinned_until.rb +++ b/db/migrate/20210624080131_add_partial_index_pinned_until.rb @@ -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