fix(credits): add attempt_index before indexing it on postgres boot - #433
Conversation
CREATE TABLE IF NOT EXISTS no-ops on the deployed reservation table, so the Open-Finance-Lab#432 run-status index raised UndefinedColumn and killed Render import. Co-authored-by: Cursor <cursoragent@cursor.com>
|
@Allan-Feng is attempting to deploy a commit to the allan-feng's projects Team on Vercel. A member of the Team first needs to authorize it. |
…ndex-boot # Conflicts: # dashboard/backend/domain/credits/repository_postgres.py # dashboard/backend/tests/domain/credits/test_repository_postgres.py
Review follow-ups for Open-Finance-Lab#433 after Open-Finance-Lab#434 landed the same ordering fix on main. - Merge main and keep this PR's DROP of prod's pre-Open-Finance-Lab#432 three-column idx_credit_llm_reservations_run_status. Open-Finance-Lab#434's bare CREATE INDEX IF NOT EXISTS matches by name alone, so it no-ops against that index and the four-column definition never lands; this PR is the only repair. - Make the repair converge: drop only when pg_get_indexdef reports a column list other than (run_id, status, call_index, attempt_index). credits_store is built at import, so an unconditional DROP+CREATE rebuilt the index under ACCESS EXCLUSIVE on every deploy. The index now sits below the last ALTER on the table, and the base DDL carries no comment naming it (main's guard asserts the name is absent there). - Generalise the guard: test_store_twin_parity now fails any Postgres twin that creates an index above the ADD COLUMN of a column it names, and _string_literals returns literals in source order so position reads as execution order. database_postgres created two agent_runs indexes on session_id above its ADD COLUMN; moved below the migrations. - Tests: the live tier reads pg_get_indexdef / pg_get_constraintdef on the search path instead of an unscoped relname join, a second boot must keep the same index OID, and the three credits fixtures share one _isolated_schema / _create_users helper. - Add the ADDING A COLUMN LATER? note the sibling twins carry, and amend the Open-Finance-Lab#434 design spec for the stale-index case. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VkzNY81VSr23dtSH5k8hHj
|
Pushed review follow-ups onto this branch as a merge from
Full backend suite green locally with |
Review follow-ups on this branch. - The staleness DO block's DROP INDEX gains IF EXISTS. Its predicate is evaluated before the lock is taken, so two concurrent boots could both reach it and the loser raised "index does not exist" -- aborting _init_schema, which kills the app, since credits_store is built at import. - ADD CONSTRAINT ... logical_attempt_key now sits behind a conkey guard, so it stops rebuilding a full unique index under ACCESS EXCLUSIVE on every boot. Column identity comes from pg_constraint.conkey rather than a pg_get_constraintdef text match (the idiom already used by the legacy sweep above it); a mismatch falls back to the previous drop+add. The 12 CHECK/FK rebuilds nearby are deliberately left unconditional -- recognising a CHECK means comparing deparsed text, which drifts -- and the DO block's comment now says so instead of implying the whole migration converges. - The source guard no longer bans "DROP INDEX IF EXISTS": conditionality is the drop's position inside the guard, not its spelling, and the old assertion made the concurrency fix un-landable. Boot-idempotence is now pinned live by OID (con.oid + con.conindid), which is the only witness that survives a same-named drop+re-add. - test_store_twin_parity: identifier patterns accept schema-qualified and quoted names, SQL `--` comments are blanked before parsing, and a new per-twin coverage test asserts every CREATE INDEX keyword actually parses -- the guard could previously read zero indexes and pass. Docstrings that claimed source position *is* execution order now name users_postgres.py, where a hoisted DDL constant already executes after later inline ALTERs. All four new guards mutation-tested. Full backend suite: 4111 passed, 46 skipped, with the @pg_only tier live against postgres:18. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01AVXYsLrNzE1biGsEfj94yL
The 2026-09-02 amendment claimed convergence as the migration's property; it is the property of the index repair and now of the logical-attempt UNIQUE. Records why the other twelve constraint rebuilds are left unconditional, and why the IF EXISTS on the drop is required despite the surrounding guard. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01AVXYsLrNzE1biGsEfj94yL
|
Second review pass on DDL
Tests
All four new guards mutation-tested. Full backend suite 4111 passed / 46 skipped with the 🤖 Generated with Claude Code |
Summary
CREATE TABLE IF NOT EXISTSno-ops on the existing Neoncredit_llm_reservationstable, thenCREATE INDEX ... (..., attempt_index)raisesUndefinedColumnbefore the laterADD COLUMNcan run.idx_credit_llm_reservations_run_statusinto the grant-migration DDL, afterADD COLUMN attempt_index. Drop and recreate the pre-feat: fail over platform credits to CommonStack #432 three-column index so the name matches the new definition.Test plan
pytest dashboard/backend/tests/domain/credits/test_repository_postgres.py::test_postgres_boot_ddl_indexes_attempt_index_only_after_add_columnpytest dashboard/backend/tests/test_store_twin_parity.py::test_postgres_twin_schema_columns_match_sqliteTEST_POSTGRES_URLset:pytest dashboard/backend/tests/domain/credits/test_repository_postgres.py::test_postgres_boot_migrates_pre_failover_reservation_table -vcredits_store backend: postgres (...)withoutUndefinedColumn: attempt_indexMade with Cursor