project-robius / project-robius/robrix

User profile cache can't notify: single-drain queue means only one widget ever sees an update

Open
#1,023 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
Rust
Stars
487
Forks
68
Avg merge
1h 22m
Merged PRs (30d)
54

Description

Problem

process_user_profile_updates() drains a single global queue and returns ():

// src/profile/user_profile_cache.rs:65
static PENDING_USER_PROFILE_UPDATES: SegQueue<UserProfileUpdate> = SegQueue::new();

// src/profile/user_profile_cache.rs:205
pub fn process_user_profile_updates(_cx: &mut Cx) {
    USER_PROFILE_CACHE.with_borrow_mut(|cache| {
        while let Some(update) = PENDING_USER_PROFILE_UPDATES.pop() {
            update.apply_to_cache(cache);
        }
    });
}

Three widgets pump that queue today:

  • src/home/navigation_tab_bar.rs:284
  • src/home/room_screen.rs:1265
  • src/profile/user_profile.rs:431

Because the queue is drained to empty and the function reports nothing, whichever widget pumps
first consumes every update, and no other widget can learn that anything changed.
That is fine
for populating a shared cache, which is what it was built for. It does not work as a
notification: a widget that wants to react to a profile change either has to win the race, or
re-read the cache on every draw and hope.

Why it matters

Any feature that needs "this user's name or avatar just changed, refresh what I'm showing" cannot
be built on this. Concretely, it is why the invite screen resolves the inviter's profile exactly
once instead of staying current: there is no way for it to hear about a later update, so the
alternative would have been a cache lookup on every draw.

It is also a silent trap rather than a loud one. Adding a fourth pumper today works right up until
another widget happens to pump first, and then the new one just quietly stops seeing updates.

Suggested fix

Drain in one place and tell everyone what changed:

  • Have process_user_profile_updates() return the set of updated user IDs, or dispatch an action
    carrying them.
  • UserProfileUpdate::user_id() already exists for exactly this and is currently
    #[allow(unused)] (src/profile/user_profile_cache.rs:92).
  • Widgets then match on the IDs they care about instead of racing to drain.
Risk

The three current pumpers each do widget-specific work immediately after pumping, so centralising
the drain changes their ordering. That is the part that needs care, not the queue change itself.

The same shape exists in avatar_cache::process_avatar_updates(), which is pumped from six
places. It gets away with it today only because it calls cx.redraw_all() when anything landed,
so consumers repaint and re-read rather than needing to be told. Worth deciding whether both
should use one mechanism.

Contributor guide

No contributing guide indexed for this repository

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with src/profile/user_profile_cache.rs, especially process_user_profile_updates(), UserProfileUpdate::user_id(), and the three current pumpers in navigation_tab_bar.rs, room_screen.rs, and user_profile.rs. Trace the widget-specific work after pumping, then inspect avatar_cache::process_avatar_updates() for comparison. Done means updates are drained centrally, interested widgets can identify affected users, and existing profile refresh behavior remains correct.

Written by the indexing model from the issue text.

Assessment

Tech stack
rust
Domain
frontend
Issue type
Feature
Difficulty
4/5
Estimated time
3-5 days
Activity status
Active
Clarity
Mostly clear
Newbie friendliness
48/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.