matrix_sdk/event_cache/caches/
mod.rs1use std::{collections::HashMap, sync::Arc};
16
17use eyeball::SharedObservable;
18use eyeball_im::VectorDiff;
19use matrix_sdk_base::{
20 ThreadingSupport,
21 event_cache::Event,
22 linked_chunk::Position,
23 sync::{JoinedRoomUpdate, LeftRoomUpdate},
24};
25use ruma::{OwnedEventId, RoomId, room_version_rules::RoomVersionRules};
26use tokio::sync::{
27 OnceCell, OwnedRwLockReadGuard, OwnedRwLockWriteGuard, RwLock, broadcast::Sender, mpsc,
28};
29
30use self::subscriber::AutoShrinkMessage;
31use super::{
32 EventCacheError, EventsOrigin, Result, automatic_pagination::AutomaticPagination, states,
33};
34use crate::{client::WeakClient, room::WeakRoom};
35
36mod aggregator;
37pub mod event_focused;
38pub mod event_linked_chunk;
39pub mod pagination;
40pub mod pinned_events;
41mod read_receipts;
42pub mod room;
43pub mod subscriber;
44pub mod thread;
45
46#[derive(Debug)]
48pub(super) struct Caches {
49 pub room: room::RoomEventCache,
53
54 pub threads: Arc<RwLock<HashMap<OwnedEventId, thread::ThreadEventCache>>>,
59
60 pub pinned_events: OnceCell<pinned_events::PinnedEventsCache>,
64
65 pub event_focused:
70 Arc<RwLock<HashMap<event_focused::EventFocusedCacheKey, event_focused::EventFocusedCache>>>,
71
72 internals: CachesInternals,
74}
75
76#[derive(Debug)]
77struct CachesInternals {
78 state: states::StateLock,
79 auto_shrink_sender: mpsc::Sender<AutoShrinkMessage>,
80 linked_chunk_update_sender: Sender<room::RoomEventCacheLinkedChunkUpdate>,
81 room_version_rules: RoomVersionRules,
82}
83
84impl Caches {
85 pub async fn new(
87 weak_client: &WeakClient,
88 room_id: &RoomId,
89 generic_update_sender: Sender<room::RoomEventCacheGenericUpdate>,
90 linked_chunk_update_sender: Sender<room::RoomEventCacheLinkedChunkUpdate>,
91 auto_shrink_sender: mpsc::Sender<AutoShrinkMessage>,
92 state: &states::StateLock,
93 automatic_pagination: Option<AutomaticPagination>,
94 ) -> Result<Self> {
95 let Some(client) = weak_client.get() else {
96 return Err(EventCacheError::ClientDropped);
97 };
98
99 let weak_room = WeakRoom::new(weak_client.clone(), room_id.to_owned());
100
101 let room = client
102 .get_room(room_id)
103 .ok_or_else(|| EventCacheError::RoomNotFound { room_id: room_id.to_owned() })?;
104 let room_version_rules = room.clone_info().room_version_rules_or_default();
105
106 let pagination_status = SharedObservable::new(pagination::SharedPaginationStatus::Idle {
107 hit_timeline_start: false,
108 });
109
110 let enabled_thread_support =
111 matches!(client.base_client().threading_support, ThreadingSupport::Enabled { .. });
112
113 let update_sender = room::RoomEventCacheUpdateSender::new(generic_update_sender.clone());
114
115 let own_user_id =
116 client.user_id().expect("the user must be logged in, at this point").to_owned();
117
118 let room_state = state
119 .try_insert_once_with(
120 states::selectors::RoomStateSelector::new(room_id.to_owned()),
121 |store_guard| {
122 room::RoomEventCacheState::new(
123 own_user_id.clone(),
124 room_id.to_owned(),
125 weak_room.clone(),
126 room_version_rules.clone(),
127 enabled_thread_support,
128 update_sender.clone(),
129 linked_chunk_update_sender.clone(),
130 store_guard,
131 pagination_status.clone(),
132 automatic_pagination,
133 )
134 },
135 )
136 .await?;
137
138 let timeline_is_not_empty =
139 room_state.read().await?.room_linked_chunk().revents().next().is_some();
140
141 let room_event_cache = room::RoomEventCache::new(
142 room_id.to_owned(),
143 weak_room,
144 own_user_id,
145 room_state,
146 pagination_status,
147 auto_shrink_sender.clone(),
148 update_sender,
149 );
150
151 if timeline_is_not_empty {
154 let _ = generic_update_sender
155 .send(room::RoomEventCacheGenericUpdate { room_id: room_id.to_owned() });
156 }
157
158 Ok(Self {
159 room: room_event_cache,
160 threads: Arc::new(RwLock::new(HashMap::new())),
161 pinned_events: OnceCell::new(),
162 event_focused: Arc::new(RwLock::new(HashMap::new())),
163 internals: CachesInternals {
164 state: state.clone(),
165 auto_shrink_sender,
166 linked_chunk_update_sender,
167 room_version_rules,
168 },
169 })
170 }
171
172 pub fn room(&self) -> &room::RoomEventCache {
176 &self.room
177 }
178
179 pub async fn thread(
187 &self,
188 thread_id: OwnedEventId,
189 ) -> Result<
190 OwnedRwLockReadGuard<
191 HashMap<OwnedEventId, thread::ThreadEventCache>,
192 thread::ThreadEventCache,
193 >,
194 > {
195 Ok(
196 match OwnedRwLockWriteGuard::try_downgrade_map(
197 self.threads.clone().write_owned().await,
198 |threads| threads.get(&thread_id),
199 ) {
200 Ok(locked_cache) => locked_cache,
202 Err(mut threads) => {
204 let room = &self.room;
205 let cache = thread::ThreadEventCache::new(
206 room.room_id().to_owned(),
207 thread_id.clone(),
208 room.own_user_id().to_owned(),
209 self.internals.room_version_rules.clone(),
210 room.weak_room().to_owned(),
211 &self.internals.state,
212 self.internals.auto_shrink_sender.clone(),
213 room.update_sender().generic_update_sender().clone(),
214 self.internals.linked_chunk_update_sender.clone(),
215 )
216 .await?;
217
218 threads.insert(thread_id.clone(), cache);
219
220 OwnedRwLockWriteGuard::downgrade_map(threads, |threads| {
221 threads.get(&thread_id).unwrap()
222 })
223 }
224 },
225 )
226 }
227
228 pub async fn pinned_events(&self) -> Result<&pinned_events::PinnedEventsCache> {
232 self.pinned_events
233 .get_or_try_init(|| {
234 pinned_events::PinnedEventsCache::new(
235 self.room.weak_room(),
236 self.room.own_user_id().clone(),
237 self.internals.room_version_rules.clone(),
238 self.internals.linked_chunk_update_sender.clone(),
239 &self.internals.state,
240 )
241 })
242 .await
243 }
244
245 pub async fn event_focused(
249 &self,
250 event_id: OwnedEventId,
251 thread_mode: event_focused::EventFocusThreadMode,
252 number_of_initial_events: u16,
253 ) -> Result<
254 OwnedRwLockReadGuard<
255 HashMap<event_focused::EventFocusedCacheKey, event_focused::EventFocusedCache>,
256 event_focused::EventFocusedCache,
257 >,
258 > {
259 let key = event_focused::EventFocusedCacheKey { focused_event_id: event_id, thread_mode };
260
261 Ok(
262 match OwnedRwLockWriteGuard::try_downgrade_map(
263 self.event_focused.clone().write_owned().await,
264 |event_focused_caches| event_focused_caches.get(&key),
265 ) {
266 Ok(locked_cache) => locked_cache,
268 Err(mut event_focused_caches) => {
270 let cache = event_focused::EventFocusedCache::new(
271 self.room.weak_room().clone(),
272 key.clone(),
273 &self.internals.state,
274 self.internals.linked_chunk_update_sender.clone(),
275 )
276 .await?;
277 cache.start_from(number_of_initial_events, thread_mode).await?;
278
279 event_focused_caches.insert(key.clone(), cache);
280
281 OwnedRwLockWriteGuard::downgrade_map(
282 event_focused_caches,
283 |event_focused_caches| event_focused_caches.get(&key).unwrap(),
284 )
285 }
286 },
287 )
288 }
289
290 pub(super) async fn handle_joined_room_update(&self, updates: JoinedRoomUpdate) -> Result<()> {
292 let Self { room, threads: _, pinned_events, event_focused, internals } = &self;
293
294 {
296 let mut updates = updates.clone();
297 updates.timeline = aggregator::aggregate_timeline_for_room(updates.timeline);
298
299 room.handle_joined_room_update(updates).await?;
300 }
301
302 {
304 let mut updates = updates.clone();
305 updates.account_data.clear();
306 updates.ambiguity_changes.clear();
307
308 let timeline_for_threads = {
309 let all_states_lock = states::CacheStateLock::new(
313 states::selectors::AllStatesSelector::new(room.room_id().to_owned()),
314 self.internals.state.clone(),
315 );
316 let all_states = all_states_lock.read().await?;
317
318 aggregator::aggregate_timeline_for_threads(
319 &updates.timeline,
320 all_states.threads(),
321 all_states.room(),
322 &internals.room_version_rules.redaction,
323 )
324 .await?
325 };
326
327 for (thread_id, timeline) in timeline_for_threads {
328 let mut updates = updates.clone();
329 updates.timeline = timeline;
330
331 let thread = self.thread(thread_id).await?;
332 thread.handle_joined_room_update(updates).await?;
333
334 let new_thread_summary =
335 thread.state().read().await?.compute_thread_summary().await?;
336
337 room.update_thread_summary(thread.thread_id(), new_thread_summary).await?;
338 }
339 }
340
341 if let Some(pinned_events) = pinned_events.get() {
343 let mut updates = updates.clone();
344 updates.timeline = aggregator::aggregate_timeline_for_pinned_events(
345 &updates.timeline,
346 &pinned_events.state().read().await?.current_event_ids(),
347 &internals.room_version_rules.redaction,
348 );
349
350 pinned_events.handle_joined_room_update(updates).await?;
351 }
352
353 {
355 let _ = event_focused;
358 }
359
360 Ok(())
361 }
362
363 pub(super) async fn handle_left_room_update(&self, updates: LeftRoomUpdate) -> Result<()> {
365 let Self { room, threads: _, pinned_events, event_focused, internals } = &self;
366
367 {
369 let mut updates = updates.clone();
370 updates.timeline = aggregator::aggregate_timeline_for_room(updates.timeline);
371
372 room.handle_left_room_update(updates).await?;
373 }
374
375 {
377 let mut updates = updates.clone();
378 updates.account_data.clear();
379 updates.ambiguity_changes.clear();
380
381 let timeline_for_threads = {
382 let all_caches_states_lock = states::CacheStateLock::new(
386 states::selectors::AllStatesSelector::new(room.room_id().to_owned()),
387 self.internals.state.clone(),
388 );
389 let all_caches_states = all_caches_states_lock.read().await?;
390
391 aggregator::aggregate_timeline_for_threads(
392 &updates.timeline,
393 all_caches_states.threads(),
394 all_caches_states.room(),
395 &internals.room_version_rules.redaction,
396 )
397 .await?
398 };
399
400 for (thread_id, timeline) in timeline_for_threads {
401 let mut updates = updates.clone();
402 updates.timeline = timeline;
403
404 let thread = self.thread(thread_id).await?;
405 thread.handle_left_room_update(updates).await?;
406
407 let new_thread_summary =
408 thread.state().read().await?.compute_thread_summary().await?;
409
410 room.update_thread_summary(thread.thread_id(), new_thread_summary).await?;
411 }
412 }
413
414 if let Some(pinned_events) = pinned_events.get() {
416 let mut updates = updates.clone();
417 updates.timeline = aggregator::aggregate_timeline_for_pinned_events(
418 &updates.timeline,
419 &pinned_events.state().read().await?.current_event_ids(),
420 &internals.room_version_rules.redaction,
421 );
422
423 pinned_events.handle_left_room_update(updates).await?;
424 }
425
426 {
428 let _ = event_focused;
431 }
432
433 Ok(())
434 }
435
436 #[cfg(feature = "e2e-encryption")]
441 pub async fn all_in_memory_events(&self) -> Result<impl Iterator<Item = Event>> {
442 let mut events = self.room.events().await?;
447
448 {
450 let event_focused = self.event_focused.read().await;
451
452 for event_focused in event_focused.values() {
453 events.extend(event_focused.events().await?);
454 }
455 }
456
457 Ok(events.into_iter())
458 }
459
460 #[cfg(feature = "e2e-encryption")]
469 pub async fn all_events_of_type(
470 &self,
471 event_type: Option<&str>,
472 session_id: Option<&str>,
473 ) -> Result<impl Iterator<Item = Event>> {
474 let mut events = {
477 let state = self.internals.state.read().await?;
478
479 state.store.get_room_events(self.room.room_id(), event_type, session_id).await?
480 };
481
482 {
485 let event_focused = self.event_focused.read().await;
486
487 for event_focused in event_focused.values() {
488 events.extend(
489 event_focused
490 .events()
491 .await?
492 .into_iter()
493 .filter(|event| event_type == event.kind.event_type().as_deref())
494 .filter(|event| session_id == event.kind.session_id()),
495 );
496 }
497 }
498
499 Ok(events.into_iter())
500 }
501}
502
503#[derive(Clone, Debug)]
505pub struct TimelineVectorDiffs {
506 pub diffs: Vec<VectorDiff<Event>>,
508 pub origin: EventsOrigin,
510}
511
512#[derive(Debug)]
514pub(super) enum EventLocation {
515 Memory(Position),
517
518 Store,
520}