From 68651aac1f2906d295fd801735dbcdc516d3d539 Mon Sep 17 00:00:00 2001 From: Ivan Enderlin Date: Tue, 13 May 2025 16:28:23 +0200 Subject: [PATCH] feat(sdk): Add `RoomEventCache::events` to avoid `::subscribe`. `RoomEventCache::subscribe` returns the set of events + the `RoomEventCacheListener`. However, creating this listener isn't cheap, especially dropping it. That's why this patch creates `RoomEventCache::events` to replace `subscribe` when the listener is not necessary. --- crates/matrix-sdk-ui/src/timeline/builder.rs | 2 +- .../src/timeline/controller/mod.rs | 2 +- crates/matrix-sdk/src/event_cache/room/mod.rs | 26 ++++++++++++++----- .../tests/integration/event_cache.rs | 10 +++---- .../src/widgets/room_view/details/events.rs | 3 +-- 5 files changed, 28 insertions(+), 15 deletions(-) diff --git a/crates/matrix-sdk-ui/src/timeline/builder.rs b/crates/matrix-sdk-ui/src/timeline/builder.rs index 1a78dce4a..60e300cea 100644 --- a/crates/matrix-sdk-ui/src/timeline/builder.rs +++ b/crates/matrix-sdk-ui/src/timeline/builder.rs @@ -351,7 +351,7 @@ async fn room_event_cache_updates_task( // The updates might have lagged, but the room event cache might have // events, so retrieve them and add them back again to the timeline, // after clearing it. - let (initial_events, _stream) = room_event_cache.subscribe().await; + let initial_events = room_event_cache.events().await; timeline_controller .replace_with_initial_remote_events( diff --git a/crates/matrix-sdk-ui/src/timeline/controller/mod.rs b/crates/matrix-sdk-ui/src/timeline/controller/mod.rs index 882d7bae4..abd3cde88 100644 --- a/crates/matrix-sdk-ui/src/timeline/controller/mod.rs +++ b/crates/matrix-sdk-ui/src/timeline/controller/mod.rs @@ -345,7 +345,7 @@ impl TimelineController { match &*focus_guard { TimelineFocusData::Live => { // Retrieve the cached events, and add them to the timeline. - let (events, _stream) = room_event_cache.subscribe().await; + let events = room_event_cache.events().await; let has_events = !events.is_empty(); diff --git a/crates/matrix-sdk/src/event_cache/room/mod.rs b/crates/matrix-sdk/src/event_cache/room/mod.rs index b247a8b7d..81c8e7197 100644 --- a/crates/matrix-sdk/src/event_cache/room/mod.rs +++ b/crates/matrix-sdk/src/event_cache/room/mod.rs @@ -158,8 +158,22 @@ impl RoomEventCache { } } + /// Read all current events. + /// + /// Use [`RoomEventCache::subscribe`] to get all current events, plus a + /// listener/subscriber. + pub async fn events(&self) -> Vec { + let state = self.inner.state.read().await; + + state.events().events().map(|(_position, item)| item.clone()).collect() + } + /// Subscribe to this room updates, after getting the initial list of /// events. + /// + /// Use [`RoomEventCache::events`] to get all current events without the + /// listener/subscriber. Creating, and especially dropping, a + /// [`RoomEventCacheListener`] isn't free. pub async fn subscribe(&self) -> (Vec, RoomEventCacheListener) { let state = self.inner.state.read().await; let events = state.events().events().map(|(_position, item)| item.clone()).collect(); @@ -1637,7 +1651,7 @@ mod tests { // The in-memory linked chunk keeps the bundled relation. { - let (events, _) = room_event_cache.subscribe().await; + let events = room_event_cache.events().await; assert_eq!(events.len(), 1); @@ -1803,7 +1817,7 @@ mod tests { assert!(room_event_cache.event(event_id1).await.is_some()); // But their presence in a linked chunk is forgotten. - let (items, _) = room_event_cache.subscribe().await; + let items = room_event_cache.events().await; assert!(items.is_empty()); // The event cache store too. @@ -1935,7 +1949,7 @@ mod tests { // when subscribing, to check that the items correspond to their new // positions. The duplicated item is removed (so it's not the first // element anymore), and it's added to the back of the list. - let (items, _stream) = room_event_cache.subscribe().await; + let items = room_event_cache.events().await; assert_eq!(items.len(), 2); assert_eq!(items[0].event_id().unwrap(), event_id1); assert_eq!(items[1].event_id().unwrap(), event_id2); @@ -1995,7 +2009,7 @@ mod tests { let (room_event_cache, _drop_handles) = room.event_cache().await.unwrap(); - let (items, _stream) = room_event_cache.subscribe().await; + let items = room_event_cache.events().await; // Because the persisted content was invalid, the room store is reset: there are // no events in the cache. @@ -2262,7 +2276,7 @@ mod tests { assert!(stream.is_empty()); // When reading the events, we do get only the last one. - let (events, _) = room_event_cache.subscribe().await; + let events = room_event_cache.events().await; assert_eq!(events.len(), 1); assert_eq!(events[0].event_id().as_deref(), Some(evid2)); @@ -2386,7 +2400,7 @@ mod tests { } // Getting the events will only give us the latest chunk. - let (events3, _stream2) = room_event_cache.subscribe().await; + let events3 = room_event_cache.events().await; assert_eq!(events3.len(), 1); assert_eq!(events3[0].event_id().as_deref(), Some(evid2)); } diff --git a/crates/matrix-sdk/tests/integration/event_cache.rs b/crates/matrix-sdk/tests/integration/event_cache.rs index 7831ebfe0..e198ba46b 100644 --- a/crates/matrix-sdk/tests/integration/event_cache.rs +++ b/crates/matrix-sdk/tests/integration/event_cache.rs @@ -203,7 +203,7 @@ async fn test_ignored_unignored() { { let room = client.get_room(other_room_id).unwrap(); let (room_event_cache, _drop_handles) = room.event_cache().await.unwrap(); - let (events, _) = room_event_cache.subscribe().await; + let events = room_event_cache.events().await; assert!(events.is_empty()); } @@ -984,7 +984,7 @@ async fn test_backpaginate_with_no_initial_events() { pagination.run_backwards_once(20).await.unwrap(); // The linked chunk should contain the events in the correct order. - let (events, _stream) = room_event_cache.subscribe().await; + let events = room_event_cache.events().await; assert_eq!(events.len(), 3, "{events:?}"); assert_event_matches_msg(&events[0], "oh well"); @@ -1047,7 +1047,7 @@ async fn test_backpaginate_replace_empty_gap() { pagination.run_backwards_once(20).await.unwrap(); // The linked chunk should contain the events in the correct order. - let (events, _stream) = room_event_cache.subscribe().await; + let events = room_event_cache.events().await; assert_event_matches_msg(&events[0], "hello"); assert_event_matches_msg(&events[1], "world"); @@ -1130,7 +1130,7 @@ async fn test_no_gap_stored_after_deduplicated_sync() { assert!(stream.is_empty()); { - let (events, _) = room_event_cache.subscribe().await; + let events = room_event_cache.events().await; assert_event_matches_msg(&events[0], "hello"); assert_event_matches_msg(&events[1], "world"); assert_event_matches_msg(&events[2], "sup"); @@ -1149,7 +1149,7 @@ async fn test_no_gap_stored_after_deduplicated_sync() { assert!(outcome.reached_start); { - let (events, _) = room_event_cache.subscribe().await; + let events = room_event_cache.events().await; assert_event_matches_msg(&events[0], "hello"); assert_event_matches_msg(&events[1], "world"); assert_event_matches_msg(&events[2], "sup"); diff --git a/labs/multiverse/src/widgets/room_view/details/events.rs b/labs/multiverse/src/widgets/room_view/details/events.rs index 3b709e002..33ce9f7e0 100644 --- a/labs/multiverse/src/widgets/room_view/details/events.rs +++ b/labs/multiverse/src/widgets/room_view/details/events.rs @@ -28,8 +28,7 @@ impl Widget for &mut EventsView<'_> { let events = tokio::task::block_in_place(|| { Handle::current().block_on(async { let (room_event_cache, _drop_handles) = room.event_cache().await.unwrap(); - let (events, _) = room_event_cache.subscribe().await; - events + room_event_cache.events().await }) });