Skip to content

drpcstream: fail-stop on receive-cap overrun instead of blocking the reader - #94

Open
suj-krishnan wants to merge 1 commit into
sujatha/flow-control-recv-bytesfrom
sujatha/flow-control-recv-cap
Open

drpcstream: fail-stop on receive-cap overrun instead of blocking the reader#94
suj-krishnan wants to merge 1 commit into
sujatha/flow-control-recv-bytesfrom
sujatha/flow-control-recv-cap

Conversation

@suj-krishnan

@suj-krishnan suj-krishnan commented Aug 3, 2026

Copy link
Copy Markdown

Stacked on #93 (base: sujatha/flow-control-recv-bytes). Implements CRDB-65749

Problem

After #93 the receive queue is byte-accounted, but it blocks the producer (manageReader) when a message would exceed the per-stream cap. This fix adds checks so that we neve wedge a whole connection due to a single stream exceeding its message cap.

Change

Enforce the cap with a stream-local fail-stop:

  • Under a byte budget, ringBuffer.Enqueue no longer blocks — it returns false when a message would exceed the cap.
  • handlePacket then terminates only that stream with a new drpc.ReceiveCapError (mapped to codes.ResourceExhausted) and sends the peer an abortive KindError. HandleFrame returns nil, so the shared reader keeps running and other streams are unaffected.
  • Flow control off keeps the legacy slot-blocking behavior unchanged.

Scope / rationale

drpc peers are trusted CockroachDB nodes, so this is not adversarial defense — it's bug-containment and operability: a distinct logged error and one failed (retryable) RPC instead of a silently wedged connection.

…reader

The byte-accounted receive queue blocked the producer (manageReader) when a
message would exceed the per-stream cap. A compliant sender never reaches the cap
-- flow control bounds it -- but a peer that ignores flow control (a bug or, in
mixed-version clusters, a legacy peer) can, and blocking manageReader stalls
frame delivery for every stream on the connection. A single flow-control bug
should not be able to wedge a whole connection.

Enforce the cap with a stream-local fail-stop. Under a byte budget Enqueue no
longer blocks: a message that would exceed the cap returns false, and handlePacket
terminates just that stream with a new ReceiveCapError (mapped to
codes.ResourceExhausted) and sends the peer an abortive KindError. The shared
reader is never blocked and other streams are unaffected. Flow control off keeps
the legacy slot-blocking behavior unchanged.

The abortive-terminate-and-notify sequence is extracted from handleAssembleError
into sendAbortiveError and shared by both per-stream policy violations (oversized
message, cap overrun).

This does not bound zero-credit (empty) message floods: an empty message consumes
no window and never exceeds a byte cap, so it is neither blocked nor failed. That
is a separate follow-up (a per-message minimum charged symmetrically in send
credit, grants, and the queue).

Co-Authored-By: roachdev-claude <roachdev-claude-bot@cockroachlabs.com>
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