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.
This commit is contained in:
Ivan Enderlin
2025-05-13 16:28:23 +02:00
parent 2c8f48fabb
commit 68651aac1f
5 changed files with 28 additions and 15 deletions
+1 -1
View File
@@ -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(
@@ -345,7 +345,7 @@ impl<P: RoomDataProvider, D: Decryptor> TimelineController<P, D> {
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();
+20 -6
View File
@@ -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<TimelineEvent> {
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<TimelineEvent>, 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));
}
@@ -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");
@@ -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
})
});