Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

event cache/timeline: reuse the Paginator when running back-paginations #3373

Merged
merged 8 commits into from
May 16, 2024

Conversation

bnjbvr
Copy link
Member

@bnjbvr bnjbvr commented May 1, 2024

First commit

Sorry, I got a bit ahead of myself and did multiple interesting changes in that large commit:

  • In contrary to the pagination happening for focused-mode timelines, the back-pagination happening from the event cache doesn't require focusing on the target event first (which causes the /context query). So the Paginator must now be instantiable from that state, with a previous-batch token that's passed by the caller. This justified introducing Paginator::set_idle_state(prev_batch_token).
  • Then, since it's possible to give a prev_batch_token set to None to that function (meaning, we don't have any prev-batch-token from sync for that room), we now need to distinguish prev_batch_token is set to None by choice from prev_batch_token is set to None because we hit the end of the timeline. This justified the introduction of paginator::PaginationToken, which reflects those three states (the third one being Some(token)).
  • From that, it was possible to reuse the Paginator for the room back-paginations. The RoomEventCache now also takes care of the pagination token, instead of being handed one, removing some failure modes (e.g. passing an incorrect back-pagination token internally). If it didn't get a proper token from a sync, it'll be in charge of waiting for it (for 3 seconds), so the timeline doesn't have to do that itself anymore.
  • The timeline's back-pagination subscriber is now just the one from the room's paginator status, which is nice, because it means it's now shared among all the timeline instances too. (What might be slightly less nice is that it may toggle between Paginating and Idle, if the caller in timeline calls the function multiple times. It seemed OK to me, especially since the timeline will silent multiple requests to back-paginate concurrently.)

Other commits

The other commits are slightly more atomic and should be easier to digest one by one, by looking at the commit messages.

TL;DR

This makes it possible for the event cache to back-paginate in the background, even when no timelines are shown, which should be handy for some use cases (esp. around read-receipts).

Fixes #3355.

@bnjbvr bnjbvr requested a review from a team as a code owner May 1, 2024 16:08
@bnjbvr bnjbvr requested review from andybalaam and Hywan and removed request for a team and andybalaam May 1, 2024 16:08
bnjbvr added 5 commits May 2, 2024 11:07
…ound

Only code motion. No changes in functionality.
…agination that failed under our feet

When a timeline reset happens while we're back-paginating, the event
cache method to run back pagination would return an success result
indicating that the pagination token disappeared. After thinking about
it, it's not the best API in the world; ideally, the backpagination
mechanism would restart automatically.

Now, this was handled in the timeline before, and the reason it was
handled there was because it was possible to back-paginate and ask for a
certain number of events. I've removed that feature, so that
back-pagination on a live timeline matches the capabilities of a
focused-timeline back-pagination: one can only ask for a given number of
*events*, not timeline items.

As a matter of fact, this simplifies the code a lot by removing many
data structures, that were also exposed (and unused, since recent
changes) in the FFI layer.
@bnjbvr bnjbvr force-pushed the bnjbvr/reuse-paginator-in-event-cache branch from 20404fb to bbd20b0 Compare May 2, 2024 09:16
Copy link

codecov bot commented May 2, 2024

Codecov Report

Attention: Patch coverage is 95.08197% with 6 lines in your changes are missing coverage. Please review.

Project coverage is 82.94%. Comparing base (83427b3) to head (3002389).
Report is 1 commits behind head on main.

Files Patch % Lines
crates/matrix-sdk/src/event_cache/pagination.rs 91.37% 5 Missing ⚠️
crates/matrix-sdk-ui/src/timeline/pagination.rs 95.00% 1 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #3373      +/-   ##
==========================================
- Coverage   82.96%   82.94%   -0.03%     
==========================================
  Files         245      246       +1     
  Lines       24901    24884      -17     
==========================================
- Hits        20660    20640      -20     
- Misses       4241     4244       +3     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

Copy link
Member

@Hywan Hywan left a comment

Choose a reason for hiding this comment

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

Excellent improvements. It really improves the code, thank you!

I'm a bit skeptical about the last commit that removes the custom and until_num_items API for the pagination, see my comment.

bindings/matrix-sdk-ffi/src/timeline/mod.rs Outdated Show resolved Hide resolved
pub fn back_pagination_status(&self) -> Subscriber<PaginationStatus> {
self.back_pagination_status.subscribe()
///
/// Note: this may send multiple Paginating/Idle sequences during a single
Copy link
Member

Choose a reason for hiding this comment

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

Why? Do we want to dedup them? Maybe with async_rx::StreamExt::dedup if we can provide a Stream.

Copy link
Member Author

Choose a reason for hiding this comment

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

It's an implementation detail of eyeball that we're running into. If you have an emitter E and an observer O. Say E emits very quickly e1, e2, e3; then if O doesn't react fast enough, it may only see e1, handle it, and then miss e2, and only see e3.

Before this PR, the exposed pagination status was controlled at the timeline level, and the timeline would make sure to reset it only when a full pagination loop is over, after we obtained e.g. N events.

After this PR, the pagination status is that of the underlying paginator. The timeline may continue to paginate after the paginator has run one pagination query. In that case, the underlying status of the paginator will be:

  • running (because it's busy doing the network request)
  • idle (because it's done processing the response)
  • (the timeline sees it hasn't received as many events as it wanted, so it retriggers the paginator) the paginator quickly re-runs into the running state

So the quick running -> idle -> running transition might make it so that an observer could see running -> running, because they didn't react fast enough to see the idle state in the middle.

I've added a dedup() call to address this, thanks.

Now, if the caller is actually fast enough, they may see a few quick transitions from running -> idle -> running; I wonder if that may cause some UI flickering (e.g. the spinner quickly disappears and reappears). At this point, both EX apps doesn't use the back-pagination status anymore, so it's a theoretical issue.

pagination_token_notifier: Default::default(),
}
}

async fn clear(&self, room_events: &mut RwLockWriteGuard<'_, RoomEvents>) {
Copy link
Member

Choose a reason for hiding this comment

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

Why passing the room_events? What's the benefits of this new approach?

Copy link
Member Author

Choose a reason for hiding this comment

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

Initially, I took the room_events lock within this function; but when this is called in replace_all_events_by, we're already holding onto the lock, and we really want to keep it throughout the full function's body, to avoid a bad race when two callers racily call replace_all_events_by.

crates/matrix-sdk/src/event_cache/mod.rs Outdated Show resolved Hide resolved
crates/matrix-sdk/src/event_cache/paginator.rs Outdated Show resolved Hide resolved
@bnjbvr bnjbvr requested a review from Hywan May 13, 2024 16:17
@bnjbvr
Copy link
Member Author

bnjbvr commented May 13, 2024

This is ready for another round of review, please!

(Note: this will need to be squashed, as the commit history has gotten a bit messy, because of large conflicts with linked chunks)

Copy link
Member

@Hywan Hywan left a comment

Choose a reason for hiding this comment

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

Nothing to add, good job, thanks for addressing the feedback!

@bnjbvr bnjbvr merged commit c8f6fe4 into main May 16, 2024
35 checks passed
@bnjbvr bnjbvr deleted the bnjbvr/reuse-paginator-in-event-cache branch May 16, 2024 08:22
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.

fix(ui): Pagination clean up in the timeline
2 participants