Skip to content

fix(profiles): evict the least recently requested profile - #2255

Merged
loganj merged 1 commit into
mainfrom
larry/profile-cache-lru
Nov 3, 2026
Merged

loganj merged 1 commit into
mainfrom
larry/profile-cache-lru

Conversation

@loganj

@loganj loganj commented Nov 3, 2026

Copy link
Copy Markdown
Contributor

🤖

Summary

  • In very large channels (for example one with about 1,700 members), people's names sometimes changed to a short hex key (like c5c3190fab) while they were still on screen.
  • The app keeps at most 1,024 people's profiles in memory. When it needs room, it should remove the profile used least recently. Instead it removed the profile it had stored first, because looking up a profile that was already loaded did not count as a use. So a person still on screen could be removed before people nobody had looked at.
  • Now each request for a profile that is already loaded counts as a use, so the app removes the profiles nobody has asked for recently. The separate cache for verified names already worked this way.

Details

  • ensure() in src/features/relay/profile-directory.ts now calls events.get(id) for each requested id, which moves a loaded entry to the newest position in the ByteLru. Reads that scan every profile (publish(), event(), and the saved-profile checks in store.ts) still use peek(), so they do not count as use.
  • The reconnect refresh passes ids in their current cache order, so it does not change the order.
  • The new test fills the cache to 1,024, requests the oldest profile, adds one more, and checks that the requested profile stays and the next-oldest one is removed. On main it fails with expected undefined to be 'P0'.

Not in this PR

  • This makes removal follow requests, not what is on screen. Background prefetches still count as requests. The largest is the mention menu, which requests up to 1,024 members of the current channel the first time someone types @, so in a channel that big it can still push out names on screen. A follow-up will change the mention menu to load only the names it shows, and will consider sizing the cache to the current channel.

🤖 Generated with Claude Code

The profile directory stored kind 0 events in a ByteLru but only ever
read them with peek(), and ensure() skipped loaded profiles without
touching them. Recency therefore moved only when a newer kind 0 arrived,
so the 1,024-entry budget evicted the profile stored longest ago, even
one still on screen, and its row fell back to a hex name.

A cache hit in ensure() now marks the profile recently used, matching
the verified-name directory, which already evicts by least recent ensure.

Co-authored-by: Larry <627498bd4bd1f281a16431e3c6cce3b5c25b6692798c78672298aefbf2f8f8b5@buzz.block.builderlab.xyz>
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Signed-off-by: Logan Johnson <[email protected]>
@loganj
loganj marked this pull request as ready for review November 3, 2026 15:42
@loganj
loganj requested a review from a team as a code owner November 3, 2026 15:42

@kalvinnchau kalvinnchau 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.

🤖 Approved at cc671e2. No actionable findings. Requested-profile recency preserves cache bounds and lifecycle behavior; the regression test fails on the base and passes here. Chromium/WebKit app checks covered eviction, focus, reload and reconnect. Native/real-relay behavior, durable archive persistence and byte-triggered eviction remain unverified.

@loganj
loganj merged commit 8ecdfbc into main Nov 3, 2026
77 of 79 checks passed
@loganj
loganj deleted the larry/profile-cache-lru branch November 3, 2026 16:16
dr0uleau added a commit that referenced this pull request Nov 3, 2026
…xecution-support-for-native-buzz-33ea2bea

* origin/main: (74 commits)
  feat(host): harden byte lifecycle and add protected image upload (#2199)
  Updated onboarding (#2183)
  perf(relay): read each signed roster once in large channels (#2244)
  AIDA-2346: Keep spoilers hidden in reminder previews (#2261)
  fix: AIDA-2371 Keep sidebar selection pill aligned during resize (#2182)
  fix(relay): skip background rereads of profiles a read found absent (#2147)
  fix: prevent startup logo selection (#1823)
  fix(agents): send sign-in evidence with agent Git credentials (#2240)
  perf(mentions): stop scanning every member on each mention keystroke (#2246)
  fix(attachments): send image dimensions so previews don't shift (#2262)
  feat(shortcuts): add Slack-style hotkeys (#2170)
  perf(plugins): share host request clients across calls (#2243)
  fix(identity): keep federated sessions and agent grants in one keychain item (#2225)
  fix(profiles): evict the least recently requested profile (#2255)
  fix(unread): repair unread from the recovery tick only every 15 minutes (#2252)
  AIDA-2374: Use readable labels in chat titles (#2185)
  perf(profiles): keep verified names checked through a planned renewal (#2234)
  feat(plugins): let a plugin ask Buzz to review an install (#2059)
  fix(identity): sign FI builds in at first run through the native session (FOUNDATION: App.tsx, services.ts) (#2249)
  Place profile verification beside the verified name (#2224)
  ...

Signed-off-by: Daniel Rouleau <[email protected]>
dr0uleau added a commit that referenced this pull request Nov 3, 2026
* commit 'c8e882165': (50 commits)
  Scope execution targets by harness session
  Read durable execution state before admission
  feat(host): harden byte lifecycle and add protected image upload (#2199)
  Updated onboarding (#2183)
  perf(relay): read each signed roster once in large channels (#2244)
  AIDA-2346: Keep spoilers hidden in reminder previews (#2261)
  fix: AIDA-2371 Keep sidebar selection pill aligned during resize (#2182)
  fix(relay): skip background rereads of profiles a read found absent (#2147)
  fix: prevent startup logo selection (#1823)
  fix(agents): send sign-in evidence with agent Git credentials (#2240)
  perf(mentions): stop scanning every member on each mention keystroke (#2246)
  fix(attachments): send image dimensions so previews don't shift (#2262)
  feat(shortcuts): add Slack-style hotkeys (#2170)
  perf(plugins): share host request clients across calls (#2243)
  fix(identity): keep federated sessions and agent grants in one keychain item (#2225)
  fix(profiles): evict the least recently requested profile (#2255)
  fix(unread): repair unread from the recovery tick only every 15 minutes (#2252)
  AIDA-2374: Use readable labels in chat titles (#2185)
  perf(profiles): keep verified names checked through a planned renewal (#2234)
  feat(plugins): let a plugin ask Buzz to review an install (#2059)
  ...

Signed-off-by: Daniel Rouleau <[email protected]>
Co-authored-by: Codex <[email protected]>
arividar pushed a commit to minniai/minni-buzz-app that referenced this pull request Nov 4, 2026
🤖

## Summary
- In very large channels (for example one with about 1,700 members),
people's names sometimes changed to a short hex key (like `c5c3190fab`)
while they were still on screen.
- The app keeps at most 1,024 people's profiles in memory. When it needs
room, it should remove the profile used least recently. Instead it
removed the profile it had stored first, because looking up a profile
that was already loaded did not count as a use. So a person still on
screen could be removed before people nobody had looked at.
- Now each request for a profile that is already loaded counts as a use,
so the app removes the profiles nobody has asked for recently. The
separate cache for verified names already worked this way.

## Details
- `ensure()` in `src/features/relay/profile-directory.ts` now calls
`events.get(id)` for each requested id, which moves a loaded entry to
the newest position in the `ByteLru`. Reads that scan every profile
(`publish()`, `event()`, and the saved-profile checks in `store.ts`)
still use `peek()`, so they do not count as use.
- The reconnect refresh passes ids in their current cache order, so it
does not change the order.
- The new test fills the cache to 1,024, requests the oldest profile,
adds one more, and checks that the requested profile stays and the
next-oldest one is removed. On `main` it fails with `expected undefined
to be 'P0'`.

## Not in this PR
- This makes removal follow requests, not what is on screen. Background
prefetches still count as requests. The largest is the mention menu,
which requests up to 1,024 members of the current channel the first time
someone types `@`, so in a channel that big it can still push out names
on screen. A follow-up will change the mention menu to load only the
names it shows, and will consider sizing the cache to the current
channel.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Signed-off-by: Logan Johnson <[email protected]>
Co-authored-by: Larry <627498bd4bd1f281a16431e3c6cce3b5c25b6692798c78672298aefbf2f8f8b5@buzz.block.builderlab.xyz>
Co-authored-by: Claude Opus 5.5 <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants