Skip to content

client: force-close abandoned client streams via runtime cleanup#235

Open
dmcgowan wants to merge 1 commit intocontainerd:mainfrom
dmcgowan:cleanup-abandoned-clients
Open

client: force-close abandoned client streams via runtime cleanup#235
dmcgowan wants to merge 1 commit intocontainerd:mainfrom
dmcgowan:cleanup-abandoned-clients

Conversation

@dmcgowan
Copy link
Copy Markdown
Member

@dmcgowan dmcgowan commented May 5, 2026

Attach a runtime.AddCleanup callback to every clientStream returned by NewStream so that callers who drop the stream without consuming it have the underlying *stream force-closed and removed from the connection's stream map. Without this safety net a leaked stream's recv buffer fills and the connection's read loop only recovers via the 1-second ErrStreamFull fallback, leaving streamID slots and goroutines pinned until that timeout fires.

The cleanup sets recvErr to errStreamAbandoned (unexported - the cleanup runs after the clientStream is unreachable, so no caller is left to match on it as a sentinel) and the abandon surfaces in the receive loop's "failed to handle message" log via the existing error path.

Also adds NewClientWithContext so callers - notably tests - can supply a parent context whose attached logger is used for the client's internal goroutines, and a unit test that drives GC, asserts errStreamAbandoned on the closed stream, verifies the connection is not deadlocked, and captures the abandon log through the supplied context's logger.

Bumps the Go minimum to 1.24 for runtime.AddCleanup.

@dmcgowan dmcgowan force-pushed the cleanup-abandoned-clients branch from 1a75cfd to 7b24df1 Compare May 5, 2026 04:53
Attach a runtime.AddCleanup callback to every clientStream returned by
NewStream so that callers who drop the stream without consuming it have
the underlying *stream force-closed and removed from the connection's
stream map. Without this safety net a leaked stream's recv buffer fills
and the connection's read loop only recovers via the 1-second
ErrStreamFull fallback, leaving streamID slots and goroutines pinned
until that timeout fires.

The cleanup sets recvErr to errStreamAbandoned (unexported - the cleanup
runs after the clientStream is unreachable, so no caller is left to
match on it as a sentinel) and the abandon surfaces in the receive
loop's "failed to handle message" log via the existing error path.

Also adds NewClientWithContext so callers - notably tests - can supply a
parent context whose attached logger is used for the client's internal
goroutines, and a unit test that drives GC, asserts errStreamAbandoned
on the closed stream, verifies the connection is not deadlocked, and
captures the abandon log through the supplied context's logger.

Bumps the Go minimum to 1.24 for runtime.AddCleanup.

Signed-off-by: Derek McGowan <derek@mcg.dev>
@dmcgowan dmcgowan force-pushed the cleanup-abandoned-clients branch from 7b24df1 to 10ccd75 Compare May 5, 2026 22:13
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