Server WebSocket read limit (32 KiB default) drops snapshots.report on hosts with many snapshots #44
Reference in New Issue
Block a user
Delete Branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Summary
The agent WebSocket handler accepts connections without calling
SetReadLimit, so it inheritscoder/websocket's 32768-byte default. Asnapshots.reportfor a host with enough snapshots exceeds that, and the server closes the connection mid-message withStatusMessageTooBig. The projection is never updated and the job never reaches a terminal state.This is a concrete instance of the best-effort delivery problem described in #40 — here the connection is lost because of the report itself, so it is reproducible rather than incidental.
Only observable after #37 is fixed. On v1.1.0 the agent panicked before it ever attempted the report, so the limit was never reached. Upgrading one agent to v1.1.1 surfaced this immediately.
Observed (agent v1.1.1, server v1.1.1)
Manual
forget --dry-runon a host with 23 snapshots in the projection:The agent then reconnects normally. From the host's perspective everything succeeded —
forget job complete, no panic, service healthy. Only the server-side view is wrong.Resulting state
status: runningwith 0 log lines (stillrunningseveral minutes later).refreshed_atunchanged — still the previous backup's timestamp.failedalerts today (see #41).So the operator sees a healthy agent, a job that never ends, and a stale snapshot count, with nothing indicating a problem.
Cause
internal/server/ws/handler.go:coder/websocketdefaults to a 32768-byte read limit, which matches the reportedread limited at 32769 bytesexactly.Sizing
Measured from one host's own projection: 23 snapshots ≈ 6999 bytes of JSON, or roughly 304 bytes per snapshot. That puts the ceiling near ~107 snapshots for a single-path host.
Treat that as indicative rather than exact — it is derived from the server's projection shape, and the wire payload may carry fields the projection does not. Hosts with multiple source groups, long path lists, or many tags will hit it sooner.
This is well within normal operating range: a host on
keep_daily 30plus weeklies and monthlies would exceed it in routine use, and any repository that has not been thinned for a while will be over it.Suggested fix
snapshots.report(a few MiB) rather than relying on the library default. Worth setting it explicitly on the agent side too, so the limit is a deliberate protocol decision rather than a library default on both ends.snapshots.report, or send a digest plus deltas. A single host with a large repository should not be able to outgrow the transport.snapshots.report ... failedat WARN, but the server has no idea the report was attempted. Reporting the failure back would let #40 flag the projection as stale instead of silently serving an old count.(1) is the immediate fix; (2) and (3) are what stop it recurring at a larger size.
Environment
v1.1.1, agentv1.1.1(commit1131f433)0.18.1, protocol_version 1github.com/coder/websocket v1.8.14runningjobs — why it is silent), #37 (fixing the panic is what exposed this)