ban-concurrent-index-creation-in-transaction
problem
While regular index creation can happen inside a transaction, this is not allowed when the CONCURRENTLY
option is used.
https://www.postgresql.org/docs/current/sql-createindex.html#SQL-CREATEINDEX-CONCURRENTLY
solution
Remove surrounding transaction markers if any.
For migrations that are implicitly wrapped in a transaction, ensure that the CREATE INDEX
command is the only command in the migration to allow migration tool to detect that no transaction is needed.
Instead of:
BEGIN;
-- <any other commands being run transactionally>
CREATE INDEX CONCURRENTLY "email_idx" ON "app_user" ("email");
COMMIT;
Use:
BEGIN;
-- <any other commands being run transactionally>
COMMIT;
CREATE INDEX CONCURRENTLY "email_idx" ON "app_user" ("email");
If you use a migration tool, it may be configured to automatically wrap commands in transactions; if that's the case, check if it supports running commands in a non-transactional context.
For example, with alembic
:
# migrations/*.py
from alembic import op
def schema_upgrades():
# <any other commands being run transactionally>
# alembic allows non-transactional operations using autocommit
with op.get_context().autocommit_block():
op.create_index(
"email_idx",
"app_user",
["email"],
schema="app",
unique=False,
postgresql_concurrently=True,
)
# <any other commands being run transactionally>
def schema_downgrades():
# <any other downgrade commands>
op.drop_index(
"email_idx",
schema="app",
)
# <any other downgrade commands>
Or alternatively:
# migrations/*.py
from alembic import op
def schema_upgrades():
# <any other commands being run transactionally>
# you can also execute BEGIN/COMMIT to delineate transactions
op.execute("COMMIT;")
op.execute("SET statement_timeout = 0;")
op.create_index(
"email_idx",
"app_user",
["email"],
schema="app",
unique=False,
postgresql_concurrently=True,
)
op.execute("BEGIN;")
# <any other commands being run transactionally>
def schema_downgrades():
# <any other downgrade commands>
op.drop_index(
"email_idx",
schema="app",
)
# <any other downgrade commands>
golang-migrate
wraps migrations in transactions but is clever enough to perform the migration if the migration file does not contain further commands next to the CREATE INDEX
command.
links
https://www.postgresql.org/docs/current/sql-createindex.html#SQL-CREATEINDEX-CONCURRENTLY