atagen

matrix-rust-sdk

public
Star0
Watch0

Commit c72dc01ad0dd

tarball

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

3 files changed+21-17
event-cache: never await a room while holding the room caches lock

The redecryptor read every room's events while holding the global by_room read lock. One room held up there (a full history load from IndexedDB) kept the lock, and since tokio queues new readers behind a waiting room creation, every room access stalled. That fits room opens timing out right after a recovery hands over the backup key, which sets the redecryptor going. It now clones the room handles out first.
Modified file: crates/matrix-sdk/src/event_cache/caches/mod.rs−10 deletions
@@ -146,16 +146,6 @@ impl Caches {
146146 pub async fn prepare_to_reset(&mut self) -> Result<ResetCaches<'_>> {
147147 ResetCaches::new(self).await
148148 }
149-
150- /// Get all events from all the event caches manged by this [`Cacches`].
151- ///
152- /// Events can be duplicated if present in different event caches.
153- #[cfg(feature = "e2e-encryption")]
154- pub async fn all_events(&self) -> Result<impl Iterator<Item = Event>> {
155- let events_from_room = self.room.events().await?;
156-
157- Ok(events_from_room.into_iter())
158- }
159149 }
160150
161151 /// Type holding exclusive locks over all event caches managed by a
Modified file: crates/matrix-sdk/src/event_cache/caches/room/history.rs+1 additions−1 deletions
@@ -253,7 +253,7 @@ impl RoomEventCache {
253253 mut cancellation: watch::Receiver<bool>,
254254 ) -> Result<RecentHistoryOutcome> {
255255 let (anchor, retained) = {
256- let mut state = self.inner.state.write().await?;
256+ let state = self.inner.state.write().await?;
257257 let retained: HashSet<_> = state
258258 .room_linked_chunk()
259259 .events()
Modified file: crates/matrix-sdk/src/event_cache/redecryptor.rs+20 additions−6 deletions
@@ -281,14 +281,28 @@ impl EventCache {
281281 Ok(events.into_iter().filter_map(filter_timeline_event_to_utd).collect())
282282 }
283283
284+ /// The rooms the event cache holds, taken out of `by_room` before any of them is awaited.
285+ ///
286+ /// A slow room (a full history load from IndexedDB) would otherwise keep the global lock, and
287+ /// tokio queues new readers behind a waiting room creation, so every room access stalls.
288+ async fn rooms_in_memory(&self) -> Vec<(OwnedRoomId, crate::event_cache::RoomEventCache)> {
289+ self.inner
290+ .by_room
291+ .read()
292+ .await
293+ .iter()
294+ .map(|(room_id, caches)| (room_id.clone(), caches.room.clone()))
295+ .collect()
296+ }
297+
284298 /// Retrieve a set of events that we weren't able to decrypt from the memory
285299 /// of the event cache.
286300 async fn get_utds_from_memory(&self) -> BTreeMap<OwnedRoomId, Vec<EventIdAndUtd>> {
287301 let mut utds = BTreeMap::new();
288302
289- for (room_id, caches) in self.inner.by_room.read().await.iter() {
290- let room_utds: Vec<_> = caches
291- .all_events()
303+ for (room_id, room) in self.rooms_in_memory().await {
304+ let room_utds: Vec<_> = room
305+ .events()
292306 .await
293307 .into_iter()
294308 .flatten()
@@ -324,9 +338,9 @@ impl EventCache {
324338 ) -> BTreeMap<OwnedRoomId, Vec<EventIdAndEvent>> {
325339 let mut decrypted_events = BTreeMap::new();
326340
327- for (room_id, caches) in self.inner.by_room.read().await.iter() {
328- let room_utds: Vec<_> = caches
329- .all_events()
341+ for (room_id, room) in self.rooms_in_memory().await {
342+ let room_utds: Vec<_> = room
343+ .events()
330344 .await
331345 .into_iter()
332346 .flatten()