Repository navigation
fix(migrations): build a new schema's indexes plainly so provisioning never waits on other transactions - #5700
Conversation
|
The rewrite currently treats the target-schema substring anywhere in the statement as proof that the index DDL belongs only to that schema. That can remove DROP INDEX CONCURRENTLY "tenant_abc".idx_a, "tenant_live".idx_b
CREATE INDEX CONCURRENTLY idx ON "tenant_live".t (a) /* "tenant_abc". */The first is valid PostgreSQL syntax and downgrades the drop for both indexes; the second shows the same substring boundary without touching the target schema at all. I reproduced both directly against Could the predicate identify the actual |
6d5ab60 to
c515df7
Compare
… never waits on other transactions The migrations create and drop indexes CONCURRENTLY so a deploy that adds one never blocks writes on a populated table. CONCURRENTLY waits for every transaction open anywhere in the database when it starts, not just on its own table. On a schema being created from nothing that wait protects nothing (its tables are empty), but a single long transaction in any other schema stalls each of its dozens of concurrent index statements, so provisioning one new schema takes minutes instead of seconds and the first request waiting on it times out. When a migration run starts on a schema with no alembic_version yet, rewrite CREATE/DROP INDEX CONCURRENTLY to the plain form for statements that name that schema. A plain build on an empty table is instant and locks only that table. Existing schemas, the default schema, unqualified statements and anything naming another schema keep CONCURRENTLY. Tests: - the rewrite applies only to index DDL that names the new schema; - with a REPEATABLE READ transaction held open on another connection, a new schema migrates to head (before this change it waited for that transaction and never finished while it stayed open; verified failing without the fix); - a schema migrated this way ends with exactly the index definitions of one migrated with CONCURRENTLY, none INVALID. Claude-Session: https://claude.ai/code/session_01MbkqGjkRXNBBpZUP8f1UmF
Review fix: the event listener's parameters and return were untyped.
c515df7 to
bf0c485
Compare
Problem
The migrations create and drop their indexes
CONCURRENTLY(37CREATE INDEX CONCURRENTLYand 25DROP INDEX CONCURRENTLY), so a deploy that adds one never blocks writes on a populated table. The cost is thatCONCURRENTLYwaits, several times per statement, for every transaction open anywhere in the database when it starts, not just on its own table.When a schema is being created from nothing, that wait protects nothing, because its tables are empty and nothing else uses them. But in a multi-schema database, a single long transaction in any other schema stalls each of the new schema's concurrent index statements for up to that transaction's duration. Examples are a large bulk
UPDATE, a long report, or a slow background task.With a workload that keeps such transactions running, provisioning a new schema goes from about a second to several minutes, and the first request waiting on it times out.
Fix
When a migration run starts on a schema that has no
alembic_versionyet, abefore_cursor_executehook rewritesCREATE [UNIQUE] INDEX CONCURRENTLYandDROP INDEX CONCURRENTLYto the plain form. It does this only for statements that name that schema ("<schema>".). A plain build on an empty table is instant and locks only that table.These still use
CONCURRENTLY:So an index on a shared or populated table is never built under a blocking lock by accident. The hook lives in
alembic/env.py, so none of the migration files change and future migrations are covered automatically.Tests
tests/test_migration_new_schema_indexes.py:CONCURRENTLY. Other schemas,public, unqualified statements, non-DDL andREINDEXare left alone.REPEATABLE READtransaction held open on another connection, a new schema migrates to head in about 1 second. Without the fix this test fails: the migration waits for that transaction and doesn't finish while it stays open. I checked this by running it with the hook removed.CONCURRENTLY, and no index is leftINVALID.The existing migration and schema-provisioning suites pass (
test_migration_*,test_alembic_dag,test_extension_alembic_tree,test_admin_rename_bank,test_claim_bank_fairness,test_admin_backup_restore): 305 passed, 127 skipped.ruff checkandruff formatare clean, andtyreports no new diagnostics.https://claude.ai/code/session_01MbkqGjkRXNBBpZUP8f1UmF