test(messaging): add chat fan-out load test (Week 5) - #77
Conversation
Adds a Socket.IO chat load driver: many receivers join a room and listen for chat:message broadcasts while a subset send, measuring end-to-end fan-out latency, delivery completeness, and rate-limit/moderation engagement. Supports multi-pod fan-out via --urls (Redis adapter). Plus a README. - load-test/chat-load-fanout.js — fan-out latency via in-payload timestamp (chat:message has no ack); reports delivery %, cross-pod completeness, and throttling counts; --urls spreads clients across pods - load-test/results/CHAT-LOAD.md — how to run, args, interpretation + caveats (active rate limiter, shared-token limitation) Note: authored by Claude (Anthropic) via Claude Code. Not yet executed against a live backend — results section is a placeholder, not fabricated. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard. |
|
Important
This repository does not receive automatic reviews because it has fewer than 10 stars. ⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Plus Run ID: Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
|
There was a problem hiding this comment.
3 issues found across 2 files
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="load-test/chat-load-fanout.js">
<violation number="1" location="load-test/chat-load-fanout.js:134">
P2: When `--urls` names multiple pods, this round-robin sends viewers to non-owner pods where `joinRoom` cannot find the room state, so most clients fail and the run does not exercise cross-pod fan-out. Route room operations through the owner or restrict this mode to an ingress that provides ownership routing.</violation>
<violation number="2" location="load-test/chat-load-fanout.js:273">
P2: When rate limiting or moderation blocks messages, `totalExpected` still includes those messages even though they never broadcast, understating delivery percentage as a fan-out failure. Exclude rejected messages from the denominator using the rejection events or an explicit server acceptance signal.</violation>
</file>
<file name="load-test/results/CHAT-LOAD.md">
<violation number="1" location="load-test/results/CHAT-LOAD.md:98">
P3: The "Bounded under load" caveat defines the target as `deliveries received ≈ (broadcast messages × receivers)`, but the script computes the reported `Delivery %` against `sent × receivers` (`totalExpected = results.sent * joined.length`). Because the doc also states rate limiting/moderation will actively block some sent messages before they ever broadcast, the printed Delivery% is guaranteed to show a shortfall under load even when fan-out is complete. Ground the caveat's equation in what the script prints (sent × receivers) or instead point readers at the `fully fanned-out` metric, which already uses the broadcast denominator.</violation>
</file>
Reply with feedback, questions, or to request a fix.
Re-trigger cubic
| // Delivery completeness. Each message that passes the server gate should | ||
| // reach every joined receiver. Rate-limited / moderated messages never | ||
| // broadcast, so they simply won't appear in nonceDeliveries. | ||
| const totalExpected = results.sent * joined.length; |
There was a problem hiding this comment.
P2: When rate limiting or moderation blocks messages, totalExpected still includes those messages even though they never broadcast, understating delivery percentage as a fan-out failure. Exclude rejected messages from the denominator using the rejection events or an explicit server acceptance signal.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At load-test/chat-load-fanout.js, line 273:
<comment>When rate limiting or moderation blocks messages, `totalExpected` still includes those messages even though they never broadcast, understating delivery percentage as a fan-out failure. Exclude rejected messages from the denominator using the rejection events or an explicit server acceptance signal.</comment>
<file context>
@@ -0,0 +1,323 @@
+ // Delivery completeness. Each message that passes the server gate should
+ // reach every joined receiver. Rate-limited / moderated messages never
+ // broadcast, so they simply won't appear in nonceDeliveries.
+ const totalExpected = results.sent * joined.length;
+ const totalReceived = results.delivered;
+ let fullyDelivered = 0;
</file context>
|
|
||
| // ---------- connect a receiver and join the room ---------- | ||
| async function connectAndJoin(idx) { | ||
| const url = URLS[idx % URLS.length]; |
There was a problem hiding this comment.
P2: When --urls names multiple pods, this round-robin sends viewers to non-owner pods where joinRoom cannot find the room state, so most clients fail and the run does not exercise cross-pod fan-out. Route room operations through the owner or restrict this mode to an ingress that provides ownership routing.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At load-test/chat-load-fanout.js, line 134:
<comment>When `--urls` names multiple pods, this round-robin sends viewers to non-owner pods where `joinRoom` cannot find the room state, so most clients fail and the run does not exercise cross-pod fan-out. Route room operations through the owner or restrict this mode to an ingress that provides ownership routing.</comment>
<file context>
@@ -0,0 +1,323 @@
+
+// ---------- connect a receiver and join the room ----------
+async function connectAndJoin(idx) {
+ const url = URLS[idx % URLS.length];
+ const socket = io(url, {
+ transports: ["websocket"],
</file context>
| `deliveries received ≈ (broadcast messages × receivers)` as you scale | ||
| receivers/senders up. A latency blow-up or delivery shortfall is the signal. | ||
|
|
There was a problem hiding this comment.
P3: The "Bounded under load" caveat defines the target as deliveries received ≈ (broadcast messages × receivers), but the script computes the reported Delivery % against sent × receivers (totalExpected = results.sent * joined.length). Because the doc also states rate limiting/moderation will actively block some sent messages before they ever broadcast, the printed Delivery% is guaranteed to show a shortfall under load even when fan-out is complete. Ground the caveat's equation in what the script prints (sent × receivers) or instead point readers at the fully fanned-out metric, which already uses the broadcast denominator.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At load-test/results/CHAT-LOAD.md, line 98:
<comment>The "Bounded under load" caveat defines the target as `deliveries received ≈ (broadcast messages × receivers)`, but the script computes the reported `Delivery %` against `sent × receivers` (`totalExpected = results.sent * joined.length`). Because the doc also states rate limiting/moderation will actively block some sent messages before they ever broadcast, the printed Delivery% is guaranteed to show a shortfall under load even when fan-out is complete. Ground the caveat's equation in what the script prints (sent × receivers) or instead point readers at the `fully fanned-out` metric, which already uses the broadcast denominator.</comment>
<file context>
@@ -0,0 +1,110 @@
+ same JWT, so the per-user bucket is shared. To test many *distinct* users,
+ extend the script to accept a token list — noted as a follow-up.
+- **"Bounded under load"** = fan-out latency percentiles stay flat and
+ `deliveries received ≈ (broadcast messages × receivers)` as you scale
+ receivers/senders up. A latency blow-up or delivery shortfall is the signal.
+
</file context>
| `deliveries received ≈ (broadcast messages × receivers)` as you scale | |
| receivers/senders up. A latency blow-up or delivery shortfall is the signal. | |
| **"Bounded under load"** = fan-out latency percentiles stay flat and the printed `Delivery %` (against `sent × receivers`) tracks `fully fanned-out` (against `broadcast × receivers`) as you scale receivers/senders up. A latency blow-up or a gap between Delivery % and fully-fanned-out is the signal. |


Note: authored by Claude (Anthropic) via Claude Code. Not yet executed against a live backend — results section is a placeholder, not fabricated.
Summary by cubic
Adds a socket.io load test that measures end-to-end chat fan-out latency and delivery completeness, covering the Week 5 goal of keeping messaging bounded under load. The script hasn't been run against a live backend yet, so the results doc's table is a placeholder.
Load test
--urlswith multiple pod addresses to spread clients across signaling pods and exercise Redis adapter fan-out.chat:messagehas no ack.Written for commit 7936135. Summary will update on new commits.