Skip to content

beacon: shard the relay queue per worker - #62

Merged
TeoSlayer merged 1 commit into
mainfrom
perf/shard-relay-queue
Oct 1, 2026
Merged

TeoSlayer merged 1 commit into
mainfrom
perf/shard-relay-queue

Conversation

@TeoSlayer

Copy link
Copy Markdown
Contributor

Problem

A single relayCh (524288 deep) carried every relay send/recv from 32 SO_REUSEPORT readers to 16 workers — all serialised on one hchan lock. At ~300k relays/sec the live mutex profile showed relayCh send/recv as the top two contention sites (174M / 216M acquisitions) and the main driver of the futex/selectgo scheduler churn (~13% each).

Change

One queue per worker, dispatched by destID % N:

  • senders spread across N independent hchan locks (no shared lock),
  • each worker gets a private receive queue,
  • per-destination ordering is preserved (same dest → same worker),
  • total capacity unchanged (relayQueueSize split evenly), drop-if-full semantics unchanged.

Worker count logic is centralised in relayWorkerCount() so New sizes the queues exactly as ListenAndServe spawns workers.

Tests

go test ./... green (incl. the relay-source-limit and dispatch fuzz targets, updated to drain all shards).

A single relayCh (524288 deep) carried every relay send/recv from 32
SO_REUSEPORT readers to 16 workers, serialising all of them on one hchan
lock. At ~300k relays/sec the mutex profile showed relayCh send/recv as the
top two contention sites (174M/216M acquisitions) and drove the futex /
selectgo scheduler churn.

Replace it with one queue per worker, dispatched by destID % N: senders
spread across N hchan locks, each worker has a private receive queue, and
per-destination ordering is preserved. Total capacity is unchanged
(relayQueueSize split evenly). Worker count logic is centralised in
relayWorkerCount() so New sizes queues exactly as ListenAndServe spawns.
@TeoSlayer
TeoSlayer merged commit 0a53c15 into main Oct 1, 2026
4 checks passed
@TeoSlayer
TeoSlayer deleted the perf/shard-relay-queue branch October 1, 2026 22:24
@codecov

codecov Bot commented Oct 1, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

TeoSlayer pushed a commit that referenced this pull request Oct 1, 2026
#62's destID-sharded relay queue already gives a flow per-worker ordering,
so this keeps only what #61 still adds on top of main:

- The batch flush falls back per-packet on any short write (n < len(msgs)),
  not only on an error. WriteBatch reports no error when a platform without
  sendmmsg sends one message per call, so the rest of the batch was silently
  discarded; #61 measured 19 of 600 relayed packets lost that way.
- maxRelaysPerSourcePerSecond 1000 -> 4000. The cap counts packets, and
  daemons are moving from ~4.2 KB stream datagrams (IP-fragmented) to
  ~1.2 KB ones, so 4000 keeps the same ceiling in bytes (~4 MB/s).
- relayQueueFor extracted so the per-dest queue invariant has a test, plus
  the order test from #61 and the WSS test enable-before-serve race fix.
TeoSlayer added a commit that referenced this pull request Oct 1, 2026
#62's destID-sharded relay queue already gives a flow per-worker ordering,
so this keeps only what #61 still adds on top of main:

- The batch flush falls back per-packet on any short write (n < len(msgs)),
  not only on an error. WriteBatch reports no error when a platform without
  sendmmsg sends one message per call, so the rest of the batch was silently
  discarded; #61 measured 19 of 600 relayed packets lost that way.
- maxRelaysPerSourcePerSecond 1000 -> 4000. The cap counts packets, and
  daemons are moving from ~4.2 KB stream datagrams (IP-fragmented) to
  ~1.2 KB ones, so 4000 keeps the same ceiling in bytes (~4 MB/s).
- relayQueueFor extracted so the per-dest queue invariant has a test, plus
  the order test from #61 and the WSS test enable-before-serve race fix.

Co-authored-by: Teo Calin <calinteodor@Teos-MacBook-Pro.local>
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