Skip to content

rendezvous: take the remaining hot-path locks off the request path - #135

Merged
TeoSlayer merged 3 commits into
mainfrom
perf/hotpath-locks
Oct 1, 2026
Merged

TeoSlayer merged 3 commits into
mainfrom
perf/hotpath-locks

Conversation

@TeoSlayer

Copy link
Copy Markdown
Contributor

Follow-up to the registry CPU work (#130–#134). Live 5-minute mutex profile after the v0.2.9-beta.6 deploy still listed four avoidable hot-path sites; this PR removes them.

Changes

  1. accept: lock-free whitelist. IsWhitelisted took the rate-limiter mutex (the same one guarding token buckets) and re-parsed the CIDR list on every call — ~1.1M acquisitions / 3.5k s of wait per 5 min. The whitelist is written rarely and read per connection, so it is now published copy-on-write behind an atomic.Pointer[[]whitelistRule].
  2. accept: throttle read-deadline refresh. Both connection loops re-armed a 5-minute SetReadDeadline on every message — one runtime timer modification per request (~43k/s live). Deadline is now re-armed at most once a minute, keeping ≥4 minutes of headroom; idle semantics unchanged.
  3. cmd/rendezvous: async slog handler. The default handler formats and writes under its own mutex on the caller goroutine (~1.1k s of wait per 5 min in the mutex profile). Records are now enqueued to a buffered queue and written by a dedicated goroutine; overflow drops are counted instead of blocking. The dynamic log-level endpoint reinstalls the wrapper.
  4. directory: release the global write lock before signalling save in HandleSetHostname. st.cb.Save() is a non-blocking trigger, so it no longer runs inside the st.mu + node-shard critical section.

Testing

  • go build ./..., go test ./... green.
  • go test -race -run "Whitelist|RateLimit" ./accept/ green.
  • Goroutine audit of the live registry: 54k goroutines are parked connection readers / relay workers, no leak.

Verification plan

Deploy to pilot-rendezvous-new, capture 5-minute mutex + CPU profiles, and compare against /tmp/locks5m.json / /tmp/cpu5m.pprof (before). Snapshot output remains byte-identical (no encoder changes).

Teo Calin added 3 commits October 2, 2026 01:51
IsWhitelisted took the same mutex as the token buckets and re-parsed the
CIDR list on every call; the live mutex profile showed it as the #2
contention site (~3.5k s of wait per 5 min). The whitelist is written
rarely (operator) and read on every connection, so publish it
copy-on-write behind an atomic.Pointer and drop the lock.

The JSON and binary connection loops also re-armed a 5-minute read
deadline on every message, one runtime timer modification per request
(~43k/s under load). Re-arm at most once a minute; the deadline keeps at
least 4 minutes of headroom, so idle-connection semantics are unchanged.
The default slog handler formats and writes under its own mutex on the
caller goroutine; the live mutex profile showed it as a top contention
site (~1.1k s of wait per 5 min). Wrap the configured handler in a
buffered async handler so hot request paths only enqueue, with formatting
and writing on a dedicated goroutine. Overflow drops are counted instead
of blocking callers. The dynamic log-level endpoint reinstalls the
wrapper so it survives level changes.
The handler held the global write lock (and the node shard) across
st.cb.Save(). Save is a non-blocking trigger and reads state under its
own locks when it runs, so moving it after the unlock shortens the
critical section without changing ordering.
@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
TeoSlayer merged commit 765fb66 into main Oct 1, 2026
9 checks passed
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