disallowed-unique-constraint
problem
Adding a UNIQUE
constraint requires an ACCESS EXCLUSIVE
lock which blocks reads and writes to the table while the index is built.
solution
Instead create an index CONCURRENTLY
and create the CONSTRAINT
USING
the index.
https://www.postgresql.org/docs/current/sql-altertable.html
Instead of:
-- blocks reads and writes to table_name while constraint is validated.
ALTER TABLE distributors ADD CONSTRAINT dist_id_uniq UNIQUE (dist_id);
Use:
-- allows reads and writes while index is built
CREATE UNIQUE INDEX CONCURRENTLY dist_id_uniq ON distributors (dist_id);
-- add constraint using the built index
ALTER TABLE distributors ADD CONSTRAINT distributors_dist_id_key UNIQUE USING INDEX dist_id_uniq;
Instead of:
-- blocks reads and writes to table_name while constraint is validated.
ALTER TABLE distributors
ADD COLUMN dist_id text CONSTRAINT dist_id_uniq UNIQUE (dist_id);
Use:
-- add nullable column separately to prevent blocking read/writes.
ALTER TABLE distributors ADD COLUMN dist_id text;
-- allows reads and writes while index is built
CREATE UNIQUE INDEX CONCURRENTLY dist_id_uniq ON distributors (dist_id);
-- add constraint using the built index
ALTER TABLE distributors ADD CONSTRAINT distributors_dist_id_key UNIQUE USING INDEX dist_id_uniq;
solution for alembic and sqlalchemy
# migrations/*.py
# First migration
from alembic import op
def schema_upgrades():
with op.get_context().autocommit_block():
op.create_index(
op.f("dist_id_uniq"),
"distributors",
["dist_id"],
unique=True,
postgresql_concurrently=True,
)
def schema_downgrades():
with op.get_context().autocommit_block():
op.drop_index(
op.f("dist_id_uniq"),
postgresql_concurrently=True,
)
# migrations/*.py
# Second migration
from alembic import op
import sqlalchemy as sa
def schema_upgrades():
op.execute(
sa.text(
"""
ALTER TABLE distributors ADD CONSTRAINT distributors_dist_id_key UNIQUE USING INDEX dist_id_uniq;
"""
),
)
def schema_downgrades():
op.drop_constraint(
"distributors_dist_id_key",
type_="unique",
)