Commit b9c68844ca91
tarball3 files changed+102-15
@@ -150,6 +150,13 @@ async fn coordinate_history(
150150
};151151
let cache = RoomEventCache { inner };152152
let recent = matches!(operation, HistoryOperation::Recent);153
+ let _background = matches!(154
+ operation,155
+ HistoryOperation::Restore | HistoryOperation::Recent | HistoryOperation::Fill(_)156
+ )157
+ .then(|| cache.inner.weak_room.get())158
+ .flatten()159
+ .map(|room| room.client().event_cache().hold_background());153160
let name = operation.name();154161
tracing::debug!(room_id = %cache.room_id(), operation = name, "history operation started");155162
let mut waiters = vec![reply];@@ -48,7 +48,7 @@ use matrix_sdk_base::{
4848
timer,4949
};5050
use matrix_sdk_common::timeout::timeout;51
-use ruma::{OwnedRoomId, RoomId};51
+use ruma::{OwnedRoomId, RoomId, time::Instant};5252
use tokio::sync::{5353
Mutex, RwLock,5454
broadcast::{Receiver, Sender, channel},@@ -248,6 +248,7 @@ impl EventCache {
248248
clears: Default::default(),249249
dropped_room_updates: Default::default(),250250
room_updates_handled: watch::Sender::new(0),251
+ background_holds: Default::default(),251252
by_room: Default::default(),252253
drop_handles: Default::default(),253254
auto_shrink_sender: Default::default(),@@ -395,6 +396,17 @@ impl EventCache {
395396
self.inner.dropped_room_updates.load(Ordering::SeqCst)396397
}397398
399
+ /// Holds off sync updates for rooms nobody views until dropped, for400
+ /// [`BACKGROUND_HOLD`] at most, so a room's history work isn't queued401
+ /// behind a catch-up's storage traffic.402
+ pub(crate) fn hold_background(&self) -> BackgroundHold {403
+ self.inner.background_holds.send_modify(|holds| {404
+ holds.count += 1;405
+ holds.since.get_or_insert_with(Instant::now);406
+ });407
+ BackgroundHold { inner: self.inner.clone() }408
+ }409
+398410
/// Check whether [`EventCache::subscribe`] has been called.399411
pub fn has_subscribed(&self) -> bool {400412
self.inner.drop_handles.get().is_some()@@ -539,6 +551,9 @@ struct EventCacheInner {
539551
/// the client's `room_updates_sent` terms.540552
room_updates_handled: watch::Sender<u64>,541553
554
+ /// History work in progress, holding off rooms nobody views.555
+ background_holds: watch::Sender<BackgroundHolds>,556
+542557
/// Lazily-filled cache of live [`RoomEventCache`], once per room.543558
//544559
// It's behind an `Arc` to get owned locks.@@ -604,6 +619,34 @@ const ROOM_UPDATES_AT_ONCE: usize = 1;
604619
#[cfg(target_family = "wasm")]605620
const ROOM_UPDATES_AT_ONCE: usize = 4;606621
622
+/// How long a room's history work can hold off sync updates for rooms nobody623
+/// views. Opening a room during a 470 room catch-up otherwise queued its624
+/// restore's IndexedDB reads behind every other room's, for 2.9s.625
+const BACKGROUND_HOLD: Duration = Duration::from_secs(3);626
+627
+#[derive(Debug, Default)]628
+struct BackgroundHolds {629
+ count: usize,630
+ since: Option<Instant>,631
+}632
+633
+/// Holds off sync updates for rooms nobody views while alive, see634
+/// [`EventCache::hold_background`].635
+pub(crate) struct BackgroundHold {636
+ inner: Arc<EventCacheInner>,637
+}638
+639
+impl Drop for BackgroundHold {640
+ fn drop(&mut self) {641
+ self.inner.background_holds.send_modify(|holds| {642
+ holds.count -= 1;643
+ if holds.count == 0 {644
+ holds.since = None;645
+ }646
+ });647
+ }648
+}649
+607650
/// One room's part of a [`RoomUpdates`].608651
enum RoomUpdate {609652
Left(LeftRoomUpdate),@@ -736,19 +779,19 @@ impl EventCacheInner {
736779
737780
/// Handle the next few rooms of `pending`, those on screen first, with the738781
/// `multiple_room_updates_lock` held.782
+ async fn viewed_pending(&self, pending: &PendingRoomUpdates) -> Vec<OwnedRoomId> {783
+ let by_room = self.by_room.read().await;784
+ pending785
+ .rooms786
+ .keys()787
+ .filter(|room_id| by_room.get(*room_id).is_some_and(|caches| caches.room.is_viewed()))788
+ .take(ROOM_UPDATES_AT_ONCE)789
+ .cloned()790
+ .collect()791
+ }792
+739793
async fn handle_pending_room_updates(&self, pending: &mut PendingRoomUpdates) {740
- let viewed: Vec<OwnedRoomId> = {741
- let by_room = self.by_room.read().await;742
- pending743
- .rooms744
- .keys()745
- .filter(|room_id| {746
- by_room.get(*room_id).is_some_and(|caches| caches.room.is_viewed())747
- })748
- .take(ROOM_UPDATES_AT_ONCE)749
- .cloned()750
- .collect()751
- };794
+ let viewed = self.viewed_pending(pending).await;752795
// rooms on screen get a round of their own, so a slow cold room can't hold them back753796
let round: Vec<OwnedRoomId> = if viewed.is_empty() {754797
pending.rooms.keys().take(ROOM_UPDATES_AT_ONCE).cloned().collect()@@ -20,7 +20,7 @@ use std::{
2020
use eyeball::Subscriber;2121
use matrix_sdk_base::{2222
linked_chunk::OwnedLinkedChunkId, serde_helpers::extract_thread_root_from_content,23
- sync::RoomUpdates,23
+ sleep::sleep, sync::RoomUpdates,2424
};2525
use ruma::{OwnedEventId, OwnedTransactionId};2626
use tokio::{@@ -36,7 +36,7 @@ use tokio::{
3636
use tracing::{Instrument as _, Span, debug, error, info, info_span, instrument, trace, warn};3737
3838
use super::{39
- AutoShrinkChannelPayload, EventCacheInner, EventsOrigin, PendingRoomUpdates,39
+ AutoShrinkChannelPayload, BACKGROUND_HOLD, EventCacheInner, EventsOrigin, PendingRoomUpdates,4040
RoomEventCacheLinkedChunkUpdate, RoomEventCacheUpdate, TimelineVectorDiffs,4141
};4242
use crate::{@@ -78,6 +78,10 @@ pub(super) async fn room_updates_task(
7878
}7979
}8080
81
+ if inner.viewed_pending(&pending).await.is_empty() {82
+ wait_out_background_hold(&inner, &mut room_updates_feed, &mut pending).await;83
+ }84
+8185
{8286
let _lock = inner.multiple_room_updates_lock.lock().await;8387
inner.handle_pending_room_updates(&mut pending).await;@@ -102,6 +106,39 @@ pub(super) async fn room_updates_task(
102106
}103107
}104108
109
+/// Wait while a room's history work holds off rooms nobody views, until it110
+/// ends, [`BACKGROUND_HOLD`] passes, or an update for a viewed room arrives.111
+async fn wait_out_background_hold(112
+ inner: &EventCacheInner,113
+ feed: &mut Receiver<RoomUpdates>,114
+ pending: &mut PendingRoomUpdates,115
+) {116
+ let mut holds = inner.background_holds.subscribe();117
+ loop {118
+ let since = holds.borrow_and_update().since;119
+ let Some(left) = since.and_then(|since| BACKGROUND_HOLD.checked_sub(since.elapsed()))120
+ else {121
+ return;122
+ };123
+ select! {124
+ changed = holds.changed() => if changed.is_err() {125
+ return;126
+ },127
+ () = sleep(left) => return,128
+ update = feed.recv() => {129
+ match update {130
+ Ok(updates) => pending.fold(updates),131
+ Err(RecvError::Lagged(num_skipped)) => pending.lagged += num_skipped,132
+ Err(RecvError::Closed) => return,133
+ }134
+ if !inner.viewed_pending(pending).await.is_empty() {135
+ return;136
+ }137
+ }138
+ }139
+ }140
+}141
+105142
/// Listen to _ignore user list update changes_ to clear the rooms when a user106143
/// is ignored or unignored.107144
#[instrument(skip_all)]