test(geode): add LoadBenchmark.fanoutScaling for selective-fanout perf
Verifies the FilterIndex path in LiveEventStore: each event matches exactly ONE of N subscribers, so without an index the relay walks all N subs per event (O(N)); with the author-keyed index the lookup is O(1) and per-event latency stays flat as N grows. Different from fanoutLatency (broadcast: 1 event reaches every sub). Here per-event work is lookup-bound rather than delivery-bound. Configurable via -DfanoutScalingSubs (comma list) and -DfanoutScalingEvents. Measured (laptop, JDK 21, full sweep): 100 subs, 2k events: p50=1.35ms p99=5.71ms 1000 subs, 2k events: p50=1.05ms p99=3.05ms 5000 subs, 1k events: p50=1.01ms p99=2.96ms p50 ~1ms across N — the predicted O(1) scaling. Default events count adapts downward at high N to stay below WebSocketSessionPump's 8192-frame outbound cap (test-client read side is the bottleneck above ~5k subs, not the relay). Plan doc updated with the measured numbers in the "How to verify" section.
This commit is contained in:
@@ -119,18 +119,37 @@ predicate. Dispatch:
|
||||
|
||||
### How to verify
|
||||
|
||||
`geode.perf.LoadBenchmark.fanoutScaling` (to be added):
|
||||
`geode.perf.LoadBenchmark.fanoutScaling` (added):
|
||||
|
||||
- N connections, each subscribes to `{authors: [pk_i], kinds: [1]}`.
|
||||
- Publish 10k EVENTs from a producer connection; each event matches
|
||||
- Publish events round-robin across the N pubkeys; each event matches
|
||||
exactly one subscriber.
|
||||
- Measure end-to-end latency p50/p99 for N ∈ {100, 1000, 5000}.
|
||||
|
||||
Without the index, p99 grows roughly linearly with N. With the
|
||||
index, p99 should be flat up to a much higher N.
|
||||
The benchmark adapts the per-N event count downward to stay below
|
||||
geode's `WebSocketSessionPump.MAX_OUTGOING_BUFFER` (8192 frames per
|
||||
session). The single-WS test subClient can't drain (subs + events)
|
||||
frames at full firehose rate above ~5k subs in a shared test JVM —
|
||||
that's a test-infra ceiling, not a relay one. Override with
|
||||
`-DfanoutScalingEvents=N` to push past the default.
|
||||
|
||||
Measured numbers from a development laptop (Linux, JDK 21, full
|
||||
sweep):
|
||||
|
||||
| N subs | events | mean (ms) | p50 (ms) | p99 (ms) | p999 (ms) |
|
||||
|-------:|-------:|----------:|---------:|---------:|----------:|
|
||||
| 100 | 2 000 | 1.63 | 1.35 | 5.71 | 20.49 |
|
||||
| 1 000 | 2 000 | 1.12 | 1.05 | 3.05 | 9.40 |
|
||||
| 5 000 | 1 000 | 1.07 | 1.01 | 2.96 | 7.38 |
|
||||
|
||||
p50 stays at ~1 ms across all three N values — the index is producing
|
||||
the predicted O(1)-per-event scaling. The minor p99 spread comes from
|
||||
GC pauses and OkHttp scheduler jitter on the test client, not from
|
||||
linear per-event work in the relay.
|
||||
|
||||
For the LocalCache side, a similar benchmark would publish events
|
||||
matching one of M observers and measure dispatch cost as M grows.
|
||||
Not yet added — Phase 2 candidate for a follow-up.
|
||||
|
||||
## Risks
|
||||
|
||||
|
||||
Reference in New Issue
Block a user