Commit 82e22202f3da
tarball4 files changed+59-42
@@ -12,18 +12,26 @@
1212
// See the License for the specific language governing permissions and1313
// limitations under the License.1414
15
-use std::sync::{Arc, atomic::AtomicU64};15
+use std::{16
+ sync::{Arc, atomic::AtomicU64},17
+ time::Duration,18
+};1619
1720
use eyeball::SharedObservable;1821
use eyeball_im::VectorDiff;1922
use matrix_sdk_base::{2023
ThreadingSupport,21
- event_cache::{Event, store::EventCacheStoreLock},24
+ event_cache::{25
+ Event,26
+ store::{EventCacheStoreLock, EventCacheStoreLockGuard},27
+ },2228
linked_chunk::Position,2329
sync::{JoinedRoomUpdate, LeftRoomUpdate},2430
};31
+use matrix_sdk_common::{executor::spawn, timeout::timeout};2532
use ruma::{OwnedRoomId, RoomId};2633
use tokio::sync::{broadcast::Sender, mpsc};34
+use tracing::{debug, warn};2735
2836
use super::{EventCacheError, EventsOrigin, Result};2937
use crate::{@@ -39,6 +47,38 @@ mod read_receipts;
3947
pub mod room;4048
pub mod thread;4149
50
+/// How long a caller waits for events to reach the store.51
+const SAVE_WAIT: Duration = Duration::from_secs(10);52
+53
+/// Save events into the store, without notifying observers.54
+///55
+/// The save runs in its own task so cancelling the caller can't cut it short, and the caller56
+/// stops waiting after [`SAVE_WAIT`] so a save that never finishes can't hold a cache lock.57
+pub(super) async fn save_events(58
+ store: EventCacheStoreLockGuard,59
+ room_id: OwnedRoomId,60
+ events: Vec<Event>,61
+) -> Result<()> {62
+ let count = events.len();63
+ let saving = spawn({64
+ let room_id = room_id.clone();65
+ async move {66
+ debug!(%room_id, count, "saving events to the store");67
+ for event in events {68
+ store.save_event(&room_id, event).await?;69
+ }70
+ Result::Ok(())71
+ }72
+ });73
+ match timeout(saving, SAVE_WAIT).await {74
+ Ok(joined) => joined.expect("joining failed"),75
+ Err(_) => {76
+ warn!(%room_id, count, "events not saved to the store yet, no longer waiting");77
+ Ok(())78
+ }79
+ }80
+}81
+4282
/// A type to hold all the caches for a given room.4383
#[derive(Debug)]4484
pub(super) struct Caches {@@ -429,16 +429,18 @@ impl RoomEventCache {
429429
/// Save some events in the event cache, for further retrieval with430430
/// [`Self::event`].431431
pub(crate) async fn save_events(&self, events: impl IntoIterator<Item = Event>) {432
- match self.inner.state.write().await {433
- Ok(mut state_guard) => {434
- if let Err(err) = state_guard.save_events(events).await {435
- warn!("couldn't save event in the event cache: {err}");436
- }437
- }438
-432
+ // the save touches only the store, which keeps each save's read and write in one433
+ // transaction, so holding the room's state across it only wedged sync behind it434
+ let store = match self.inner.state.read().await {435
+ Ok(state_guard) => state_guard.store_guard(),439436
Err(err) => {440437
warn!("couldn't save event in the event cache: {err}");438
+ return;441439
}440
+ };441
+ let events = events.into_iter().collect();442
+ if let Err(err) = super::save_events(store, self.inner.room_id.clone(), events).await {443
+ warn!("couldn't save event in the event cache: {err}");442444
}443445
}444446
@@ -36,7 +36,6 @@ use matrix_sdk_base::{
3636
serde_helpers::{extract_edit_target, extract_thread_root},3737
sync::Timeline,3838
};39
-use matrix_sdk_common::executor::spawn;4039
use ruma::{4140
EventId, OwnedEventId, OwnedRoomId, OwnedUserId, TransactionId,4241
events::{@@ -462,6 +461,10 @@ impl<'a> RoomEventCacheStateLockReadGuard<'a> {
462461
super::history::gap_positions(&self.state.room_linked_chunk)463462
}464463
464
+ pub(super) fn store_guard(&self) -> EventCacheStoreLockGuard {465
+ self.store.clone()466
+ }467
+465468
pub(super) fn has_sync_hole(&self) -> bool {466469
self.state.sync_hole.is_some()467470
}@@ -1624,21 +1627,8 @@ impl<'a> RoomEventCacheStateLockWriteGuard<'a> {
16241627
&mut self,16251628
events: impl IntoIterator<Item = Event>,16261629
) -> Result<(), EventCacheError> {1627
- let store = self.store.clone();1628
- let room_id = self.state.room_id.clone();1629
- let events = events.into_iter().collect::<Vec<_>>();1630
-1631
- // Spawn a task so the save is uninterrupted by task cancellation.1632
- spawn(async move {1633
- for event in events {1634
- store.save_event(&room_id, event).await?;1635
- }1636
- super::Result::Ok(())1637
- })1638
- .await1639
- .expect("joining failed")?;1640
-1641
- Ok(())1630
+ let events = events.into_iter().collect();1631
+ super::super::save_events(self.store.clone(), self.state.room_id.clone(), events).await16421632
}16431633
16441634
#[cfg(test)]@@ -19,7 +19,6 @@ use matrix_sdk_base::{
1919
event_cache::{Event, Gap, store::EventCacheStoreLock},2020
linked_chunk::{OwnedLinkedChunkId, Position, Update},2121
};22
-use matrix_sdk_common::executor::spawn;2322
use ruma::{OwnedEventId, OwnedRoomId};2423
use tokio::sync::broadcast::Sender;2524
use tracing::instrument;@@ -192,22 +191,8 @@ impl<'a> ThreadEventCacheStateLockWriteGuard<'a> {
192191
193192
/// Save events into the database, without notifying observers.194193
pub async fn save_events(&mut self, events: impl IntoIterator<Item = Event>) -> Result<()> {195
- let store = self.store.clone();196
- let room_id = self.state.room_id.clone();197
- let events = events.into_iter().collect::<Vec<_>>();198
-199
- // Spawn a task so the save is uninterrupted by task cancellation.200
- spawn(async move {201
- for event in events {202
- store.save_event(&room_id, event).await?;203
- }204
-205
- Result::Ok(())206
- })207
- .await208
- .expect("joining failed")?;209
-210
- Ok(())194
+ let events = events.into_iter().collect();195
+ super::super::save_events(self.store.clone(), self.state.room_id.clone(), events).await211196
}212197
213198
/// Reset this data structure as if it were brand new.