relay_overhead_seconds.
For how to turn these numbers into a deployment, see Sizing and tuning.
What this measures
Each run puts one relay (or a group of relays behind HAProxy) on its own box, sends it an open-loop request schedule from a generator box, and steps the offered rate up until the relay refuses part of it. The upstream is a mock that answers with a fixed latency model, so every millisecond of variation above it is Relay, the network, or the state store. This is not a service-level objective, and the numbers are not a promise for any other setup. The repository’s in-process benchmarks (make bench-gate, in-memory state, no network) are a regression gate for allocations and are not published as performance figures.
Setup
How to read the numbers
- Overhead is measured by the generator: the client-side total latency of a request minus the duration the mock upstream reports for it. It includes two network hops inside one availability zone, so it is a little higher than
relay_overhead_seconds, which Relay measures inside the process. Each step reports the worse of the two request types. - Errors are every non-2xx response, as a share of all responses, the worse of the two request types. A request refused as overloaded (
503, or529on Anthropic-shaped routes) counts as an error. - Latency knee is the highest step at which p99 overhead stays at or under 15 ms with at most 0.1 % errors, with every lower step passing too.
- Error capacity is the highest served rate at a step with at most 0.1 % errors. Its p99 is always given with it, because it is usually well past the knee.
- Relay cores and working set cover the last two minutes of each step. Working set is the largest value in that window.
- A step where the generators fell behind their schedule measures the generator, not Relay. Those steps are marked as excluded and left out of every figure. Where the generators sent less than the step’s rate without falling behind, the table shows what was sent.
- Usage record delivery was not checked in these runs.
Results by scenario
One relay per box
Served against offered requests per second on one relay, by box size. The v0.11.8 line on 16 and 32 cores is the later run described below, not part of the v0.11.7 tables.
- v0.11.7 (before engine rework)
The 32-core ladder steps from 16,000 straight to 32,000 (p99 41.1 ms), so its knee lies somewhere between the two. Its highest served figure comes from a second run on the same boxes; the first run’s 64,000 step was generator-bound.4 cores (c7a.xlarge)
8 cores (c7a.2xlarge)
16 cores (c7a.4xlarge)
Second run on the same boxes:
32 cores (c7a.8xlarge)
Second run on the same boxes:
Requests per core
Latency knee and error capacity per core. Box cores, 32 for the four-relay group.
Four relays behind HAProxy
Four 8-core relays (c7a.2xlarge) behind HAProxy 3.2 on a c7a.4xlarge, sharing one store. HAProxy keeps connections to the relays alive (http-reuse always). The generator box was a c7a.8xlarge, smaller than in the single-box runs, and at 64,000 and 80,000 the generators sent slightly less than the step’s rate.
Four 8-core relays sharing one store, against four times one 8-core box at a quarter of the load.
- v0.11.7 (before engine rework)
Latency knee 32,022 rps (p99 1.9 ms); error capacity 62,385 rps (p99 35.8 ms). Relay cores are the sum of the four relays; working set is the largest of the four. Admission p99 is
relay_admission_seconds, the time from accepting a request to handing it upstream, which under load is mostly the wait for a store connection.State store
One 16-core relay (c7a.4xlarge), the store on its own c7a.4xlarge. The Valkey run used the v0.11.7 code without its last commit, the default in-flight cap of 2048 and RELAY_UPSTREAM_MAX_IDLE_PER_HOST=1024. Both Dragonfly runs used the v0.11.7 code with the idle cap at its default (the in-flight cap); the default-flags run kept the in-flight cap at 2048, the hash-tag run used 20000. The cap of 2048 does not bind below about 30,000 rps on this box.
- v0.11.7 (before engine rework)
Valkey 8, io-threads 4
Dragonfly v2.0.2, default flags
Dragonfly v2.0.2, —cluster_mode=emulated —lock_on_hashtags
--cluster_mode=emulated --lock_on_hashtags each script stays on one shard, and 28,048 rps was still under 0.1 % errors at the last step.
Upstream idle connections
One 8-core relay (c7a.2xlarge) on Valkey 8, the v0.11.7 code without its last commit (so the idle cap does not follow the in-flight cap), in-flight cap 2048.
- v0.11.7 (before engine rework)
RELAY_UPSTREAM_MAX_IDLE_PER_HOST=128RELAY_UPSTREAM_MAX_IDLE_PER_HOST=1024Streaming
One 16-core relay (c7a.4xlarge), streaming Anthropic Messages and OpenAI Chat Completions, 8 tokens per event. The mock starts each stream after 300 ms and sends 80 tokens per second; streams last 2.1 s at p50, 15 s at p99 and 3 s on average, so open streams are the start rate times three.
Relay working set against open streams on one 16-core relay.
- v0.11.7 (before engine rework)
Default in-flight cap
The same boxes at the defaultRELAY_MAX_INFLIGHT of 2048, generators on a c7a.8xlarge. On 16 and 32 cores the cap, not the box, sets the ceiling near 30,000 rps: requests over it are refused as overloaded while cores sit idle.
- v0.11.7 (before engine rework)
8 cores (c7a.2xlarge)
16 cores (c7a.4xlarge)
32 cores (c7a.8xlarge)
Later run: 16 and 32 cores on the v0.11.8 code
A separate run after v0.11.7, on 16 and 32 cores only, with a development build carrying the Go code of v0.11.8 before its dependency updates. It includes allocation and garbage-collection work on the request path. It comparesGOGC=100 with GOGC=400, alternating the two on the same boxes; each row is the mean of two runs, except the 32-core steps from 48,000 up, which ran once. Same setup as the v0.11.7 table otherwise: generators on a c7a.16xlarge, Dragonfly with hash tags on a c7a.8xlarge, RELAY_MAX_INFLIGHT=20000. There are no v0.11.8 figures for 4 cores, 8 cores or the four-relay group.
GOGC=400 used about 9 % fewer cores per request on 16 cores and 10–16 % fewer on 32 cores, cut p99 overhead by a quarter to a half, and cost 0.7–1.6 GiB more working set. On 16 cores both settings stop at about 40,000, where the wait for the store binds first.
Step tables
Step tables
16 cores (c7a.4xlarge), GOGC 100
16 cores (c7a.4xlarge), GOGC 400
32 cores (c7a.8xlarge), GOGC 100
32 cores (c7a.8xlarge), GOGC 400
Past capacity
Past error capacity, a single relay refuses the excess as overloaded (503, or 529 on Anthropic-shaped routes, with Retry-After: 1) and keeps serving close to its highest rate: the 8-core box served 25,563 rps at 32,000 offered against a peak of 26,141, and the 16-core box 41,451 at 56,000 against 41,860. Two exceptions:
- The four-relay group fell from 77,921 rps to 60,618 at the next step, with 9.7 % errors, as the wait on the shared store reached its highest (admission p99 123 ms).
- On 32 cores the generators fell behind above 64,000 offered, so behaviour past that point was not measured.