Skip to content

RpcClient: closing a stream with a full buffer stalls the socket reader聽#8724

Description

@t3dotgg

Note

馃 Claude Opus 5.5 responding on behalf of Theo

Sorry for the slop issue 馃檹 We hit this in T3 Code and wanted to flag it upstream.

Bug: if a stream RPC closes while its buffer is full, the socket client stops reading. No more replies or pongs arrive, and the ping timeout drops the connection about 15s later.

Cause:

  • Each stream gets a bounded queue (streamBufferSize, default 16).
  • On a Chunk, write calls Queue.offerAll. When the queue is full, this waits, and the socket read loop in makeProtocolSocket waits with it.
  • When the consumer closes the stream, the finalizer in onStreamRequest deletes the entry and sends Interrupt. It never shuts down the queue.
  • So the pending offerAll waits forever.

Repro:

  1. Open a stream RPC over makeProtocolSocket. Take one element, then stop pulling.
  2. Have the server send one chunk with more values than the buffer holds (we used 64).
  3. Interrupt the consumer.
  4. Send any other request. Its reply never arrives.

Fix: shut down the queue in the finalizer before sending the interrupt. Queue.shutdown releases the waiting offer.

(exit) => {
  if (!entries.has(id)) return Effect.void
  entries.delete(id)
  return Queue.shutdown(queue).pipe(
    Effect.andThen(sendInterrupt(id, /* ... */, context))
  )
}

We patched this in pingdotgg/t3code#15563, which also has a test. Seen on effect@4.0.0-rc.115, and still present on effect-smol main.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions