Skip to content

Name users the cached Zulip user list has lost - #256

Merged
GraemeF merged 2 commits into
mainfrom
worktree-comms-6otw
Oct 2, 2026
Merged

GraemeF merged 2 commits into
mainfrom
worktree-comms-6otw

Conversation

@GraemeF

@GraemeF GraemeF commented Oct 1, 2026 •

Copy link
Copy Markdown
Contributor

Zulip 12.3 serves GET /users from a realm-wide cache. Creating a user deletes that cache inside the creating transaction, before it commits. A GET /users that lands in that gap caches the list without the new user, and the user stays missing until the next change to any user. The source is get_realm_user_dicts in zerver/models/users.py and flush_user_profile in zerver/lib/cache.py.

A newly minted pinned bot that hits this gap is invisible to commy. resolve returns nothing for it, @**name** is refused, and its posts arrive labelled human. Mentions of it would also be dropped on the way in, so it would not wake on its own mention.

When the cached list lacks a user id it needs, the adapter now reads GET /users?user_ids=…, which queries the database. To place a name, resolve and the outbound mention check first ask POST /messages/render to resolve @**name**, which also queries the database. Both reads happen only on a miss, and a history or catch-up page asks once for all of its missing ids.

What a reviewer should know:

  • list_agents and list_humans still read the cached list, because they need all of it.
  • Cross-realm system bots such as Notification Bot are in neither list. Each message they send now costs one extra read that comes back empty, and they are still labelled human. They send only a small fraction of messages.
  • The real fix belongs in Zulip, which should delete the cache when the transaction commits. No upstream issue has been filed yet.

Zulip 12.3 serves GET /users from a realm-wide cache. Creating a user
deletes that cache before the creating transaction commits, so a
GET /users landing in that gap caches the list without the new user. It
stays missing until the next change to any user. A pinned bot minted
into that gap could not be resolved or mentioned, its posts were
labelled human, and mentions of it were dropped on the way in.

When the cached list lacks a user id it needs, the adapter now reads
GET /users with user_ids, which queries the database. For a name, it
first asks Zulip's renderer to resolve @**name**, which also queries the
database. Both reads happen only on a miss. list_agents and list_humans
still read the cached list.
@GraemeF
GraemeF marked this pull request as ready for review October 2, 2026 05:56
@GraemeF
GraemeF merged commit 6ebf84e into main Oct 2, 2026
2 checks passed
@GraemeF
GraemeF deleted the worktree-comms-6otw branch October 2, 2026 06:19
GraemeF added a commit that referenced this pull request Oct 2, 2026
Patch release, 0.24.2 → 0.24.3. It ships the fix that finds a newly
created user Zulip's cached user list has missed (#256), the fix that
stops an unsubscribe from one topic dropping the seat's other topics in
that channel (#257), and the PyJWT security bump in the Hermes lockfile
(#253).

Merge #256 first. The notes in `RELEASE-NOTES/0.24.3.md` become the
GitHub Release body. Merging fires the tag and npm publish.
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.

1 participant