atagen

matrix-rust-sdk

public
Star0
Watch0

Commit a88b3d82b396

tarball

atagen <boss@atagen.co>2026-10-26 08:53 UTC

2 files changed+59-35
event-cache: read a linked chunk once more before wiping it

Every IndexedDB DomException maps to InvalidData, so one aborted transaction while loading a room ('The transaction has finished') cleared that room's whole stored history. The fork's room Clear then scans and decrypts every chunk in the store under the global by_room lock, stalling every room's cache. A failed metadata, last-chunk or previous-chunk read now gets one retry before the reset.
Modified file: crates/matrix-sdk/src/event_cache/caches/room/pagination.rs+10 additions−5 deletions
@@ -184,11 +184,16 @@ impl PaginatedCache for Arc<RoomEventCacheInner> {
184184
185185 // The first chunk is not a gap, we can load its previous chunk.
186186 let linked_chunk_id = LinkedChunkId::Room(&state.state.room_id);
187- let new_first_chunk = match state
188- .store
189- .load_previous_chunk(linked_chunk_id, prev_first_chunk.identifier())
190- .await
191- {
187+ let first_chunk = prev_first_chunk.identifier();
188+ let loaded = match state.store.load_previous_chunk(linked_chunk_id, first_chunk).await {
189+ // a failed read resets the room, so a transient IndexedDB error gets a second read
190+ Err(err) => {
191+ tracing::warn!("retrying the previous chunk after a storage error: {err}");
192+ state.store.load_previous_chunk(linked_chunk_id, first_chunk).await
193+ }
194+ loaded => loaded,
195+ };
196+ let new_first_chunk = match loaded {
192197 Ok(Some(new_first_chunk)) => {
193198 // All good, let's continue with this chunk.
194199 new_first_chunk
Modified file: crates/matrix-sdk/src/event_cache/caches/room/state.rs+49 additions−30 deletions
@@ -333,30 +333,43 @@ impl LockedRoomEventCacheState {
333333 //
334334 // If loading the full linked chunk failed, we'll clear the event cache, as it
335335 // indicates that at some point, there's some malformed data.
336- let full_linked_chunk_metadata =
337- match load_linked_chunk_metadata(&store_guard, linked_chunk_id).await {
338- Ok(metas) => metas,
339- Err(err) => {
340- error!("error when loading a linked chunk's metadata from the store: {err}");
341-
342- // Try to clear storage for this room.
343- store_guard
344- .handle_linked_chunk_updates(linked_chunk_id, vec![Update::Clear])
345- .await?;
346-
347- // Restart with an empty linked chunk.
348- None
349- }
350- };
336+ // a storage error can be transient (an IndexedDB transaction aborted under us), and the
337+ // clear below throws away the room's whole history, so read once more before trusting it
338+ let metadata = match load_linked_chunk_metadata(&store_guard, linked_chunk_id).await {
339+ Err(EventCacheError::Storage(err)) => {
340+ warn!("retrying a linked chunk's metadata after a storage error: {err}");
341+ load_linked_chunk_metadata(&store_guard, linked_chunk_id).await
342+ }
343+ other => other,
344+ };
345+ let full_linked_chunk_metadata = match metadata {
346+ Ok(metas) => metas,
347+ Err(err) => {
348+ error!("error when loading a linked chunk's metadata from the store: {err}");
349+
350+ // Try to clear storage for this room.
351+ store_guard
352+ .handle_linked_chunk_updates(linked_chunk_id, vec![Update::Clear])
353+ .await?;
354+
355+ // Restart with an empty linked chunk.
356+ None
357+ }
358+ };
351359
352- let linked_chunk = match store_guard
353- .load_last_chunk(linked_chunk_id)
354- .await
355- .map_err(EventCacheError::from)
356- .and_then(|(last_chunk, chunk_identifier_generator)| {
360+ let last_chunk = match store_guard.load_last_chunk(linked_chunk_id).await {
361+ Err(err) => {
362+ warn!("retrying a linked chunk's latest chunk after a storage error: {err}");
363+ store_guard.load_last_chunk(linked_chunk_id).await
364+ }
365+ loaded => loaded,
366+ };
367+ let linked_chunk = match last_chunk.map_err(EventCacheError::from).and_then(
368+ |(last_chunk, chunk_identifier_generator)| {
357369 lazy_loader::from_last_chunk(last_chunk, chunk_identifier_generator)
358370 .map_err(EventCacheError::from)
359- }) {
371+ },
372+ ) {
360373 Ok(linked_chunk) => linked_chunk,
361374 Err(err) => {
362375 error!("error when loading a linked chunk's latest chunk from the store: {err}");
@@ -622,17 +635,23 @@ impl<'a> RoomEventCacheStateLockWriteGuard<'a> {
622635 pub async fn shrink_to_last_chunk(&mut self) -> Result<(), EventCacheError> {
623636 // Attempt to load the last chunk.
624637 let linked_chunk_id = LinkedChunkId::Room(&self.state.room_id);
625- let (last_chunk, chunk_identifier_generator) =
626- match self.store.load_last_chunk(linked_chunk_id).await {
627- Ok(pair) => pair,
638+ let loaded = match self.store.load_last_chunk(linked_chunk_id).await {
639+ Err(err) => {
640+ warn!("retrying a linked chunk's latest chunk after a storage error: {err}");
641+ self.store.load_last_chunk(linked_chunk_id).await
642+ }
643+ loaded => loaded,
644+ };
645+ let (last_chunk, chunk_identifier_generator) = match loaded {
646+ Ok(pair) => pair,
628647
629- Err(err) => {
630- // If loading the last chunk failed, clear the entire linked chunk.
631- error!("error when reloading a linked chunk from memory: {err}");
648+ Err(err) => {
649+ // If loading the last chunk failed, clear the entire linked chunk.
650+ error!("error when reloading a linked chunk from memory: {err}");
632651
633- return self.reset_internal().await;
634- }
635- };
652+ return self.reset_internal().await;
653+ }
654+ };
636655
637656 debug!("unloading the linked chunk, and resetting it to its last chunk");