Skip to content

feat(stream): add Control::accept_with_buffer - #6637

Open
nikicat wants to merge 1 commit into
libp2p:masterfrom
nikicat:stream-accept-with-buffer
Open

nikicat wants to merge 1 commit into
libp2p:masterfrom
nikicat:stream-accept-with-buffer

Conversation

@nikicat

@nikicat nikicat commented Sep 23, 2026 •

Copy link
Copy Markdown

Description

Control::accept creates the channel behind IncomingStreams with a capacity of zero, and the
connection handler hands each fully negotiated inbound stream over with try_send from within
the swarm's poll. If a second stream for the same protocol arrives before the consumer has taken
the previous one, it is dropped even though negotiation already succeeded: the remote sees a
successful open_stream (and possibly a successful write) for data that is never delivered.
This happens as soon as several peers open a stream for the same protocol at about the same time
(one-stream-per-message protocols hit it routinely).

This PR adds Control::accept_with_buffer(protocol, n), which queues up to n negotiated streams.
accept keeps its current behaviour (it is accept_with_buffer(protocol, 0)), so the change is
non-breaking and bumps libp2p-stream to 0.5.0-alpha.1. Streams dropped because the buffer is
full are now logged at warn instead of debug, since the remote cannot tell they were lost.

The new test opens 16 streams while nobody polls IncomingStreams and expects all 16 to be
delivered; it fails with a buffer of 0.

AI Assistance Disclosure

Tools used: Claude Code

Attestation:

  • I have read every line of this diff, understand what it does, and can explain it in review.

Notes & open questions

  • An alternative would be to make accept itself buffered with a default capacity, or to put the
    capacity into a Behaviour config. I kept accept unchanged to avoid changing behaviour for
    existing users; happy to switch if maintainers prefer one of those.
  • Even with a buffer, a burst larger than n is still dropped, because the handler cannot wait
    for the consumer from within poll. The warn log makes that visible.

Change checklist

  • I have performed a self-review of my own code
  • I have made corresponding changes to the documentation
  • I have added tests that prove my fix is effective or that my feature works
  • A changelog entry has been made in the appropriate crates

🤖 Generated with Claude Code

`Control::accept` creates the channel behind `IncomingStreams` with a capacity
of zero, and the connection handler hands each fully negotiated inbound stream
over with `try_send` from within the swarm's poll. When a second stream for the
same protocol arrives before the consumer has taken the previous one, it is
dropped even though negotiation already succeeded: the remote sees a
successful `open_stream` (and possibly a successful write) for data that is
never delivered. This happens as soon as several peers open a stream for the
same protocol at about the same time.

`accept_with_buffer(protocol, n)` queues up to `n` negotiated streams. `accept`
keeps its current behaviour (`accept_with_buffer(protocol, 0)`), so the change
is non-breaking. Streams dropped because the buffer is full are now logged at
`warn` instead of `debug`.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@nikicat
nikicat force-pushed the stream-accept-with-buffer branch from 58c8cf8 to 9f259c8 Compare September 23, 2026 14:04
@nikicat
nikicat marked this pull request as ready for review September 23, 2026 19:03

This branch has not been deployed

No deployments
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