Skip to content

perf(racer): remove small-object read bottlenecks in the sidecar path - #958

Merged
Jordan Olshevski (jveski) merged 10 commits into
mainfrom
racer-object-read-perf
Oct 21, 2026
Merged

Jordan Olshevski (jveski) merged 10 commits into
mainfrom
racer-object-read-perf

Conversation

@jveski

Copy link
Copy Markdown
Contributor

This fixes several bottlenecks that capped small-object reads through the racer-object sidecar when data is already cached in node memory. The dataplane reactor now submits cancellations from scopes cancelled during a service turn before it sleeps, which removes a 1ms stall on most requests at low load. The handoff acceptor now drains the client accept backlog with its remaining budget, and the ingress handoff locks each worker target independently so accepting connections no longer contends with every worker's poll. The SDK's Stat lane is now sized from MaxConnections instead of a fixed 4 slots and 16 queued requests, which caused 503s once more than about 20 GETs were in flight.

…or sleeps

A scope cancelled during a service turn was only noticed by the scan at the end of the next poll, so the reactor slept a full IDLE_WAIT with the cancel unsubmitted. Every client request cancels its disconnect watch before responding, which added about 1ms per request at low load. Reactor::wait now queues pending cancellations before it blocks.
Every worker polls its ingress inbox on each turn. With one mutex over all
targets, 20 workers and the handoff acceptor serialized on it, and the
acceptor blocked on the futex about once per accepted connection, capping
the node's client connection rate. Give each target its own lock and keep
an already registered waker instead of cloning it on every poll.
The Stat lane had four slots and a queue of 16, so a caller that Stats before
every Get (such as the racer-object sidecar) failed with ErrUnavailable once
about 20 requests were in flight, well below MaxConnections. The Stat lane and
idle connection pool now follow MaxConnections, and the queue matches the bulk
lane.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

The pre-wait cancellation scan can evaluate deadlines against the wrong clock environment.

1 open issue
What changed in this PR

Improves cached small-object reads through the Racer sidecar by reducing scheduling delays and contention.

Changes:

  • Submits cancellations before sleeping and drains connection backlogs within budget.
  • Uses independent handoff target locks and avoids redundant waker clones.
  • Scales SDK Stat concurrency and idle connections with MaxConnections.
File Description
pkg/​racersdk/​client.go Expands Stat capacity and connection pooling.
pkg/​racersdk/​client_stat_test.go Tests lane sizing and idle limits.
cmd/​racer-dataplane/​src/​client/​tests.rs Tests backlog draining and wake behavior.
cmd/​racer-dataplane/​src/​client/​listener.rs Accepts multiple connections within budget.
cmd/​racer-dataplane/​runtime/​src/​reactor/​tests.rs Tests cancellation submission before waiting.
cmd/​racer-dataplane/​runtime/​src/​reactor.rs Adds pre-wait cancellation scanning and readiness probing.
cmd/​racer-dataplane/​flow/​src/​admission.rs Separates target locks and optimizes waker reuse.

Review effort: Balanced

Comment thread cmd/racer-dataplane/runtime/src/reactor.rs
A nonzero wait queues ASYNC_CANCEL for scopes whose check fails, and
scope checks read the ambient clock. Enter the reactor's captured
environment for that scan, as poll_budgeted already does, so a caller in
another environment cannot cancel live I/O or miss an expired deadline.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

Expanded idle pooling can block GET admission, and already-woken workers incur unnecessary full cancellation scans.

2 open issues
1 resolved since last review

Review effort: Balanced

Comment thread pkg/racersdk/client.go Outdated
Comment thread cmd/racer-dataplane/runtime/src/reactor.rs Outdated
…e is pending

A pending local wake makes Reactor::wait return without sleeping, so scanning
every in-flight scope first only adds full-table work to budgeted busy turns.
Peek the local wake flag without consuming it and leave the scan to the next
poll_budgeted turn; submission and the swap-based early return are unchanged.
Racer holds an ingress connection slot for every open client connection,
including idle keep-alive ones, until its 30 second request timeout. Since
the Stat lane and the idle pool were both sized from MaxConnections, a
burst of concurrent Stats could leave every ingress slot held by pooled
sockets, and Get, which always dials a fresh connection, would stall until
Racer reclaimed them.

Keep the Stat lane at MaxConnections, but cap the idle pool at four
connections, or MaxConnections if smaller. Add a regression test where the
server admits as many connections as the client budget: a Stat burst
followed by Get now succeeds instead of timing out.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔵 Needs a closer look

The test cleanup deadlock needs fixing, and reactor timing and cross-worker synchronization changes still warrant human validation.

1 open issue
2 resolved since last review

Review effort: Balanced

Comment thread pkg/racersdk/client_stat_test.go Outdated

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔵 Needs a closer look

Reactor cancellation timing and cross-thread handoff changes warrant human review and validation under load.

No open issues 🎉

1 resolved since last review

Review effort: Balanced

@jveski
Jordan Olshevski (jveski) marked this pull request as ready for review October 21, 2026 16:37
@jveski
Jordan Olshevski (jveski) requested a review from a team October 21, 2026 16:37
@jveski
Jordan Olshevski (jveski) added this pull request to the merge queue Oct 21, 2026
Merged via the queue into main with commit 93dc2fe Oct 21, 2026
60 of 61 checks passed
@jveski
Jordan Olshevski (jveski) deleted the racer-object-read-perf branch October 21, 2026 17:01
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.

3 participants