atagen

matrix-rust-sdk

public
Star0
Watch0

Commit 0d900d49c1ac

tarball

atagen <boss@atagen.co>2026-10-27 04:11 UTC

1 file changed+13-0
event-cache: stop the retained-event walk at a looping stored chain

newest_retained_event_id walks load_previous_chunk back through the store with no cycle check, holding the room's state lock. A looping stored chain, which the store-ordered metadata bug could leave behind, kept it going forever. That stalls room_updates_task, which handles rooms one at a time, so no room took sync again. It now fails with a Cycle error. handle_sync also logs on entry, so a stuck room shows in a trace.
Modified file: crates/matrix-sdk/src/event_cache/caches/room/state.rs+13 additions
@@ -861,6 +861,9 @@ impl<'a> RoomEventCacheStateLockWriteGuard<'a> {
861861 }
862862 let linked_chunk_id = LinkedChunkId::Room(&self.state.room_id);
863863 let mut first = chunk.chunks().next().expect("a linked chunk is never empty").identifier();
864+ // a stored chain that loops back on itself would otherwise keep this walk, and the room
865+ // state lock it runs under, going forever, which stalls every room's sync handling
866+ let mut seen = HashSet::from([first]);
864867 while let Some(previous) = self.store.load_previous_chunk(linked_chunk_id, first).await? {
865868 if let ChunkContent::Items(events) = &previous.content
866869 && let Some(id) = events.iter().rev().find_map(Event::event_id)
@@ -868,6 +871,15 @@ impl<'a> RoomEventCacheStateLockWriteGuard<'a> {
868871 return Ok(Some(id));
869872 }
870873 first = previous.identifier;
874+ if !seen.insert(first) {
875+ error!(room_id = %self.state.room_id, "stored linked chunk loops back on itself");
876+ return Err(EventCacheError::LinkedChunkLoader(
877+ lazy_loader::LazyLoaderError::Cycle {
878+ new_chunk: first,
879+ with_chunk: first,
880+ },
881+ ));
882+ }
871883 }
872884 Ok(None)
873885 }
@@ -1012,6 +1024,7 @@ impl<'a> RoomEventCacheStateLockWriteGuard<'a> {
10121024 mut timeline: Timeline,
10131025 ephemeral_events: &[Raw<AnySyncEphemeralRoomEvent>],
10141026 ) -> Result<(bool, Vec<VectorDiff<Event>>), EventCacheError> {
1027+ debug!(room_id = %self.state.room_id, "handle_sync started");
10151028 let mut prev_batch = timeline.prev_batch.take();
10161029 let had_prev_batch = prev_batch.is_some();