Skip to content

fix(orphan): bound timed-out renewal PostgreSQL lifetime - #35

Merged
bipolarrr merged 1 commit into
masterfrom
perf/prefix-orphan-claim-io-bounds
Aug 17, 2026
Merged

fix(orphan): bound timed-out renewal PostgreSQL lifetime#35
bipolarrr merged 1 commit into
masterfrom
perf/prefix-orphan-claim-io-bounds

Conversation

@bipolarrr

Copy link
Copy Markdown
Collaborator

Summary

  • follow up PR perf(orphan): bound PREFIX cleanup claim renewal I/O #33 by bounding the actual PostgreSQL lifetime of orphan claim renewal statements, not only the JavaScript await
  • run renewal in one pinned transaction with renewal-local statement_timeout=45s and rollback-before-release semantics
  • preserve the existing blocking advisory lock, state/token fencing, DB-clock expiry check, heartbeat, and destructive-bound renewal behavior
  • add CI-registered PostgreSQL regressions for actual advisory wait cleanup, late mutation, pool recovery, success/expiry, and outer abort

Closes #34.

Root cause and reproduction

PR #33's Promise.race() bounded the reaper caller at 60 seconds but did not cancel the underlying Prisma/PostgreSQL query.

Using the production repository path with PostgreSQL 16 and a separate transaction holding OBJECT_REFERENCE_CLAIM_LOCK_ID reproduced both failure modes:

  • after a 250ms JS observer deadline, pg_stat_activity still showed the renewal as active / Lock / advisory
  • releasing the blocker let that original query return { count: 1 } and increased claim_until by 60,384ms
  • three blocked renewals filled an isolated three-slot pool and prevented a follow-up query from acquiring a slot

Implementation

renewActiveClaim() now runs in a Prisma interactive transaction pinned to one connection:

transaction acquisition (maxWait 5s)
→ SET LOCAL statement_timeout = 45s
→ SET LOCAL idle_in_transaction_session_timeout = 5s
→ existing pg_advisory_xact_lock + fenced UPDATE
→ abort checkpoint
→ COMMIT or ROLLBACK
→ connection release

The original SQL still requires DELETE_CLAIMED, the current token, and claim_until > clock_timestamp(). PostgreSQL timeout rejection remains distinct from { count: 0 } ownership loss. Service heartbeat/pre-delete callers still share one physical renewal flight, and failed/aborted renewal cannot authorize LIST/DELETE work.

Resource bounds and scope

renewal statement/advisory wait server bound: 45,000ms
transaction acquisition timer:                5,000ms
Prisma interactive transaction timer:        50,000ms
aborted-idle PostgreSQL backend guard:         5,000ms
JS renewal deadline:                         60,000ms

observed post-timeout active renewals:  0
observed late lease mutations:          0
observed retained three-slot renewals:  0

The 45-second hard bound applies after the transaction-local settings reach a normally communicating PostgreSQL server. Prisma transaction timing is not active-query cancellation, and this change does not claim a hard client pool-slot bound for a network blackhole during BEGIN, timeout setup, COMMIT, or ROLLBACK transport.

PostgreSQL regression

The new test:integration:orphan-renewal-timeout suite is registered in the repository integration orchestrator and verifies:

  • a real advisory waiter exists before timeout and disappears afterward
  • blocker release cannot produce a late claim_until update
  • the same application pool is usable after rollback
  • all three waiters in a three-slot pool clean up before the acquisition deadline
  • no idle in transaction or aborted transaction remains
  • normal renewal still extends the lease
  • expired renewal returns { count: 0 } without mutation
  • direct and service outer abort paths remain fail-closed and roll the update back

Validation

  • focused orphan/PREFIX — 41 passed
  • PostgreSQL renewal lifetime — 6 passed
  • PostgreSQL orphan durability — 16 passed
  • PostgreSQL + Garage recovery — 7 passed
  • full API — 958 passed, 77 skipped
  • lint, TypeScript, build, architecture/dependency checks, audit, migration policy, and git diff --check — passed

Independent review

A fresh adversarial reviewer found and drove fixes for CI registration and overbroad resource-bound wording, then returned ACCEPT. A different fresh verifier independently reproduced the original PostgreSQL waiter/late UPDATE, reran production-path and full validation, and returned APPROVE. There are no unresolved Critical/High/Medium findings.

@bipolarrr
bipolarrr marked this pull request as ready for review August 17, 2026 12:22
@bipolarrr
bipolarrr merged commit 4a5f260 into master Aug 17, 2026
2 checks passed
@bipolarrr
bipolarrr deleted the perf/prefix-orphan-claim-io-bounds branch August 18, 2026 01:05
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

fix(orphan): bound actual PostgreSQL lifetime of timed-out claim renewals

1 participant