Skip to content

fix(server): raise the default control-plane idle timeout to 15m - #1158

Merged
fuziontech merged 1 commit into
mainfrom
fix/raise-default-idle-timeout-15m
Sep 5, 2026
Merged

fix(server): raise the default control-plane idle timeout to 15m#1158
fuziontech merged 1 commit into
mainfrom
fix/raise-default-idle-timeout-15m

Conversation

@fuziontech

Copy link
Copy Markdown
Member

Why

An idle client connection pins a worker, so the control plane closes it after DefaultControlPlaneIdleTimeout. That was 60s (broke any client pausing between statements), then 5m. 5m is still short of the gaps clients actually leave.

Measured against a production deployment, the connections 5m was reaping were not abandoned:

reap -> client's next use:   p50 8s    p90 35s    max 58s

Every observed idle gap fell between 5m00s and 5m58s. These clients were missing the threshold by seconds, then paying a cold worker respawn to get back — churn that reclaims nothing. And only ~4% of reaps had no client return at all, so the window isn't holding many genuinely abandoned connections either.

There's a prior data point for the lever working: reaps ran ~850/day at 60s and dropped to ~170/day at 5m.

What

DefaultControlPlaneIdleTimeout 5m → 15m.

reaps avoided extra idle worker time
10m 100% of measured ~0.5 workers held
15m 100% + margin ~1.1 workers held

10m would clear the measured cluster, but the worst observed gap is already 5m58s — that leaves ~4 minutes before a slower run puts us back here. 15m buys real margin for about one continuously-held worker fleet-wide, against roughly 150 avoided cold restarts a day.

The value deliberately sits at the ceiling TestDefaultControlPlaneIdleTimeout enforces: beyond 15m an abandoned connection holds a pinned worker too long to justify as a default. Raising it further should be a deliberate decision that trips that guard.

Escape hatches are unchanged: operators wanting tighter density set --idle-timeout / DUCKGRES_IDLE_TIMEOUT; a client needing longer asks per connection via duckgres.idle_timeout, bounded by DUCKGRES_CLIENT_IDLE_TIMEOUT_MAX.

Scope

This reduces how often the reap fires — it is not a correctness fix. A client whose gap can exceed any finite timeout still has to treat the reap as retryable rather than fatal. Clients that already request a longer per-connection duckgres.idle_timeout are unaffected.

Testing

  • go build ./... and go vet ./... clean.
  • ./server/... green, including the updated TestDefaultControlPlaneIdleTimeout.
  • go test ./... has 4 failing packages (cmd/cache-proxy, tests/controlplane, tests/integration, tests/trino-ducklake-smoke) — verified identical on unmodified origin/main in this environment, so they're pre-existing/environmental (they need docker/postgres/trino), not introduced here.

An idle client connection pins a worker, so the control plane closes it after
DefaultControlPlaneIdleTimeout. That was 60s, which broke any client pausing
between statements; it became 5m. 5m is still short of the gaps clients
actually leave.

Measured against a production deployment: the connections 5m reaped were not
abandoned. The client came back a median of 8s after the reap and at worst
58s, so every observed idle gap fell between 5m00s and 5m58s. Those clients
were missing the threshold by seconds and then paying a cold worker respawn to
get back -- churn that reclaims nothing. Only ~4% of reaps had no client
return at all, so the window is not holding many genuinely abandoned
connections either.

Raise it to 15m. That clears the observed cluster with margin for a slower
run, and costs on the order of one continuously-held worker across the fleet
against roughly 150 avoided cold restarts a day. The value deliberately sits
at the ceiling the accompanying test enforces: beyond 15m an abandoned
connection holds a pinned worker too long to justify as a default. Operators
who want tighter density still set --idle-timeout / DUCKGRES_IDLE_TIMEOUT, and
a client needing longer asks per connection via duckgres.idle_timeout, bounded
by DUCKGRES_CLIENT_IDLE_TIMEOUT_MAX.

This reduces how often the reap fires; it is not a correctness fix. A client
whose gap can exceed any finite timeout still has to treat the reap as
retryable rather than fatal.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VyKpkM46efTrRcCJNp8Bmd
@fuziontech
fuziontech requested a review from a team September 4, 2026 20:16
@github-actions

github-actions Bot commented Sep 4, 2026

Copy link
Copy Markdown

Test Impact Plan

Deterministic summary of how this PR changes tests, CI runners, and coverage-risk signals.

Summary

Area Added Changed Deleted
Test files 0 1 0
E2E/journey files 0 0 0
Workflow files 0 0 0

Signals

  • Test cases: +0 / -0
  • Assertions: +1 / -1
  • Skips or known failures added: 0
  • Workflow continue-on-error added: 0
  • Workflow path filters added: 0
  • Test commands removed from justfile: 0
  • E2E/journey retry lines added: 0

Coverage risk: neutral or increased

No coverage-reduction warnings detected.

@fuziontech
fuziontech merged commit e511c69 into main Sep 5, 2026
43 of 44 checks passed
@fuziontech
fuziontech deleted the fix/raise-default-idle-timeout-15m branch September 5, 2026 14:22
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