Skip to content

dotnet: bound a channel's receive queue by bytes, not by 32 items - #61

Merged
Sniperlyf3 merged 1 commit into
mainfrom
claude/meowsshapi-prs-issues-k2bx2m
Sep 25, 2026
Merged

Sniperlyf3 merged 1 commit into
mainfrom
claude/meowsshapi-prs-issues-k2bx2m

Conversation

@Sniperlyf3

Copy link
Copy Markdown
Owner

Problem

A healthy SSH session could fail with channel data is not being consumed fast enough.

  • Cause: AgentChannelDataPump queued at most 32 items per channel, whatever their size. A burst of small PTY output frames, such as a prompt redraw or a command that prints line by line, filled the queue before the consumer caught up.
  • Where it showed up: dotnet-android-e2e on ci: run the Go agent E2E suite on Windows, and fail if it skips #60. The Android probe's SSH session was faulted on a commit whose sibling run passed the same job.
  • What the bound is for: capping memory held for a consumer that has stopped. At 32 items, that was a few hundred bytes for small frames but 1 MiB for 32 KiB frames. The limit guarded the wrong quantity.

Fix

  • Byte limit: each channel's queue holds at most 4 MiB of payload (MaxQueuedBytes).
  • Item cap: at most 16384 items (MaxQueuedItems), so a flood of empty frames can't grow the queue's own overhead.
  • Release on delivery: bytes are released when an item is delivered, not when it is dequeued, because an item handed to a stalled sink is still in memory.
  • Oversized frames: an empty queue always accepts the next item, so a single frame larger than the limit is never mistaken for backpressure.
  • Unchanged: a channel whose consumer really has stopped is still faulted without blocking the shared read loop (N1).

Verification

  • Full suite: dotnet test dotnet/Meowshell.sln passes, 227/227.

  • New tests:

    • 4096 three-byte frames into a stalled sink do not fault the channel.
    • A frame larger than the limit is accepted into an empty queue.
    • A consumer that keeps up receives 3× the limit in total without faulting.
  • Mutation checks (each rule broken on purpose; a test failed every time):

    Mutation Test that failed
    Item cap set back to 32 burst test
    Empty-queue exemption removed oversize test
    Byte limit removed stall test
    Bytes never released running-total test
  • Existing tests: the two stall tests and ReceiveBackpressureRequestsRemoteChannelCleanupExactlyOnce now overflow the queue by bytes, using 32 KiB frames (the agent's read size), instead of by item count.

🤖 Generated with Claude Code

https://claude.ai/code/session_013WM5HyhwRLKBDyHR6qitkk


Generated by Claude Code

A healthy shell could be failed with "channel data is not being consumed
fast enough". AgentChannelDataPump queued at most 32 items per channel,
whatever their size, so a burst of small PTY output frames (a prompt
redraw, a line-at-a-time command) filled it before the pump caught up.
dotnet-android-e2e hit exactly that on #60: the probe's SSH session was
faulted on a commit whose sibling run passed the same job. The bound is
there to cap memory held for a consumer that has stopped, and 32 items
capped that at a few hundred bytes for small frames while allowing 1 MiB
for 32 KiB ones.

The queue is now unbounded as a channel and bounded by what it holds: at
most 4 MiB of payload, plus a 16384-item cap so a flood of empty frames
cannot grow the queue's own overhead. Bytes are released once an item is
delivered, not when it is dequeued, since the item handed to a stalled
sink is still in memory. An empty queue always takes the next item, so a
single frame larger than the limit is never mistaken for backpressure.

Verified: dotnet test dotnet/Meowshell.sln 227/227. New tests: a burst of
4096 three-byte frames into a stalled sink does not fault; a frame over
the limit is accepted into an empty queue; a consumer that keeps up
receives 3x the limit without faulting. Each rule was mutated and a test
failed: item cap back to 32 (burst test), no empty-queue exemption
(oversize test), no byte limit (stall test), bytes never released
(running-total test). The two existing stall tests now overflow by bytes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013WM5HyhwRLKBDyHR6qitkk
@Sniperlyf3
Sniperlyf3 merged commit c4c79d7 into main Sep 25, 2026
22 checks passed
@Sniperlyf3
Sniperlyf3 deleted the claude/meowsshapi-prs-issues-k2bx2m branch September 25, 2026 17:10
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