Files
buzz/mobile/lib/features/profile
Tom Brow 36fa0fe318 perf(mobile): subscribe to presence only for people on screen (#9444)
The mobile app subscribed to every presence event in the community,
though it only shows presence for people on screen. It now subscribes
only to the people it tracks.

### Validation

**Impact:** on an iPhone 12 mini signed in to Block's Builderlab, 5
minutes idle on Messages:

| | Presence downloaded | All WebSocket traffic |
| --- | --- | --- |
| Before | 2.9 MB (8153 events) | 3.1 MB |
| After | 3.6 KB (10 events) | 66 KB |

That's about 35 MB per hour saved at Builderlab's presence rate (about
27 events per second). Measured on an instrumented profile build of the
first revision,
[3f789d1](3f789d16b8).
The later swap fix doesn't change what is subscribed.

**Device runs (automated, profile builds on the 12 mini):**
- First revision: cold start, opening and leaving the home list, a
Control Center pull, and a resume after 60 s in the background. Presence
dots kept updating for people on screen.
- Final head [9844c43](https://github.com/block/buzz/commit/9844c438dd):
cold start, opening two channels, a 60 s background resume, and
scrolling the home list through the DM section. Every DM row showed its
presence dot (online and away), including rows newly scrolled into view.
Opening a DM and its details sheet worked as before.

**Added tests:**
- Swapping subscriptions: a late replacement is promoted without
clearing anyone's dot and re-reads the newly covered people; a retryable
close or an outright failure keeps the current stream; a second track
while one is opening doesn't open a third subscription. Each guard was
removed in turn and the test failed each time.
- A widening that keeps failing is retried each poll, and expired leases
are still corrected.
- Replacements the relay never answers are dropped and retried, and
after the current stream also closes, the next poll covers and reads
everyone.
- A first subscription still retrying when it opens waits for its ready,
then reads everyone and widens.

**Updated test:** the filter test now expects no subscription while
nobody is tracked, then `authors: [alice]`, then `[alice, bob]`. The
full mobile test suite passes.

### How it works

- The live subscription filters by `authors`: everyone shown with
presence this session. The cache never untracks; it resets on community
or account change. Nothing is subscribed while nobody is tracked.
- When someone new is tracked, a replacement subscription opens for the
wider set. The current one keeps delivering until the replacement's
first EOSE, then it's swapped in and the old one closed. Newly covered
people get a fresh snapshot at that point.
- If the replacement fails or closes first, it's dropped and nothing on
screen changes. The 60-second poll retries the widening and keeps
reading the people already covered.
- One replacement opens at a time; people tracked meanwhile join the
next round.

### Regressions

- A person tracked mid-session gets live presence after one extra
subscription round trip (up to about a second on a slow link). People
already on screen keep live presence throughout the swap.
- Each widening resends the full tracked list (about 67 bytes per
person), so a few hundred people cost tens of KB per widening, against
35 MB per hour saved. The relay indexes `authors` filters up to 4096
authors, so this is cheap for it.

---------

Signed-off-by: Tom Brow <tomb@squareup.com>
Co-authored-by: Tom Brow <tomb@squareup.com>
Co-authored-by: studio <dc2fc481343672df31a689840c09299972e704dac592e0da6f18486646a7ee3e@buzz.block.builderlab.xyz>
2026-11-13 13:32:53 -08:00
..