Measure a real conversation's cost (relay#38) #101

Merged
paulhitt merged 1 commits from feat/measure-conversation-cost into main 2026-09-04 20:40:54 +00:00
paulhitt commented 2026-09-04 15:27:58 +00:00 (Migrated from gitlab.com)

Nobody knew what one message cost, so nobody could rank the rest of the
scaling backlog. This walks a conversation, a group, a call and an attachment
through the real application and counts requests, payload bytes and database
round trips per flow, with the numbers and the ranking written up in
docs/COST.md. The budgets are ceilings, so a change that doubles the work
behind a send fails here with a number.

The headline: one message is 2 requests and 10 round trips — and seven of
those ten are authorisation and presence, three touch a message. Every idea
on the backlog about moving envelopes around is optimising the smaller half.

The measurement immediately found a duplicate read. authenticate loads
the device row to check the token's key binding, then poll loads the same
row again for the revocation check — one primary-key read done twice,
microseconds apart, on the busiest route in the system. Filed separately.

It also corrected relay#27's premise. That issue says a ten-person group
with two devices each is twenty uploads; the upload count is right and the
request count is not, because /e2e/messages takes one account and a LIST of
that account's devices. Ten people with two devices is ten requests, not
twenty. Fan-out is per person, only the blob is per device — a bandwidth
argument, not a request-count one.

And it downgraded relay#28. Round trips are flat against retained history —
the cursor query is id > after on an index and returns one page whatever is
behind it. What is not flat is bytes, and the client decides: a poll with no
cursor asks from zero and returns 21 KB of retained envelopes against 381
bytes for the one that is new. Cold storage is an argument about disk, not
about the hot path.

Two faults in the harness itself, both caught by disbelieving a number:

  • app.fluent.history records nothing a route does. Handlers use req.db,
    which reads the history flag from the REQUEST's storage, fresh per request.
    Every flow measured zero queries and every budget passed vacuously. A cost
    harness that reports zero cost is worse than none — it answers with a wrong
    number. Counted through a middleware that starts history per request now.

  • The history test compared a cold relay against a warm one and got ELEVEN
    round trips cold against TEN warm. Storage growth cannot make polls cheaper.
    The breakdown named it: the presence heartbeat writes once per account per
    online window, so the first sample paid for a write the second had already
    made. Both samples are now taken warm.

631 relay tests in 56 suites green, core paths 7/7.

Co-Authored-By: Claude Opus 5 noreply@anthropic.com

Nobody knew what one message cost, so nobody could rank the rest of the scaling backlog. This walks a conversation, a group, a call and an attachment through the real application and counts requests, payload bytes and database round trips per flow, with the numbers and the ranking written up in docs/COST.md. The budgets are ceilings, so a change that doubles the work behind a send fails here with a number. The headline: one message is 2 requests and 10 round trips — and seven of those ten are authorisation and presence, three touch a message. Every idea on the backlog about moving envelopes around is optimising the smaller half. The measurement immediately found a duplicate read. `authenticate` loads the device row to check the token's key binding, then `poll` loads the same row again for the revocation check — one primary-key read done twice, microseconds apart, on the busiest route in the system. Filed separately. It also corrected relay#27's premise. That issue says a ten-person group with two devices each is twenty uploads; the upload count is right and the request count is not, because /e2e/messages takes one account and a LIST of that account's devices. Ten people with two devices is ten requests, not twenty. Fan-out is per person, only the blob is per device — a bandwidth argument, not a request-count one. And it downgraded relay#28. Round trips are flat against retained history — the cursor query is `id > after` on an index and returns one page whatever is behind it. What is not flat is bytes, and the client decides: a poll with no cursor asks from zero and returns 21 KB of retained envelopes against 381 bytes for the one that is new. Cold storage is an argument about disk, not about the hot path. Two faults in the harness itself, both caught by disbelieving a number: - `app.fluent.history` records nothing a route does. Handlers use `req.db`, which reads the history flag from the REQUEST's storage, fresh per request. Every flow measured zero queries and every budget passed vacuously. A cost harness that reports zero cost is worse than none — it answers with a wrong number. Counted through a middleware that starts history per request now. - The history test compared a cold relay against a warm one and got ELEVEN round trips cold against TEN warm. Storage growth cannot make polls cheaper. The breakdown named it: the presence heartbeat writes once per account per online window, so the first sample paid for a write the second had already made. Both samples are now taken warm. 631 relay tests in 56 suites green, core paths 7/7. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
paulhitt commented 2026-09-04 15:27:58 +00:00 (Migrated from gitlab.com)

assigned to @paulhitt

assigned to @paulhitt
paulhitt commented 2026-09-04 20:40:52 +00:00 (Migrated from gitlab.com)

deleted the feat/prove-two-relay-instances branch. This merge request now targets the main branch

deleted the `feat/prove-two-relay-instances` branch. This merge request now targets the `main` branch
paulhitt commented 2026-09-04 20:40:55 +00:00 (Migrated from gitlab.com)

mentioned in commit 51d386c3a8

mentioned in commit 51d386c3a84ade3c97ec654c5fceada467f8b847
paulhitt (Migrated from gitlab.com) merged commit 51d386c3a8 into main 2026-09-04 20:40:55 +00:00
paulhitt commented 2026-09-09 18:59:10 +00:00 (Migrated from gitlab.com)

changed the description

changed the description
Sign in to join this conversation.