Skip to content

Keep the reflector's keep-alive connections open between the broker's requests - #168

Open
shawnburke wants to merge 1 commit into
mainfrom
shawn/reflector-idle-timeout
Open

shawnburke wants to merge 1 commit into
mainfrom
shawn/reflector-idle-timeout

Conversation

@shawnburke

Copy link
Copy Markdown
Collaborator

Why

The agent's HTTP server (the reflector the broker talks to on 127.0.0.1) set ReadTimeout: 1s and no IdleTimeout. net/http uses ReadTimeout as the keep-alive idle limit when IdleTimeout is zero, so the server closed any keep-alive connection that sat idle for one second.

The broker keeps its local connections open (Node 20's default agent), and the gap between its requests is often longer than a second. It regularly wrote the next request into a socket the server had just closed and got ECONNRESET / "socket hang up", then retried after 500ms.

From one Fiserv relay (agent 0.3.4, 18:00 to 18:30 UTC on 2026-10-14):

count
"Request failed. Retrying after 500ms" against 127.0.0.1 4,774
requests that exhausted all 3 retries 7
of those, dispatcher /__axon/info liveness probes 3

A failed liveness probe is what the dispatcher reports as "agent unresponsive", so this churn feeds false negatives on the Cortex side as well as latency on relayed calls.

What

  • Set IdleTimeout explicitly to five minutes on the server. ReadTimeout is unchanged.
  • Add TestServer_KeepsKeepAliveConnectionIdlePastReadTimeout: send a request on a raw connection, wait longer than ReadTimeout, send another on the same connection, and expect a response. It fails on main with unexpected EOF.

Verified

  • New test fails before the change and passes after.
  • go vet ./server/http/ clean; go test ./... in agent/ green (14 packages).

Context

Found while investigating CD-662 (Fiserv GitLab relay timeouts). This is a side issue there, not the cause of the outage, but it is independent and worth fixing on its own.

🤖 Generated with Claude Code

… requests

The HTTP server set ReadTimeout without IdleTimeout, and net/http falls back
to ReadTimeout as the keep-alive idle limit. One second is shorter than the
gaps between consecutive requests from the broker, which keeps its
connections open by default (Node 20's agent), so it regularly wrote the next
request into a socket this server had just closed. Each one surfaced as
"socket hang up" ECONNRESET and a 500ms retry; a 30-minute window from one
relay showed 4,774 such retries, and the 7 that exhausted their retries
included 3 of the dispatcher's own /__axon/info liveness probes.

Set IdleTimeout explicitly to five minutes so an idle connection outlives any
realistic gap, and cover it with a test that reuses a connection after
sitting idle past ReadTimeout.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@shawnburke
shawnburke marked this pull request as ready for review October 15, 2026 00:03
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.

1 participant