feat: detect and repair index definition drift during migrations - #134
Open
jerenkrantz wants to merge 2 commits into
Open
feat: detect and repair index definition drift during migrations#134jerenkrantz wants to merge 2 commits into
jerenkrantz wants to merge 2 commits into
Conversation
Previously, Migrations() only checked whether an index existed by name. If an index existed but its definition had changed (different columns, method, predicate, etc.), the drift was silently ignored. Now readIndexes() fetches the indexdef from pg_indexes and compares it against the expected definition. When drift is detected, a DROP INDEX followed by CREATE INDEX is emitted to bring the index back in sync. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Instead of DROP then CREATE (which leaves a window with no index), use a three-step approach: 1. CREATE INDEX with a temporary name (_new suffix) 2. DROP INDEX the old one 3. ALTER INDEX RENAME the temp to the original name This ensures there is always at least one usable index available during the transition. The CONCURRENTLY keyword prevents blocking concurrent queries during both the create and drop phases. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Migrations()now detects when an existing index's definition has drifted from the expected state and automatically repairs itpg_indexes.indexdefas the source of truth, handling all index types (BTREE, GIN, BTREE_GIN, HNSW), UNIQUE indexes, WHERE predicates, override expressions, and partitioned tables (ON ONLY)Migration strategy
Uses a create-then-drop approach to avoid any window without an index:
CREATE INDEX CONCURRENTLY IF NOT EXISTS "<name>_new" ...— builds the replacement index (non-blocking)DROP INDEX CONCURRENTLY IF EXISTS "<name>"— removes the stale index (non-blocking)ALTER INDEX "<name>_new" RENAME TO "<name>"— instant catalog-only renameCONCURRENTLYis used for non-partitioned tables (matching existing behavior viaindex2sql). This ensures queries always have at least one usable index during the transition.Drift detection only triggers when
index2expectedDef()produces a definition that differs from whatpg_indexes.indexdefactually stores — unchanged indexes are skipped with zero overhead.Test plan
pgQuoteIdent,pgNormalizeExpr,index2expectedDef,indexRename2sqlcovering all index types, partitioned tables, uppercase names, dropped/primary indexesTestMigrationIndexMutation— creates schema, introduces drift, verifies the 3-step CREATE/DROP/RENAME migration, and confirms convergence🤖 Generated with Claude Code