fix(state): retry state-store queries past a Prisma Postgres cold-start (FT-5226)#82
Merged
Merged
Conversation
commit: |
wmadden
approved these changes
Jul 15, 2026
…rt (FT-5226) A freshly provisioned or idle-resumed Prisma Postgres database refuses connections while its upstream warms up (FT-5226) — the edge proxy answers "Failed to connect to upstream database" until the real Postgres is reachable. The state layer already rides this out on the bootstrap migration (layer.ts retries it for 2 minutes), but the per-op state queries that run afterwards did not: a bare Effect.tryPromise in service.ts (every CRUD op) and in lock.ts (checkLive). So when the state DB had gone idle, the first query during a plan/destroy failed the whole deploy — observed in CI as a StateStoreError at plan.make during destroy. Extend the same cold-start budget to those queries: retryColdStart wraps the CRUD attempt and checkLive, retrying every 5s for up to 2 minutes. The retry is deliberately scoped to connection ESTABLISHMENT failures — ECONNREFUSED/ENOTFOUND/EAI_AGAIN and the "upstream database" message. Mid-session drops (a terminated/reset connection, e.g. postgres.js's CONNECTION_ENDED after the pool ends) are NOT retried: for the state store those are the lost-lease signal checkLive must surface loudly, not paper over. This makes the set narrower than @internal/prisma-cloud's pg-connection.ts, which can't be imported here anyway (it depends on @internal/lowering — importing back would cycle). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Signed-off-by: willbot <w.a.madden+machine@gmail.com> Signed-off-by: Will Madden <madden@prisma.io>
a7539f5 to
787a703
Compare
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.
A freshly provisioned or idle-resumed Prisma Postgres database refuses connections while its upstream warms up (FT-5226) — the edge proxy answers
Failed to connect to upstream databaseuntil the real Postgres is reachable. This is the same behavior thecold-connect canarytracks;pg-connection.tsdocumentsupstream databaseas its signature.The gap
The state layer already rides this out on the bootstrap migration — layer.ts retries the first connect for 2 minutes, with a comment naming exactly this case. But the per-op state queries that run afterwards did not: a bare
Effect.tryPromisein service.ts (every CRUD op) and in lock.ts (checkLive).So when the state DB had gone idle, the first query during a plan/destroy hit the cold upstream and failed the whole deploy. Surfaced by a
Deploy, verify, destroyCI run:StateStoreError: Failed to connect to upstream databaseatplan.makeduring destroy, ~13s in — far short of the 2-min migration retry, because it came from the unretried query path, not bootstrap.The fix
retryColdStart(new transient.ts) wraps the CRUDattemptandcheckLive, retrying every 5s for up to 2 minutes — the same budget bootstrap already uses.The retry is deliberately scoped to connection establishment failures (
ECONNREFUSED/ENOTFOUND/EAI_AGAINand theupstream databasemessage). Mid-session drops — a terminated/reset connection, e.g. postgres.js'sCONNECTION_ENDEDafter a pool ends — are not retried: for the state store those are the lost-lease signalcheckLivemust surface loudly, not paper over. Verified empirically that a refused connect (ECONNREFUSED) and a query-after-.end()(CONNECTION_ENDED) have distinct signatures, and thatlock.ts's lease-loss test (which ends the pool) still fails fast rather than retrying.This makes the set narrower than
@internal/prisma-cloud'spg-connection.ts(which also retries mid-session drops for the runtime store client). That helper can't be imported here regardless:@internal/prisma-clouddepends on@internal/lowering, so importing it back would cycle — hence the small, purpose-scoped local predicate.Tests / verification
transient.test.ts: predicate classification (cold-start vs lost-lease vs dead-pool vs real query error, incl. thecauseunwrap) and retry behavior (retries a cold start to success; never retries a lost lease) with an injected instant schedule.lock.test.tslease-loss/FT-5219 tests finish in <1s, confirming the dead-pool path is not retried.@internal/lowering97 pass; full workspace build (24/24) and typecheck (50/50) green.Follow-up to #77 (the storage rename). Independent change.
🤖 Generated with Claude Code