promote(uat→main): GRO-2123 seed advisory lock + GRO-2100 uat-groomer linkage ordering #157
Reference in New Issue
Block a user
Delete Branch "uat"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Promote uat→main: GRO-2123 seed advisory lock + GRO-2100 uat-groomer linkage ordering
Carries all uat fixes since the last promotion (uat HEAD
e2eacbc; main HEADc92fb25).GRO-2123 — Reset CronJob intermittent FK 23503 (seed advisory lock)
concurrencyPolicy: Forbidonreset-demo-dataCronJob) + #617 (UAT image bump), both mergedgit.farh.net/groombook/reset:2026.06.04-e2eacbc— log markers✓ Acquired seed advisory lock+✓ Released seed advisory lock; 0 hits on23503/invoice_tip_splits_invoice_id_invoices_id_fkpackages/db/src/seed.ts— newwithSeedAdvisoryLock<T>helper reserves a postgres-js connection, takespg_advisory_lock(0x47524f4f)("GROO"), runs the seed body, and releases both the lock and the connection infinallywith alockHeldguard.UAT_PLAYBOOK.md §3.29documents the regression check.GRO-2100 — uat-groomer ↔ UAT Pup Alpha linkage ordering
Security follow-up
apps/api/src/db/seed.tsduplicate that lackswithSeedAdvisoryLock): tracked as a separate Paperclip cleanup ticket — not on the runtime path (Dockerfile runspackages/db/src/seed.ts), and the security fix is independent of removing the duplicate.cc @cpfarhood
The reset-demo-data CronJob in groombook-uat intermittently failed with FK 23503 on invoice_tip_splits because two pods could run the seed concurrently: the new pod's TRUNCATE deleted rows the old pod was still inserting. Acquire a session-level advisory lock for the full duration of the seed. CRITICAL: with postgres-js connection pooling, a pg_advisory_lock acquired on one pooled connection and released on a different one is a no-op (the lock is bound to the pg-backend that took it). We therefore reserve a dedicated connection for the lock, take pg_advisory_lock(KEY) on it, run the seed on the pooled connections, and release the lock + reserved connection in a try/finally so a thrown seed error cannot leak the lock or the connection. Defence-in-depth with the infra PR that switches concurrencyPolicy: Replace → Forbid on the reset-demo-data CronJob. - Adds withSeedAdvisoryLock helper and runSeedBody extracted function - Wraps seed() body in the helper; client.end() runs after the lock releases so a reserved connection is not returned to a closed pool - SEED_ADVISORY_LOCK_KEY = 0x47524f4f ("GROO" in ASCII) — arbitrary stable 32-bit key, referenced in runbooks - UAT_PLAYBOOK.md §3.29 documents the regression check cc @cpfarhood Co-Authored-By: Paperclip <noreply@paperclip.ing>Phase 4 code review — APPROVED ✅
Reviewed
promote(uat→main)#157 (uat@e2eacbc→main@c92fb25). Diff is exactly the two GRO-* fixes acrosspackages/db/src/seed.ts(+216/-5) andUAT_PLAYBOOK.md(+3) — no surprise refactors.Correctness / architecture
withSeedAdvisoryLock<T>: reserves a dedicatedpool.reserve()connection, takespg_advisory_lock(0x47524f4f), runs the body, and unlocks +release()s infinallyguarded bylockHeld. Lock and connection share the same session, so no orphaned-lock / cross-connection no-op risk. No nested re-acquire on the seed path → no self-deadlock. Sound under exception paths.runSeedBodyextraction withclient.end()moved to the caller is clean single-ownership of the connection lifecycle.a0000001-…-0001upsert key), correctly ordered AFTER services seed in bothseedKnownUsers()and the fullseed()branch, and defensively guarded (null client / missing groomer / missing pet / missing service all skip, not crash).Defense-in-depth: app-level lock complements infra #616 (
concurrencyPolicy: Forbid) — covers manualkubectl create job --from=cronjobinvocations that bypass the CronJob concurrency policy.Gates
reset:2026.06.04-e2eacbcBuild & Push Docker Images (pull_request)red was a transient CoreDNS flake in the post-build smoke test (lookup git.farh.net on 10.43.0.10:53: server misbehaving) — all four images built+pushed fine; I reran the job and it is now green.apps/api/src/db/seed.tsduplicate) correctly isolated to GRO-2129, off the runtime path — good call keeping it out of this promotion.Approving. Per the SDLC promotion model, @Flea Flicker self-merges (CTO never merges app PRs).