atagen

matrix-rust-sdk

public
Star0
Watch0

Commit 33da5ef143fe

tarball

atagen <boss@atagen.co>2026-10-28 05:03 UTC

3 files changed+45-1
event-cache: keep the last chunk from memory when a limited sync unloads the rest

Right after a limited sync with a gap persists its events, `shrink_to_last_chunk` read the newest chunk straight back from the store, though memory had just written it. On wasm that is one more IndexedDB round trip per room seen for the first time, and a catch-up on a 399 room account paid it 242 times. The reload path still reads the store, since another process may have changed it.
Modified file: crates/matrix-sdk-common/src/linked_chunk/lazy_loader.rs+22 additions
@@ -208,6 +208,28 @@ fn emit_new_first_chunk_updates<const CAP: usize, Item, Gap>(
208208 }
209209 }
210210
211+/// Unload every chunk but the last, as [`replace_with`] does with that last chunk
212+/// read back from a store that matches memory, without the read.
213+pub fn unload_all_but_last<const CAP: usize, Item, Gap>(
214+ linked_chunk: &mut LinkedChunk<CAP, Item, Gap>,
215+) -> Result<(), LazyLoaderError>
216+where
217+ Item: Clone,
218+ Gap: Clone,
219+{
220+ let last = linked_chunk.links.latest_chunk();
221+ let chunk = RawChunk {
222+ content: last.content().clone(),
223+ previous: last.previous().map(|previous| previous.identifier()).or(last.lazy_previous()),
224+ identifier: last.identifier(),
225+ next: None,
226+ };
227+ let generator = ChunkIdentifierGenerator::new_from_previous_chunk_identifier(
228+ linked_chunk.chunk_identifier_generator.current(),
229+ );
230+ replace_with(linked_chunk, Some(chunk), generator)
231+}
232+
211233 /// Replace the items with the given last chunk of items and generator.
212234 ///
213235 /// This clears all the chunks in memory before resetting to the new chunk,
Modified file: crates/matrix-sdk/src/event_cache/caches/event_linked_chunk.rs+7 additions
@@ -633,6 +633,13 @@ impl EventLinkedChunk {
633633 })
634634 }
635635
636+ /// Unload every chunk but the last, keeping the last as memory holds it.
637+ pub(in super::super) fn unload_all_but_last(&mut self) -> Result<(), LazyLoaderError> {
638+ self.inhibit_updates_to_ordering_tracker(|this| {
639+ lazy_loader::unload_all_but_last(&mut this.chunks)
640+ })
641+ }
642+
636643 /// Prepends a lazily-loaded chunk at the beginning of the linked chunk.
637644 #[instrument(err, skip_all, fields(sentry = true))]
638645 pub(in super::super) fn insert_new_chunk_as_first(
Modified file: crates/matrix-sdk/src/event_cache/caches/room/state.rs+16 additions−1 deletions
@@ -661,6 +661,21 @@ impl<'a> RoomEventCacheStateLockWriteGuard<'a> {
661661 self.state.find_event_with_relations(event_id, filters, &self.store).await
662662 }
663663
664+ /// [`Self::shrink_to_last_chunk`] right after memory was persisted, so the last
665+ /// chunk is taken from memory rather than read back from the store.
666+ async fn shrink_to_last_chunk_in_memory(&mut self) -> Result<(), EventCacheError> {
667+ debug!("unloading the linked chunk, and keeping its last chunk from memory");
668+ if let Err(err) = self.state.room_linked_chunk.unload_all_but_last() {
669+ error!("error when unloading the linked chunk: {err}");
670+ return self.shrink_to_last_chunk().await;
671+ }
672+ self.state
673+ .pagination_status
674+ .set(SharedPaginationStatus::Idle { hit_timeline_start: false });
675+ let _ = self.state.room_linked_chunk.store_updates().take();
676+ Ok(())
677+ }
678+
664679 /// If storage is enabled, unload all the chunks, then reloads only the
665680 /// last one.
666681 ///
@@ -1230,7 +1245,7 @@ impl<'a> RoomEventCacheStateLockWriteGuard<'a> {
12301245 //
12311246 // We must do this *after* persisting these events to storage (in
12321247 // `post_process_new_events`).
1233- self.shrink_to_last_chunk().await?;
1248+ self.shrink_to_last_chunk_in_memory().await?;
12341249 }
12351250
12361251 let timeline_event_diffs = self.state.room_linked_chunk.updates_as_vector_diffs();