fix(db): serialise schema migrations with an advisory lock + stagger drain crons #240

Merged
chaim merged 1 commits from worktree-drain-deadlock into main 2026-06-13 08:08:56 +00:00
Owner

הבעיה

legal-halacha-drain קרס 29 פעמים עם asyncpg.exceptions.DeadlockDetectedError.

שורש: כל drain קצר-חיים (cron) מריץ מחדש בעלייתו את כל ה-schema migrations האידמפוטנטיות (get_pool_run_schema_migrations). שלושה jobs — metadata-drain, halacha-drain, halacha-supervisor — נדלקו באותה דקה (*/15 / תחילת-שעה). שני תהליכים שהריצו את ה-DDL במקביל לקחו AccessExclusiveLock בסדר הפוך → Postgres הרג אחד מהם ב-deadlock.

התיקון — שתי שכבות

  1. שורש (#2): עטיפת _run_schema_migrations ב-pg_advisory_lock ברמת-session — רק תהליך אחד מריץ DDL בכל רגע; מקבילים ממתינים במקום deadlock. גוף ה-DDL חולץ ל-_apply_schema_ddl. אידמפוטנטי, הסכמה לא משתנה.
  2. הגנה-בעומק (#1): דקת-הצתה ייחודית לכל drain — metadata :00, supervisor :05, halacha-drain :10, digest :12, court-fetch :17 — כך שאחים לא עולים יחד. SCRIPTS.md עודכן בהתאם.

אימות

  • pg_advisory_lock נבדק חי מול ה-DB (localhost:5433): migrations רצו, הנעילה שוחררה (0 locks תלויים).
  • דקות-ההצתה החדשות אומתו חיות ב-PM2 (pm2 save בוצע על ה-host).
  • db.py עובר ast.parse; שלושת ה-.cjs עוברים node -c.

Invariants

  • G1 — תיקון במקור (מסלול-המיגרציה היחיד), לא בתסמין.
  • G2 — לא נוצר מסלול-בקרה מקביל.

🤖 Generated with Claude Code

## הבעיה `legal-halacha-drain` קרס 29 פעמים עם `asyncpg.exceptions.DeadlockDetectedError`. **שורש:** כל drain קצר-חיים (cron) מריץ מחדש בעלייתו את כל ה-schema migrations האידמפוטנטיות (`get_pool` → `_run_schema_migrations`). שלושה jobs — `metadata-drain`, `halacha-drain`, `halacha-supervisor` — נדלקו **באותה דקה** (`*/15` / תחילת-שעה). שני תהליכים שהריצו את ה-DDL במקביל לקחו `AccessExclusiveLock` בסדר הפוך → Postgres הרג אחד מהם ב-deadlock. ## התיקון — שתי שכבות 1. **שורש (#2):** עטיפת `_run_schema_migrations` ב-`pg_advisory_lock` ברמת-session — רק תהליך אחד מריץ DDL בכל רגע; מקבילים **ממתינים** במקום deadlock. גוף ה-DDL חולץ ל-`_apply_schema_ddl`. אידמפוטנטי, הסכמה לא משתנה. 2. **הגנה-בעומק (#1):** דקת-הצתה ייחודית לכל drain — metadata `:00`, supervisor `:05`, halacha-drain `:10`, digest `:12`, court-fetch `:17` — כך שאחים לא עולים יחד. `SCRIPTS.md` עודכן בהתאם. ## אימות - `pg_advisory_lock` נבדק חי מול ה-DB (`localhost:5433`): migrations רצו, הנעילה שוחררה (0 locks תלויים). - דקות-ההצתה החדשות אומתו חיות ב-PM2 (`pm2 save` בוצע על ה-host). - `db.py` עובר `ast.parse`; שלושת ה-`.cjs` עוברים `node -c`. ## Invariants - **G1** — תיקון במקור (מסלול-המיגרציה היחיד), לא בתסמין. - **G2** — לא נוצר מסלול-בקרה מקביל. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
chaim added 1 commit 2026-06-13 08:08:47 +00:00
fix(db): serialise schema migrations with an advisory lock + stagger drain crons
All checks were successful
G12 Leak-Guard / leak-guard (pull_request) Successful in 6s
49acde591e
legal-halacha-drain crashed 29× with asyncpg DeadlockDetectedError. Root cause:
every short-lived cron drain re-runs the idempotent schema migrations on startup
(get_pool → _run_schema_migrations), and three jobs (metadata-drain, halacha-drain,
halacha-supervisor) all fired on the same minute (*/15 / top-of-hour). Two
processes running the DDL concurrently took AccessExclusiveLock in opposite order
→ Postgres killed one with a deadlock.

Two-layer fix:
- Root cause: wrap _run_schema_migrations in a session-level pg_advisory_lock so
  only one process applies DDL at a time; concurrent migrators wait instead of
  deadlocking. DDL body extracted to _apply_schema_ddl. Idempotent, schema
  unchanged.
- Defence-in-depth: give each cron drain a distinct firing minute —
  metadata :00, supervisor :05, halacha-drain :10, digest :12, court-fetch :17 —
  so siblings no longer start at the same instant. SCRIPTS.md updated to match.

Invariants: G1 (fix at source — the single migration path — not the symptom);
G2 (no parallel control path introduced).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
chaim merged commit 72f81734f1 into main 2026-06-13 08:08:56 +00:00
chaim deleted branch worktree-drain-deadlock 2026-06-13 08:08:56 +00:00
Sign in to join this conversation.
No Reviewers
No Label
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: ezer-mishpati/legal-ai#240