require-concurrent-index-creation

problem#

During a normal index creation, table updates are blocked, but reads are still allowed. CONCURRENTLY avoids locking the table against writes during index creation.

https://www.postgresql.org/docs/current/sql-createindex.html#SQL-CREATEINDEX-CONCURRENTLY

solution#

Ensure all index creations use the CONCURRENTLY option.

This rule ignores indexes added to tables created in the same transaction.

create index#

Instead of:

-- blocks writes to table while index is built
CREATE INDEX "email_idx" ON "app_user" ("email");

Use:

-- allows reads and writes while index is built
CREATE INDEX CONCURRENTLY "email_idx" ON "app_user" ("email");

solution for alembic and sqlalchemy#

Instead of:

# migrations/*.py
from alembic import op
def schema_upgrades():
op.create_index(
"email_idx",
"app_user",
["email"],
schema="app",
unique=False,
)
def schema_downgrades():
op.drop_index(
"email_idx",
schema="app",
)

Use:

# migrations/*.py
from alembic import op
def schema_upgrades():
with op.get_context().autocommit_block():
op.create_index(
op.f("email_idx"),
"app_user",
["email"],
unique=False,
postgresql_concurrently=True,
)
def schema_downgrades():
with op.get_context().autocommit_block():
op.drop_index(
"email_idx",
table_name="app_user",
postgresql_concurrently=True,
)

Notes:

In a concurrent index build, the index is actually entered as an “invalid” (details)