Add BEP 11 PEX and a growable shared peer pool to the swarm#12
Merged
Conversation
The swarm statically partitioned the initial peer list across workers at Start, so a worker that lost its peers couldn't get others' and PEX peers had nowhere to go. Replace that with a shared, growable pool and wire in BEP 11 peer exchange. Protocol (peer.go): - Advertise ut_pex (local id 2) alongside ut_metadata in the BEP 10 extended handshake. - parsePexPeers validates against PeerMessageLimits, then unpacks added (compact v4) and added6 (compact v6) via the existing unpackPeers, and drops unconnectable entries (port 0 / unspecified IP) at the boundary. - PeerConn buffers harvested peers (handlePexMessage / DrainPexPeers). Swarm (swarm.go): - peerChan (buffered) + mutex-guarded knownAddrs replace the static split; AddPeers dedups and pushes non-blocking. - Workers acquire a connection from the pool BEFORE pulling work, so a worker waiting on a peer can't strand a piece marked in-progress. - inFlight (atomic) is incremented at the pull, before dialing, so the new exhaustion supervisor can't mistake an in-flight connect for a dead swarm; it cancels only after the pool is empty, inFlight==0, and pieces remain, sustained across a short grace window. - The unbuffered-resultChan / drain / close-after-WaitGroup shutdown is preserved verbatim. Wiring (downloader.go): downloadPiece harvests ut_pex messages; the worker drains them into the pool after each piece. connectToAny is replaced by a single-addr dialAndHandshake. Tested unit (dead-peer skip, PEX discovery e2e, exhaustion, no-leak) under -race, and end-to-end against a real Transmission seeder (5 MiB / 20 pieces, checksum verified) — during which Transmission's ut_pex was harvested and used, validating the path against a real client.
iksnerd
added a commit
that referenced
this pull request
Jun 21, 2026
Add BEP 11 PEX and a growable shared peer pool to the swarm
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
Replaces the swarm's static peer-list partitioning with a shared, growable peer pool and wires in BEP 11 peer exchange (ut_pex). PEX discovers peers during a download; the old
Start()split the initial list across workers with no way to add peers mid-flight, so PEX peers had nowhere to go — doing both together also closes the deferred static-partitioning reliability gap.Changes
Protocol (
peer.go)ut_pex(local id 2) alongsideut_metadatain the BEP 10 extended handshake.parsePexPeers: validates againstPeerMessageLimits, unpacksadded(compact v4) +added6(compact v6) via the existingunpackPeers, and drops unconnectable entries (port 0 / unspecified IP) at the trust boundary.PeerConnbuffers harvested peers (handlePexMessage/DrainPexPeers).Swarm (
swarm.go)peerChan(buffered) + mutex-guardedknownAddrsreplace the static split;AddPeersdedups + non-blocking push.inFlight(atomic) is incremented at the pull, before dialing, so the new exhaustion supervisor can't mistake an in-flight connect for a dead swarm. It cancels only after the pool is empty,inFlight==0, and pieces remain — sustained across a short grace window.resultChan/ drain / close-after-WaitGroupshutdown is preserved verbatim.Wiring (
downloader.go)downloadPieceharvestsut_pexmessages; the worker drains them into the pool after each piece.connectToAny→ single-addrdialAndHandshake.Testing
-race): dead-peer skip, PEX discovery end-to-end, peer-supply exhaustion, no goroutine leak, plus the parser/handshake/drain tests.transmission-daemonseeder (5 MiB / 20 pieces) — checksum verified. Transmission'sut_pexwas harvested and used mid-download, validating the PEX path against a real client. This run also surfaced (and fixed) Transmission advertising an unconnectable port-0 peer.🤖 Generated with Claude Code