Skip to content

Phase C2: neighbor discovery (Handshaker, NeighborFinder, NeighborUpdateManager)#85

Merged
ptesavol merged 1 commit into
mainfrom
claude/phase-c2
Jul 12, 2026
Merged

Phase C2: neighbor discovery (Handshaker, NeighborFinder, NeighborUpdateManager)#85
ptesavol merged 1 commit into
mainfrom
claude/phase-c2

Conversation

@ptesavol

Copy link
Copy Markdown
Collaborator

Summary

Ports the seven neighbor-discovery classes of the content-delivery layer from the TS pin (src/content-delivery-layer/neighbor-discovery/), with all seven TS test files. Rebased onto current main (over the parallel-session phases C4/C7) with no conflicts.

What's ported

  • HandshakeRpcRemote / HandshakeRpcLocal / Handshaker — stream-part neighbor handshakes: parallel target selection from the four contact views (ring left/right, Kademlia-nearby, random; websocket-node preference when neighborless), up to two handshakes in parallel, and the full interleaving protocol (a full node accepts the requester and hands one existing neighbor over, with the ongoing-interleave accounting that keeps ≥2 neighbors during churn). The TS detached interleave continuation (.then() without await) becomes a time-bounded GuardedAsyncScope task.
  • NeighborFinder — repeated doFindNeighbors rounds (two concurrent chains, 250 ms apart) until minCount neighbors or the contact views are exhausted; self-stops and can be restarted. An INeighborFinder seam supports the TS-style { start: jest.fn() } test doubles.
  • NeighborUpdateRpcRemote / NeighborUpdateRpcLocal / NeighborUpdateManager — periodic neighbor-list exchange: learns new contacts from callers, records round-trip times (setRtt/getRtt added to ContentDeliveryRpcRemote), prunes with removeMe when both sides exceed the target count.
  • Support: NodeList gains the public getLast(exclude) (TS parity — only a static helper existed); CoroutineHelper exports folly's collectAllTryRange (the Promise.allSettled equivalent); TestUtils gains the mock remote factories.

A bug this phase found and fixed in its own code

The first version of the mock-remote factory deliberately leaked its transportless communicator (to dodge static-destruction concerns). That leak held a folly KeepAlive on the shared worker pool forever, so the pool's static destructor blocked at process exit — the test binary printed PASSED and then never terminated (stack-sampled: ~CPUThreadPoolExecutor → joinKeepAlive → Baton::tryWaitSlow). Fixed by making it a function-local static object: constructed after the pool, destroyed before it, KeepAlive released in order. Verified: the three affected fixtures and the full suite now exit cleanly. Lesson recorded for future phases.

Tests (all ported from the pin)

TS test C++
unit/HandshakeRpcLocal.test.ts (7 tests) HandshakeRpcLocalTest
unit/Handshaker.test.ts HandshakerTest (FakeTransport with failing sends instead of simulator — same observable outcome, no timeout waits)
unit/NeighborFinder.test.ts NeighborFinderTest
unit/NeighborUpdateRpcLocal.test.ts (6 tests) NeighborUpdateRpcLocalTest
integration/HandshakeRpcRemote.test.ts HandshakeRpcRemoteTest (house two-communicator pattern)
integration/NeighborUpdateRpcRemote.test.ts NeighborUpdateRpcRemoteTest (same)
integration/Handshakes.test.ts (3 tests incl. interleaving) HandshakesTest over real dht Simulator/SimulatorTransports

Results (local, arm64 mac)

trackerless-network unit 54/54 with clean process exit, streamr-dht unit 181/181, full-tree build green, package and root lint rc=0.

With C2 in, the remaining milestone-C phases are C3 (ContentDeliveryLayerNode, which composes these classes), C5, C6, and C8.

🤖 Generated with Claude Code

…ateManager)

Ports the seven neighbor-discovery classes of the content-delivery layer
from the TS pin, with all seven TS test files:

- HandshakeRpcRemote/HandshakeRpcLocal/Handshaker: stream-part neighbor
  handshakes with parallel target selection from the four contact views
  and the interleaving protocol (a full node accepts the requester and
  hands one existing neighbor over). The TS detached interleave
  continuation becomes a time-bounded GuardedAsyncScope task.
- NeighborFinder: repeated doFindNeighbors rounds (two concurrent
  chains, 250 ms apart) until minCount neighbors or contacts exhausted;
  INeighborFinder seam for the TS-style test doubles.
- NeighborUpdateRpcRemote/RpcLocal/NeighborUpdateManager: periodic
  neighbor-list exchange with rtt recording (setRtt/getRtt added to
  ContentDeliveryRpcRemote) and removeMe pruning.
- NodeList: public getLast(exclude) added (TS parity; only a static
  helper existed).
- CoroutineHelper: export folly's collectAllTryRange (Promise.allSettled
  equivalent).
- TestUtils: createMockContentDeliveryRpcRemote/createMockHandshakeRpcRemote
  factories over a transportless communicator whose sends fail fast.

Local: trackerless-network unit 43/43 (21 new C2 tests).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@ptesavol
ptesavol merged commit 10d47ca into main Jul 12, 2026
6 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant