Skip to content

OUT-3631 | Error: Failed query: select "settings"."id", "settings"."workspace_id", "settings"."segment_id", "settings"."subheading", "settings"."content", "settings"."background_color", "settings"."banner_image_id", "settings"."banner_position_x", "settings"."banner_posi... - #197

Open
arpandhakal wants to merge 1 commit into
mainfrom
OUT-3631

Conversation

@arpandhakal

Copy link
Copy Markdown
Collaborator

Changes

  • Configure the postgres client in src/db/db.ts with idle_timeout: 20, max_lifetime: 30m, and connect_timeout: 10 so cached TCP sockets to the Supabase transaction pooler (port 6543) are recycled client-side before the pooler evicts them.
  • Remove debug: true so the driver stops logging every query in production.

Context

Sentry issue OUT-3631 surfaced a Failed query wrapping an inner write CONNECTION_CLOSED aws-1-us-east-1.pooler.supabase.com:6543 on GET / during settingsService.getForWorkspace().

The query itself is fine — the real failure is that the pooler had already closed the TCP socket that postgres had cached on a warm Vercel runtime. The next write onto that stale socket blew up before bytes left the process. On Supavisor/PgBouncer + Vercel this is a well-known pattern: the client never proactively recycles idle sockets, so the first request after an idle period on a reused lambda racing the pooler's own eviction timeout gets a CONNECTION_CLOSED. That matches the observed frequency (1 occurrence in the last 90 days).

The fix sets client-side lifetimes shorter than the pooler's, so the client always wins the race and reconnects cleanly.

Testing Criteria

  • Verify app still boots and the home page loads (exercises the same settings-actions query path that crashed).
  • Confirm Vercel production logs no longer include the verbose postgres debug query spam after deploy.
  • Monitor Sentry for recurrence of CLIENT-HOME-V3-W / CONNECTION_CLOSED errors over the next 90 days.

Notes

  • No schema, API contract, or product behavior changes. Driver-level config only.
  • idle_timeout and max_lifetime are standard postgres.js options recommended for Supavisor/PgBouncer setups.

Impact & Surface Area of Change

  • Touches every Drizzle query path in the app, since they all flow through the singleton at src/db/db.ts. Risk is minimal because the only behavioral changes are (a) idle sockets close sooner and (b) queries are no longer logged in production. Query semantics are unchanged.

🤖 Generated with Claude Code

Sets idle_timeout, max_lifetime, and connect_timeout on the postgres
client so cached sockets to the Supabase transaction pooler (port 6543)
are closed client-side before the pooler evicts them, preventing the
transient CONNECTION_CLOSED write errors on the first query after an
idle period on warm Vercel runtimes. Also drops debug: true so we stop
logging every query in production.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@vercel

vercel Bot commented Apr 23, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
client-home-v3 Ready Ready Preview, Comment Apr 23, 2026 1:09pm

Request Review

@greptile-apps

greptile-apps Bot commented Apr 23, 2026

Copy link
Copy Markdown

Greptile Summary

This PR fixes a CONNECTION_CLOSED error from the Supabase transaction pooler by adding client-side socket lifecycle settings (idle_timeout: 20s, max_lifetime: 1800s, connect_timeout: 10s) to the postgres.js singleton so cached sockets are recycled before the pooler evicts them. It also removes debug: true to stop verbose query logging in production.

Confidence Score: 5/5

Safe to merge — driver-level config only, no query semantics or schema changes.

The change is minimal and well-targeted: three standard postgres.js timeout options plus removal of a debug flag. Values are correct (max_lifetime: 60*30 = 1800 s = 30 min matches the PR description). No P0 or P1 issues found.

No files require special attention.

Important Files Changed

Filename Overview
src/db/db.ts Adds idle_timeout (20s), max_lifetime (1800s), and connect_timeout (10s) to the postgres.js client, and removes debug: true; all changes are correct and well-scoped to address stale-socket errors from the Supabase transaction pooler.

Sequence Diagram

sequenceDiagram
    participant Vercel as Vercel Lambda (warm)
    participant PGClient as postgres.js client (singleton)
    participant Pooler as Supabase Pooler port 6543

    Note over PGClient: idle_timeout=20s, max_lifetime=1800s

    Vercel->>PGClient: query (first request after idle)

    alt Before fix — stale socket
        PGClient->>Pooler: write on cached TCP socket
        Pooler-->>PGClient: CONNECTION_CLOSED (pooler evicted)
        PGClient-->>Vercel: Failed query error (Sentry OUT-3631)
    else After fix — client recycles first
        Note over PGClient: idle_timeout fires after 20s idle, closes socket proactively
        PGClient->>Pooler: new TCP connect (connect_timeout=10s)
        Pooler-->>PGClient: connection established
        PGClient->>Pooler: query
        Pooler-->>PGClient: result
        PGClient-->>Vercel: success
    end
Loading

Reviews (1): Last reviewed commit: "fix(OUT-3631): recycle idle postgres soc..." | Re-trigger Greptile

@priosshrsth priosshrsth left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@arpandhakal I have approved this one if we want to merge it.

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.

2 participants