Skip to content

perf(mobile): subscribe to presence only for people on screen - #9444

Merged
brow merged 4 commits into
mainfrom
tomb/mobile-presence-tracked-only
Nov 13, 2026
Merged

brow merged 4 commits into
mainfrom
tomb/mobile-presence-tracked-only

Conversation

@brow

@brow brow commented Nov 13, 2026 •

Copy link
Copy Markdown
Collaborator

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. 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: 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.

The presence cache subscribed to every kind:20001 heartbeat in the
community and dropped the ones it wasn't tracking. On Block's Builderlab
that is about 27 events a second, roughly 3 MB every 5 minutes (35 MB an
hour) while the app is open, each one decoded and verified, to show
presence for a few DM partners and open profiles.

Subscribe with `authors` set to the tracked keys instead. Tracking a new
key replaces the subscription with a wider one, opening the new one before
closing the old, and reads the new key's snapshot once the subscription
covers it. With nothing tracked there is no subscription.

Signed-off-by: Tom Brow <[email protected]>
@brow

brow commented Nov 13, 2026

Copy link
Copy Markdown
Collaborator Author

@codex review

@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Nov 13, 2026 •

Copy link
Copy Markdown
Codex Review Summary: ✅ Completed

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-11-13T17:38:03.813594Z 9844c43 Draft marked ready
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 3f789d16b8

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

A person tracked while live gets an immediate snapshot, but a change made
before their subscription opens could be missed; read them again once
covered. A replacement that reports retrying before it opens no longer
replaces the working subscription.

Co-authored-by: studio <dc2fc481343672df31a689840c09299972e704dac592e0da6f18486646a7ee3e@buzz.block.builderlab.xyz>
Signed-off-by: Tom Brow <[email protected]>
@brow

brow commented Nov 13, 2026

Copy link
Copy Markdown
Collaborator Author

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: f91a25da26

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

A replacement opened when subscribeWithStatus returned, which also happens
on a retryable CLOSED or the 500 ms timeout, so it could retire a healthy
stream or blank every dot on a late EOSE. It now stays pending until its own
first ready; a retried, closed or failed replacement is dropped and the
current stream and cache are kept. One replacement opens at a time, and the
keys it newly covers are re-read once it is live. A first subscription that
is still retrying when it opens waits for its ready before reading anyone. The 60-second poll keeps reading
the covered keys while a widening keeps failing, so expired leases are
still corrected.

This supersedes the previous commit's promotion logic, and its tests are
folded into the swap journey.

Signed-off-by: Tom Brow <[email protected]>
Co-authored-by: studio <dc2fc481343672df31a689840c09299972e704dac592e0da6f18486646a7ee3e@buzz.block.builderlab.xyz>
@brow

brow commented Nov 13, 2026

Copy link
Copy Markdown
Collaborator Author

@codex review

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. What shall we delve into next?

Reviewed commit: ba4d6459c6

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

…ement

Keys tracked while the first subscription was backing off got live
coverage only at the next poll; its recovery now widens the subscription
when tracked keys are uncovered. A replacement the relay never answers no
longer holds the opening slot: each poll drops it before widening again,
so a later close of the active stream is recovered at the next poll.

Signed-off-by: Tom Brow <[email protected]>
Co-authored-by: studio <dc2fc481343672df31a689840c09299972e704dac592e0da6f18486646a7ee3e@buzz.block.builderlab.xyz>
@brow

brow commented Nov 13, 2026

Copy link
Copy Markdown
Collaborator Author

@codex review

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. More of your lovely PRs please.

Reviewed commit: 9844c438dd

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@brow
brow marked this pull request as ready for review November 13, 2026 17:36
@brow
brow requested a review from a team as a code owner November 13, 2026 17:36
@brow
brow deployed to codex-review November 13, 2026 17:36 — with GitHub Actions Active
@github-actions github-actions Bot added the codex-security-review-current The posted Codex security review matches its recorded range. label Nov 13, 2026
@github-actions

Copy link
Copy Markdown

🔐 Codex Security Review

Note: This is an automated, security-focused review generated by Codex.
Use it as a supplement to human review; false positives are possible.

Scope

  • Exact PR diff: 642ce4165cc84d67b99f25b05444dbfff456bce7...9844c438dd27181455d9409f844f5fe56bc1c6d1
  • Model: gpt-6.1-sol

💡 Click "edited" above to see earlier reviews for this PR.


Review Summary

Overall Risk: NONE

No concrete security, correctness, or reliability regressions found in the authorized PR range.

Findings

No concrete security, correctness, or reliability findings were identified.

Notes

  • Review used read-only inspection of the diff, surrounding source, callers, tests, and history. Builds and tests were not executed, as requested.

Generated by Codex Security Review |
Requested by: @brow |
Workflow run

@brow
brow enabled auto-merge (squash) November 13, 2026 17:58

@jedwards27 jedwards27 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤖 :bot: Jude’s code review agent — changes recommended (P2). This is a COMMENT review per the repository’s agent-review policy; the finding below is author-actionable, not an approval.

Reviewed: 3c0adc188206bab8551a7af53dcf6bd54c9d0d09..9844c438dd27181455d9409f844f5fe56bc1c6d1 (exact head). Risk: medium, mobile presence/network-resource lifecycle.

Finding — retired people remain in the live presence subscription. track() only adds to _tracked (mobile/lib/features/profile/presence_cache_provider.dart:105–121), and the new live filter uses that entire accumulated set (:151–163,224–227). Lazy DM rows call track as they are built (mobile/lib/features/channels/channels_page/channel_tile.dart:247–253, sections.dart:67–76), while opened profile sheets also track without releasing on close (mobile/lib/features/profile/user_profile_sheet.dart:155–161). Scrolling through DMs or closing profiles therefore keeps receiving presence for people no longer on screen, and every new person issues a replacement REQ containing everyone previously seen. At more than 4,096 authors the relay’s selector index falls back to broader kind routing (crates/buzz-relay/src/subscription.rs:829–867). This erodes the PR’s on-screen/network bound and conflicts with VISION_MOBILE.md’s bounded background/network-work principle. The PR description itself documents that the cache never untracks; the small idle measurement does not establish a bound after sustained browsing.

Author action: retire subjects when their last visible consumer disappears (account for the same person being visible in multiple surfaces), or provide another demonstrably bounded/pruned active-set design. Add a production-seam regression proving a scrolled-off/closed subject leaves the author filter while a still-visible subject retains live updates, plus a large-list bound. Verification owner: author for fix/test; reviewer for revised-head filter/relay recheck. No separate defect was established in the inspected replacement/retry/EOSE path.

Validation at the reviewed head in clean worktrees: git diff --check clean; just mobile-install exit 0; just mobile-check passed (format/analyzer); just mobile-test exit 0 (Flutter 4,806 passed, ~70 skipped; secp256k1 Dart 27 passed). Focused flutter test test/features/profile/presence_cache_provider_test.dart passed 10 tests. Those green tests do not cover retirement. The PR describes automated iPhone 12 mini runs on the final head, including scrolling newly visible DM rows; reviewers did not independently exercise native scrolling/unmount against a real relay. Confidence gap only, author action none on that absence alone; verification owner: reviewer/human for independent device proof if required. Any new head needs delta review and affected gates rerun.

@brow

brow commented Nov 13, 2026

Copy link
Copy Markdown
Collaborator Author

🤖 Thanks. This is the tradeoff the description calls out: the tracked set only grows during a session. It's still bounded by the people this session has actually shown, which is always a subset of the community-wide presence stream that main subscribes to today, so this PR can only reduce live presence traffic, never increase it. Past 4,096 authors the relay falls back to kind routing, which is what main gets for every session now.

Retiring people when their last visible row or sheet goes away (refcounted across surfaces, with a debounced shrink) is a worthwhile next bound. I'll do it as a follow-up PR with the regression tests you describe, so this one can land the large win as reviewed.

@kazemek21 kazemek21 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Kyle reviewed this PR and the Nexus review-tracker notes and approved with no comments.

Posted by Codex (gpt-6.1-sol) on behalf of kylez

@brow
brow merged commit 36fa0fe into main Nov 13, 2026
60 checks passed
@brow
brow deleted the tomb/mobile-presence-tracked-only branch November 13, 2026 21:32

This branch was successfully deployed

1 active deployment
codex-review — 9844c438 Deployed Nov 13, 2026 by brow via Run Codex Security Review #14973
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

codex-security-review-current The posted Codex security review matches its recorded range.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants