atagen

matrix-rust-sdk

public
Star0
Watch0

Commit f82c4ac42cb4

tarball

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

2 files changed+34-2
event-cache: ask a store read again when cache creation never hears back

A wasm soak left `load_all_chunks_metadata` pending in room cache creation with no IndexedDB transaction open, which holds the room caches lock and wedges every room. Both reads in `LockedRoomEventCacheState::new` now ask again after 5s and say so, which recovers a lost wakeup and tells it apart from a task that is never polled again.
Modified file: crates/matrix-sdk/src/event_cache/caches/mod.rs+24 additions
@@ -13,6 +13,7 @@
1313 // limitations under the License.
1414
1515 use std::{
16+ future::Future,
1617 sync::{Arc, atomic::AtomicU64},
1718 time::Duration,
1819 };
@@ -79,6 +80,29 @@ pub(super) async fn save_events(
7980 }
8081 }
8182
83+/// How long a store read may go unanswered before it is asked again.
84+const STORE_READ_RETRY: Duration = Duration::from_secs(5);
85+
86+/// Run a store read, and ask once more if the first one never answers.
87+///
88+/// On wasm an IndexedDB read during cache creation has been seen to stay pending with no
89+/// transaction left open, wedging every room behind it. The logs say whether a fresh read
90+/// goes through, which tells a lost wakeup from a task that is never polled again.
91+pub(super) async fn read_store<T, F>(what: &'static str, read: impl Fn() -> F) -> T
92+where
93+ F: Future<Output = T>,
94+{
95+ match timeout(read(), STORE_READ_RETRY).await {
96+ Ok(answer) => answer,
97+ Err(_) => {
98+ warn!(what, "a store read went unanswered, asking again");
99+ let answer = read().await;
100+ warn!(what, "the repeated store read answered");
101+ answer
102+ }
103+ }
104+}
105+
82106 /// A type to hold all the caches for a given room.
83107 #[derive(Debug)]
84108 pub(super) struct Caches {
Modified file: crates/matrix-sdk/src/event_cache/caches/room/state.rs+10 additions−2 deletions
@@ -345,7 +345,11 @@ impl LockedRoomEventCacheState {
345345 // indicates that at some point, there's some malformed data.
346346 // a storage error can be transient (an IndexedDB transaction aborted under us), and the
347347 // clear below throws away the room's whole history, so read once more before trusting it
348- let metadata = match load_linked_chunk_metadata(&store_guard, linked_chunk_id).await {
348+ let metadata = match super::super::read_store("linked chunk metadata", || {
349+ load_linked_chunk_metadata(&store_guard, linked_chunk_id)
350+ })
351+ .await
352+ {
349353 Err(EventCacheError::Storage(err)) => {
350354 warn!("retrying a linked chunk's metadata after a storage error: {err}");
351355 load_linked_chunk_metadata(&store_guard, linked_chunk_id).await
@@ -367,7 +371,11 @@ impl LockedRoomEventCacheState {
367371 }
368372 };
369373
370- let last_chunk = match store_guard.load_last_chunk(linked_chunk_id).await {
374+ let last_chunk = match super::super::read_store("latest chunk", || {
375+ store_guard.load_last_chunk(linked_chunk_id)
376+ })
377+ .await
378+ {
371379 Err(err) => {
372380 warn!("retrying a linked chunk's latest chunk after a storage error: {err}");
373381 store_guard.load_last_chunk(linked_chunk_id).await