1pub mod pagination;
16mod state;
17mod updates;
18
19use std::{collections::BTreeMap, fmt, sync::Arc};
20
21use eyeball::SharedObservable;
22use matrix_sdk_base::{
23 deserialized_responses::{AmbiguityChange, ThreadSummary},
24 event_cache::Event,
25 sync::Timeline,
26};
27use ruma::{
28 EventId, OwnedEventId, OwnedMxcUri, OwnedRoomId, OwnedUserId, RoomId,
29 events::{AnyRoomAccountDataEvent, relation::RelationType},
30 serde::Raw,
31};
32use tokio::sync::{Notify, mpsc};
33use tracing::{instrument, trace, warn};
34
35use self::pagination::RoomPagination;
36pub use self::{
37 state::RoomEventCacheState,
38 updates::{
39 RoomEventCacheGenericUpdate, RoomEventCacheLinkedChunkUpdate, RoomEventCacheUpdate,
40 RoomEventCacheUpdateSender,
41 },
42};
43use super::{
44 super::{
45 EventsOrigin, Result,
46 states::{CacheStateLock, StateLockWriteGuard, selectors::RoomStateSelector},
47 },
48 TimelineVectorDiffs,
49 event_linked_chunk::sort_positions_descending,
50 pagination::SharedPaginationStatus,
51 read_receipts::MaybeReceiptEventContent,
52 subscriber::{AutoShrinkMessage, Subscriber},
53};
54use crate::room::WeakRoom;
55
56#[derive(Clone)]
60pub struct RoomEventCache {
61 inner: Arc<RoomEventCacheInner>,
62}
63
64impl fmt::Debug for RoomEventCache {
65 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
66 f.debug_struct("RoomEventCache").finish_non_exhaustive()
67 }
68}
69
70impl RoomEventCache {
71 pub(super) fn new(
73 room_id: OwnedRoomId,
74 weak_room: WeakRoom,
75 own_user_id: OwnedUserId,
76 state: CacheStateLock<RoomStateSelector>,
77 shared_pagination_status: SharedObservable<SharedPaginationStatus>,
78 auto_shrink_sender: mpsc::Sender<AutoShrinkMessage>,
79 update_sender: RoomEventCacheUpdateSender,
80 ) -> Self {
81 Self {
82 inner: Arc::new(RoomEventCacheInner {
83 room_id,
84 weak_room,
85 own_user_id,
86 state,
87 update_sender,
88 pagination_batch_token_notifier: Notify::new(),
89 auto_shrink_sender,
90 shared_pagination_status,
91 }),
92 }
93 }
94
95 pub fn room_id(&self) -> &RoomId {
97 &self.inner.room_id
98 }
99
100 pub(super) fn own_user_id(&self) -> &OwnedUserId {
102 &self.inner.own_user_id
103 }
104
105 pub(super) fn weak_room(&self) -> &WeakRoom {
107 &self.inner.weak_room
108 }
109
110 pub async fn events(&self) -> Result<Vec<Event>> {
115 let state = self.inner.state.read().await?;
116
117 Ok(state.room_linked_chunk().events().map(|(_position, item)| item.clone()).collect())
118 }
119
120 pub async fn subscribe(&self) -> Result<(Vec<Event>, Subscriber<RoomEventCacheUpdate>)> {
127 let state = self.inner.state.read().await?;
128 let events =
129 state.room_linked_chunk().events().map(|(_position, item)| item.clone()).collect();
130
131 let subscribers_handle = state.subscribers_handle();
132
133 let subscriber = Subscriber::new(
134 self.inner.update_sender.new_room_receiver(),
135 AutoShrinkMessage::Room { room_id: self.inner.room_id.clone() },
136 self.inner.auto_shrink_sender.clone(),
137 subscribers_handle,
138 );
139
140 trace!("added a room event cache subscriber; new count: {}", subscribers_handle.count());
141
142 Ok((events, subscriber))
143 }
144
145 pub fn pagination(&self) -> RoomPagination {
148 RoomPagination::new(self.inner.clone())
149 }
150
151 pub async fn rfind_map_event_in_memory_by<O, P>(&self, predicate: P) -> Result<Option<O>>
159 where
160 P: FnMut(&Event) -> Option<O>,
161 {
162 Ok(self.inner.state.read().await?.rfind_map_event_in_memory_by(predicate))
163 }
164
165 pub async fn find_event(&self, event_id: &EventId) -> Result<Option<Event>> {
170 Ok(self
171 .inner
172 .state
173 .read()
174 .await?
175 .find_event(event_id)
176 .await
177 .ok()
178 .flatten()
179 .map(|(_loc, event)| event))
180 }
181
182 pub async fn find_event_with_relations(
194 &self,
195 event_id: &EventId,
196 filter: Option<Vec<RelationType>>,
197 ) -> Result<Option<(Event, Vec<Event>)>> {
198 Ok(self
200 .inner
201 .state
202 .read()
203 .await?
204 .find_event_with_relations(event_id, filter)
205 .await
206 .ok()
207 .flatten())
208 }
209
210 pub async fn find_event_relations(
222 &self,
223 event_id: &EventId,
224 filter: Option<Vec<RelationType>>,
225 ) -> Result<Vec<Event>> {
226 self.inner.state.read().await?.find_event_relations(event_id, filter.clone()).await
228 }
229
230 pub(in super::super) fn state(&self) -> &CacheStateLock<RoomStateSelector> {
232 &self.inner.state
233 }
234
235 #[instrument(skip_all, fields(room_id = %self.room_id()))]
237 pub(super) async fn handle_joined_room_update(
238 &self,
239 timeline: Timeline,
240 read_receipts: MaybeReceiptEventContent,
241 account_data: Vec<Raw<AnyRoomAccountDataEvent>>,
242 ambiguity_changes: BTreeMap<OwnedEventId, AmbiguityChange>,
243 avatar_changes: Option<BTreeMap<OwnedUserId, Option<OwnedMxcUri>>>,
244 ) -> Result<()> {
245 self.inner
246 .handle_timeline(timeline, read_receipts, ambiguity_changes, avatar_changes)
247 .await?;
248 self.inner.handle_account_data(account_data);
249
250 Ok(())
251 }
252
253 #[instrument(skip_all, fields(room_id = %self.room_id()))]
255 pub(super) async fn handle_left_room_update(
256 &self,
257 timeline: Timeline,
258 ambiguity_changes: BTreeMap<OwnedEventId, AmbiguityChange>,
259 ) -> Result<()> {
260 self.inner
261 .handle_timeline(timeline, MaybeReceiptEventContent::none(), ambiguity_changes, None)
262 .await?;
263
264 Ok(())
265 }
266
267 pub(in super::super) async fn update_thread_summary(
268 &self,
269 thread_id: &EventId,
270 new_thread_summary: Option<ThreadSummary>,
271 ) -> Result<()> {
272 let timeline_event_diffs = self
273 .inner
274 .state
275 .write()
276 .await?
277 .update_thread_summary(thread_id, new_thread_summary)
278 .await?;
279
280 if !timeline_event_diffs.is_empty() {
281 self.inner.update_sender.send(
282 RoomEventCacheUpdate::UpdateTimelineEvents(TimelineVectorDiffs {
283 diffs: timeline_event_diffs,
284 origin: EventsOrigin::Sync,
285 }),
286 Some(RoomEventCacheGenericUpdate { room_id: self.inner.room_id.clone() }),
287 );
288 }
289
290 Ok(())
291 }
292
293 pub(in super::super) fn update_sender(&self) -> &RoomEventCacheUpdateSender {
295 &self.inner.update_sender
296 }
297
298 pub(crate) async fn insert_sent_event_from_send_queue(&self, event: Event) -> Result<()> {
300 self.inner.insert_sent_event_from_send_queue(event).await
301 }
302
303 pub async fn debug_string(&self) -> Vec<String> {
306 match self.inner.state.read().await {
307 Ok(read_guard) => read_guard.room_linked_chunk().debug_string(),
308 Err(err) => {
309 warn!(?err, "Failed to obtain the read guard for the `RoomEventCache`");
310
311 vec![]
312 }
313 }
314 }
315}
316
317pub(super) struct RoomEventCacheInner {
319 room_id: OwnedRoomId,
321
322 weak_room: WeakRoom,
323
324 own_user_id: OwnedUserId,
326
327 state: CacheStateLock<RoomStateSelector>,
329
330 pagination_batch_token_notifier: Notify,
332
333 shared_pagination_status: SharedObservable<SharedPaginationStatus>,
334
335 auto_shrink_sender: mpsc::Sender<AutoShrinkMessage>,
340
341 update_sender: RoomEventCacheUpdateSender,
343}
344
345impl RoomEventCacheInner {
346 fn handle_account_data(&self, account_data: Vec<Raw<AnyRoomAccountDataEvent>>) {
347 if account_data.is_empty() {
348 return;
349 }
350
351 let mut handled_read_marker = false;
352
353 trace!("Handling account data");
354
355 for raw_event in account_data {
356 match raw_event.deserialize() {
357 Ok(AnyRoomAccountDataEvent::FullyRead(ev)) => {
358 if handled_read_marker {
361 continue;
362 }
363
364 handled_read_marker = true;
365
366 self.update_sender.send(
368 RoomEventCacheUpdate::MoveReadMarkerTo { event_id: ev.content.event_id },
369 None,
370 );
371 }
372
373 Ok(_) => {
374 }
377
378 Err(e) => {
379 let event_type = raw_event.get_field::<String>("type").ok().flatten();
380 warn!(event_type, "Failed to deserialize account data: {e}");
381 }
382 }
383 }
384 }
385
386 async fn handle_timeline(
389 &self,
390 timeline: Timeline,
391 read_receipts: MaybeReceiptEventContent,
392 ambiguity_changes: BTreeMap<OwnedEventId, AmbiguityChange>,
393 avatar_changes: Option<BTreeMap<OwnedUserId, Option<OwnedMxcUri>>>,
394 ) -> Result<()> {
395 self.handle_timeline_inner(
396 self.state.write().await?,
397 timeline,
398 read_receipts,
399 ambiguity_changes,
400 avatar_changes,
401 )
402 .await
403 }
404
405 async fn insert_sent_event_from_send_queue(&self, event: Event) -> Result<()> {
409 let state = self.state.write().await?;
410
411 if state.room_linked_chunk().events().next().is_some() {
415 return self
416 .handle_timeline_inner(
417 state,
418 Timeline { limited: false, prev_batch: None, events: vec![event] },
419 MaybeReceiptEventContent::none(),
420 BTreeMap::new(),
421 None,
422 )
423 .await;
424 }
425
426 Ok(())
427 }
428
429 async fn handle_timeline_inner(
430 &self,
431 mut state: StateLockWriteGuard<'_, RoomEventCacheState>,
432 timeline: Timeline,
433 read_receipts: MaybeReceiptEventContent,
434 ambiguity_changes: BTreeMap<OwnedEventId, AmbiguityChange>,
435 avatar_changes: Option<BTreeMap<OwnedUserId, Option<OwnedMxcUri>>>,
436 ) -> Result<()> {
437 if timeline.events.is_empty()
438 && timeline.prev_batch.is_none()
439 && read_receipts.is_none()
440 && ambiguity_changes.is_empty()
441 && avatar_changes.as_ref().is_none_or(|avatars| avatars.is_empty())
442 {
443 return Ok(());
444 }
445
446 trace!("adding new events");
447
448 let (stored_prev_batch_token, timeline_event_diffs) =
449 state.handle_sync(timeline, &read_receipts).await?;
450
451 drop(state);
452
453 if stored_prev_batch_token {
456 self.pagination_batch_token_notifier.notify_one();
457 }
458
459 if !timeline_event_diffs.is_empty() {
462 self.update_sender.send(
463 RoomEventCacheUpdate::UpdateTimelineEvents(TimelineVectorDiffs {
464 diffs: timeline_event_diffs,
465 origin: EventsOrigin::Sync,
466 }),
467 Some(RoomEventCacheGenericUpdate { room_id: self.room_id.clone() }),
468 );
469 }
470
471 if let Some(read_receipts) = read_receipts.into_inner() {
472 self.update_sender
473 .send(RoomEventCacheUpdate::AddReadReceiptEvent { event: read_receipts }, None);
474 }
475
476 if !ambiguity_changes.is_empty() || avatar_changes.as_ref().is_some_and(|c| !c.is_empty()) {
477 self.update_sender.send(
478 RoomEventCacheUpdate::UpdateMembers { ambiguity_changes, avatar_changes },
479 None,
480 );
481 }
482
483 Ok(())
484 }
485}
486
487#[cfg(test)]
488mod tests {
489 use matrix_sdk_base::{RoomState, event_cache::Event};
490 use matrix_sdk_test::{async_test, event_factory::EventFactory};
491 use ruma::{
492 RoomId, event_id,
493 events::{relation::RelationType, room::message::RoomMessageEventContentWithoutRelation},
494 room_id, user_id,
495 };
496
497 use crate::test_utils::logged_in_client;
498
499 #[async_test]
500 async fn test_find_event_by_id_with_edit_relation() {
501 let original_id = event_id!("$original");
502 let related_id = event_id!("$related");
503 let room_id = room_id!("!galette:saucisse.bzh");
504 let f = EventFactory::new().room(room_id).sender(user_id!("@ben:saucisse.bzh"));
505
506 assert_relations(
507 room_id,
508 f.text_msg("Original event").event_id(original_id).into(),
509 f.text_msg("* An edited event")
510 .edit(
511 original_id,
512 RoomMessageEventContentWithoutRelation::text_plain("And edited event"),
513 )
514 .event_id(related_id)
515 .into(),
516 f,
517 )
518 .await;
519 }
520
521 #[async_test]
522 async fn test_find_event_by_id_with_thread_reply_relation() {
523 let original_id = event_id!("$original");
524 let related_id = event_id!("$related");
525 let room_id = room_id!("!galette:saucisse.bzh");
526 let f = EventFactory::new().room(room_id).sender(user_id!("@ben:saucisse.bzh"));
527
528 assert_relations(
529 room_id,
530 f.text_msg("Original event").event_id(original_id).into(),
531 f.text_msg("A reply").in_thread(original_id, related_id).event_id(related_id).into(),
532 f,
533 )
534 .await;
535 }
536
537 #[async_test]
538 async fn test_find_event_by_id_with_reaction_relation() {
539 let original_id = event_id!("$original");
540 let related_id = event_id!("$related");
541 let room_id = room_id!("!galette:saucisse.bzh");
542 let f = EventFactory::new().room(room_id).sender(user_id!("@ben:saucisse.bzh"));
543
544 assert_relations(
545 room_id,
546 f.text_msg("Original event").event_id(original_id).into(),
547 f.reaction(original_id, ":D").event_id(related_id).into(),
548 f,
549 )
550 .await;
551 }
552
553 #[async_test]
554 async fn test_find_event_by_id_with_poll_response_relation() {
555 let original_id = event_id!("$original");
556 let related_id = event_id!("$related");
557 let room_id = room_id!("!galette:saucisse.bzh");
558 let f = EventFactory::new().room(room_id).sender(user_id!("@ben:saucisse.bzh"));
559
560 assert_relations(
561 room_id,
562 f.poll_start("Poll start event", "A poll question", vec!["An answer"])
563 .event_id(original_id)
564 .into(),
565 f.poll_response(vec!["1"], original_id).event_id(related_id).into(),
566 f,
567 )
568 .await;
569 }
570
571 #[async_test]
572 async fn test_find_event_by_id_with_poll_end_relation() {
573 let original_id = event_id!("$original");
574 let related_id = event_id!("$related");
575 let room_id = room_id!("!galette:saucisse.bzh");
576 let f = EventFactory::new().room(room_id).sender(user_id!("@ben:saucisse.bzh"));
577
578 assert_relations(
579 room_id,
580 f.poll_start("Poll start event", "A poll question", vec!["An answer"])
581 .event_id(original_id)
582 .into(),
583 f.poll_end("Poll ended", original_id).event_id(related_id).into(),
584 f,
585 )
586 .await;
587 }
588
589 #[async_test]
590 async fn test_find_event_by_id_with_filtered_relationships() {
591 let original_id = event_id!("$original");
592 let related_id = event_id!("$related");
593 let associated_related_id = event_id!("$recursive_related");
594 let room_id = room_id!("!galette:saucisse.bzh");
595 let event_factory = EventFactory::new().room(room_id).sender(user_id!("@ben:saucisse.bzh"));
596
597 let original_event = event_factory.text_msg("Original event").event_id(original_id).into();
598 let related_event = event_factory
599 .text_msg("* Edited event")
600 .edit(original_id, RoomMessageEventContentWithoutRelation::text_plain("Edited event"))
601 .event_id(related_id)
602 .into();
603 let associated_related_event =
604 event_factory.reaction(related_id, "🤡").event_id(associated_related_id).into();
605
606 let client = logged_in_client(None).await;
607
608 let event_cache = client.event_cache();
609 event_cache.subscribe().unwrap();
610
611 client.base_client().get_or_create_room(room_id, RoomState::Joined);
612 let room = client.get_room(room_id).unwrap();
613
614 let (room_event_cache, _drop_handles) = room.event_cache().await.unwrap();
615
616 {
617 let mut state = room_event_cache.inner.state.write().await.unwrap();
618
619 state.save_events([original_event]).await.unwrap();
621
622 state.save_events([related_event]).await.unwrap();
624
625 state.save_events([associated_related_event]).await.unwrap();
627 }
628
629 let filter = Some(vec![RelationType::Replacement]);
630 let (event, related_events) = room_event_cache
631 .find_event_with_relations(original_id, filter)
632 .await
633 .expect("Failed to find the event with relations")
634 .expect("Event has no relation");
635 let cached_event_id = event.event_id().unwrap();
637 assert_eq!(cached_event_id, original_id);
638
639 assert_eq!(related_events.len(), 1);
641
642 let related_event_id = related_events[0].event_id().unwrap();
643 assert_eq!(related_event_id, related_id);
644
645 let filter = Some(vec![RelationType::Thread]);
647 let (event, related_events) = room_event_cache
648 .find_event_with_relations(original_id, filter)
649 .await
650 .expect("Failed to find the event with relations")
651 .expect("Event has no relation");
652
653 let cached_event_id = event.event_id().unwrap();
655 assert_eq!(cached_event_id, original_id);
656 assert!(related_events.is_empty());
658 }
659
660 #[async_test]
661 async fn test_find_event_by_id_with_recursive_relation() {
662 let original_id = event_id!("$original");
663 let related_id = event_id!("$related");
664 let associated_related_id = event_id!("$recursive_related");
665 let room_id = room_id!("!galette:saucisse.bzh");
666 let event_factory = EventFactory::new().room(room_id).sender(user_id!("@ben:saucisse.bzh"));
667
668 let original_event = event_factory.text_msg("Original event").event_id(original_id).into();
669 let related_event = event_factory
670 .text_msg("* Edited event")
671 .edit(original_id, RoomMessageEventContentWithoutRelation::text_plain("Edited event"))
672 .event_id(related_id)
673 .into();
674 let associated_related_event =
675 event_factory.reaction(related_id, "👍").event_id(associated_related_id).into();
676
677 let client = logged_in_client(None).await;
678
679 let event_cache = client.event_cache();
680 event_cache.subscribe().unwrap();
681
682 client.base_client().get_or_create_room(room_id, RoomState::Joined);
683 let room = client.get_room(room_id).unwrap();
684
685 let (room_event_cache, _drop_handles) = room.event_cache().await.unwrap();
686
687 {
688 let mut state = room_event_cache.inner.state.write().await.unwrap();
689
690 state.save_events([original_event]).await.unwrap();
692
693 state.save_events([related_event]).await.unwrap();
695
696 state.save_events([associated_related_event]).await.unwrap();
698 }
699
700 let (event, related_events) = room_event_cache
701 .find_event_with_relations(original_id, None)
702 .await
703 .expect("Failed to find the event with relations")
704 .expect("Event has no relation");
705 let cached_event_id = event.event_id().unwrap();
707 assert_eq!(cached_event_id, original_id);
708
709 assert_eq!(related_events.len(), 2);
711
712 let related_event_id = related_events[0].event_id().unwrap();
713 assert_eq!(related_event_id, related_id);
714 let related_event_id = related_events[1].event_id().unwrap();
715 assert_eq!(related_event_id, associated_related_id);
716 }
717
718 async fn assert_relations(
719 room_id: &RoomId,
720 original_event: Event,
721 related_event: Event,
722 event_factory: EventFactory,
723 ) {
724 let client = logged_in_client(None).await;
725
726 let event_cache = client.event_cache();
727 event_cache.subscribe().unwrap();
728
729 client.base_client().get_or_create_room(room_id, RoomState::Joined);
730 let room = client.get_room(room_id).unwrap();
731
732 let (room_event_cache, _drop_handles) = room.event_cache().await.unwrap();
733
734 let original_event_id = original_event.event_id().unwrap().to_owned();
735 let related_id = related_event.event_id().unwrap().to_owned();
736
737 {
738 let mut state = room_event_cache.inner.state.write().await.unwrap();
739
740 state.save_events([original_event]).await.unwrap();
742
743 let unrelated_id = event_id!("$2");
745 state
746 .save_events([event_factory
747 .text_msg("An unrelated event")
748 .event_id(unrelated_id)
749 .into()])
750 .await
751 .unwrap();
752
753 state.save_events([related_event]).await.unwrap();
755 }
756
757 let (event, related_events) = room_event_cache
758 .find_event_with_relations(&original_event_id, None)
759 .await
760 .expect("Failed to find the event with relations")
761 .expect("Event has no relation");
762 let cached_event_id = event.event_id().unwrap();
764 assert_eq!(cached_event_id, original_event_id);
765
766 let related_event_id = related_events[0].event_id().unwrap();
768 assert_eq!(related_event_id, related_id);
769 }
770}
771
772#[cfg(all(test, not(target_family = "wasm")))] mod timed_tests {
774 use std::{ops::Not, sync::Arc};
775
776 use assert_matches::assert_matches;
777 use assert_matches2::assert_let;
778 use eyeball_im::VectorDiff;
779 use futures_util::FutureExt;
780 use matrix_sdk_base::{
781 RoomState,
782 event_cache::{
783 Gap,
784 store::{EventCacheStore as _, MemoryStore},
785 },
786 linked_chunk::{
787 ChunkContent, ChunkIdentifier, LinkedChunkId, Position, Update,
788 lazy_loader::from_all_chunks,
789 },
790 store::StoreConfig,
791 sync::Timeline,
792 };
793 use matrix_sdk_common::cross_process_lock::CrossProcessLockConfig;
794 use matrix_sdk_test::{ALICE, BOB, async_test, event_factory::EventFactory};
795 use ruma::{
796 EventId, event_id,
797 events::{AnySyncMessageLikeEvent, AnySyncTimelineEvent},
798 room_id,
799 serde::Raw,
800 user_id,
801 };
802 use serde_json::json;
803 use tokio::task::yield_now;
804
805 use super::{
806 super::{super::TimelineVectorDiffs, pagination::LoadMoreEventsBackwardsOutcome},
807 MaybeReceiptEventContent, RoomEventCache, RoomEventCacheGenericUpdate,
808 RoomEventCacheUpdate,
809 };
810 use crate::{assert_let_timeout, test_utils::client::MockClientBuilder};
811
812 #[async_test]
813 async fn test_write_to_storage() {
814 let room_id = room_id!("!galette:saucisse.bzh");
815 let event_id_0 = event_id!("$ev0");
816 let f = EventFactory::new().room(room_id).sender(user_id!("@ben:saucisse.bzh"));
817
818 let event_cache_store = Arc::new(MemoryStore::new());
819
820 let client = MockClientBuilder::new(None)
821 .on_builder(|builder| {
822 builder.store_config(
823 StoreConfig::new(CrossProcessLockConfig::multi_process("hodor"))
824 .event_cache_store(event_cache_store.clone()),
825 )
826 })
827 .build()
828 .await;
829
830 let event_cache = client.event_cache();
831
832 event_cache.subscribe().unwrap();
834
835 client.base_client().get_or_create_room(room_id, RoomState::Joined);
836 let room = client.get_room(room_id).unwrap();
837
838 let mut generic_stream = event_cache.subscribe_to_room_generic_updates();
839 let (room_event_cache, _drop_handles) = room.event_cache().await.unwrap();
840
841 let timeline = Timeline {
843 limited: true,
844 prev_batch: Some("raclette".to_owned()),
845 events: vec![f.text_msg("hey yo").event_id(event_id_0).into_event()],
846 };
847
848 room_event_cache
849 .handle_joined_room_update(
850 timeline,
851 MaybeReceiptEventContent::none(),
852 Default::default(),
853 Default::default(),
854 Default::default(),
855 )
856 .await
857 .unwrap();
858
859 assert_matches!(
861 generic_stream.recv().await,
862 Ok(RoomEventCacheGenericUpdate { room_id: expected_room_id }) => {
863 assert_eq!(expected_room_id, room_id);
864 }
865 );
866 assert!(generic_stream.is_empty());
867
868 let linked_chunk = from_all_chunks::<3, _, _>(
870 event_cache_store.load_all_chunks(LinkedChunkId::Room(room_id)).await.unwrap(),
871 )
872 .unwrap()
873 .unwrap();
874
875 assert_eq!(linked_chunk.chunks().count(), 2);
876
877 let mut chunks = linked_chunk.chunks();
878
879 assert_matches!(chunks.next().unwrap().content(), ChunkContent::Gap(gap) => {
881 assert_eq!(gap.token, "raclette");
882 });
883
884 assert_matches!(chunks.next().unwrap().content(), ChunkContent::Items(events) => {
886 assert_eq!(events.len(), 1);
887 assert_eq!(events[0].event_id(), Some(event_id_0));
888 });
889
890 assert!(chunks.next().is_none());
892 }
893
894 #[async_test]
895 async fn test_write_to_storage_strips_bundled_relations() {
896 let room_id = room_id!("!galette:saucisse.bzh");
897 let f = EventFactory::new().room(room_id).sender(user_id!("@ben:saucisse.bzh"));
898
899 let event_cache_store = Arc::new(MemoryStore::new());
900
901 let client = MockClientBuilder::new(None)
902 .on_builder(|builder| {
903 builder.store_config(
904 StoreConfig::new(CrossProcessLockConfig::multi_process("hodor"))
905 .event_cache_store(event_cache_store.clone()),
906 )
907 })
908 .build()
909 .await;
910
911 let event_cache = client.event_cache();
912
913 event_cache.subscribe().unwrap();
915
916 client.base_client().get_or_create_room(room_id, RoomState::Joined);
917 let room = client.get_room(room_id).unwrap();
918
919 let mut generic_stream = event_cache.subscribe_to_room_generic_updates();
920 let (room_event_cache, _drop_handles) = room.event_cache().await.unwrap();
921
922 let ev = f
924 .text_msg("hey yo")
925 .sender(*ALICE)
926 .with_bundled_edit(f.text_msg("Hello, Kind Sir").sender(*ALICE))
927 .into_event();
928
929 let timeline = Timeline { limited: false, prev_batch: None, events: vec![ev] };
930
931 room_event_cache
932 .handle_joined_room_update(
933 timeline,
934 MaybeReceiptEventContent::none(),
935 Default::default(),
936 Default::default(),
937 Default::default(),
938 )
939 .await
940 .unwrap();
941
942 assert_matches!(
944 generic_stream.recv().await,
945 Ok(RoomEventCacheGenericUpdate { room_id: expected_room_id }) => {
946 assert_eq!(expected_room_id, room_id);
947 }
948 );
949 assert!(generic_stream.is_empty());
950
951 {
953 let events = room_event_cache.events().await.unwrap();
954
955 assert_eq!(events.len(), 1);
956
957 let ev = events[0].raw().deserialize().unwrap();
958 assert_let!(
959 AnySyncTimelineEvent::MessageLike(AnySyncMessageLikeEvent::RoomMessage(msg)) = ev
960 );
961
962 let original = msg.as_original().unwrap();
963 assert_eq!(original.content.body(), "hey yo");
964 assert!(original.unsigned.relations.replace.is_some());
965 }
966
967 let linked_chunk = from_all_chunks::<3, _, _>(
969 event_cache_store.load_all_chunks(LinkedChunkId::Room(room_id)).await.unwrap(),
970 )
971 .unwrap()
972 .unwrap();
973
974 assert_eq!(linked_chunk.chunks().count(), 1);
975
976 let mut chunks = linked_chunk.chunks();
977 assert_matches!(chunks.next().unwrap().content(), ChunkContent::Items(events) => {
978 assert_eq!(events.len(), 1);
979
980 let ev = events[0].raw().deserialize().unwrap();
981 assert_let!(AnySyncTimelineEvent::MessageLike(AnySyncMessageLikeEvent::RoomMessage(msg)) = ev);
982
983 let original = msg.as_original().unwrap();
984 assert_eq!(original.content.body(), "hey yo");
985 assert!(original.unsigned.relations.replace.is_none());
986 });
987
988 assert!(chunks.next().is_none());
990 }
991
992 #[async_test]
993 async fn test_clear() {
994 let room_id = room_id!("!galette:saucisse.bzh");
995 let f = EventFactory::new().room(room_id).sender(user_id!("@ben:saucisse.bzh"));
996
997 let event_cache_store = Arc::new(MemoryStore::new());
998
999 let event_id1 = event_id!("$1");
1000 let event_id2 = event_id!("$2");
1001
1002 let ev1 = f.text_msg("hello world").event_id(event_id1).into_event();
1003 let ev2 = f.text_msg("how's it going").event_id(event_id2).into_event();
1004
1005 event_cache_store
1007 .handle_linked_chunk_updates(
1008 LinkedChunkId::Room(room_id),
1009 vec![
1010 Update::NewItemsChunk {
1012 previous: None,
1013 new: ChunkIdentifier::new(0),
1014 next: None,
1015 },
1016 Update::NewGapChunk {
1018 previous: Some(ChunkIdentifier::new(0)),
1019 new: ChunkIdentifier::new(42),
1021 next: None,
1022 gap: Gap { token: "comté".to_owned() },
1023 },
1024 Update::NewItemsChunk {
1026 previous: Some(ChunkIdentifier::new(42)),
1027 new: ChunkIdentifier::new(1),
1028 next: None,
1029 },
1030 Update::PushItems {
1031 at: Position::new(ChunkIdentifier::new(1), 0),
1032 items: vec![ev1.clone()],
1033 },
1034 Update::NewItemsChunk {
1036 previous: Some(ChunkIdentifier::new(1)),
1037 new: ChunkIdentifier::new(2),
1038 next: None,
1039 },
1040 Update::PushItems {
1041 at: Position::new(ChunkIdentifier::new(2), 0),
1042 items: vec![ev2.clone()],
1043 },
1044 ],
1045 )
1046 .await
1047 .unwrap();
1048
1049 let client = MockClientBuilder::new(None)
1050 .on_builder(|builder| {
1051 builder.store_config(
1052 StoreConfig::new(CrossProcessLockConfig::multi_process("hodor"))
1053 .event_cache_store(event_cache_store.clone()),
1054 )
1055 })
1056 .build()
1057 .await;
1058
1059 let event_cache = client.event_cache();
1060
1061 event_cache.subscribe().unwrap();
1063
1064 client.base_client().get_or_create_room(room_id, RoomState::Joined);
1065 let room = client.get_room(room_id).unwrap();
1066
1067 let (room_event_cache, _drop_handles) = room.event_cache().await.unwrap();
1068
1069 let (items, mut stream) = room_event_cache.subscribe().await.unwrap();
1070 let mut generic_stream = event_cache.subscribe_to_room_generic_updates();
1071
1072 {
1074 assert!(room_event_cache.find_event(event_id1).await.unwrap().is_some());
1075 assert!(room_event_cache.find_event(event_id2).await.unwrap().is_some());
1076 }
1077
1078 {
1080 assert_eq!(items.len(), 1);
1082 assert_eq!(items[0].event_id().unwrap(), event_id2);
1083
1084 assert!(stream.is_empty());
1085 }
1086
1087 {
1089 room_event_cache.pagination().run_backwards_once(20).await.unwrap();
1090
1091 assert_let_timeout!(
1092 Ok(RoomEventCacheUpdate::UpdateTimelineEvents(TimelineVectorDiffs { diffs, .. })) =
1093 stream.recv()
1094 );
1095 assert_eq!(diffs.len(), 1);
1096 assert_matches!(&diffs[0], VectorDiff::Insert { index: 0, value: event } => {
1097 assert_eq!(event.event_id().unwrap(), event_id1);
1099 });
1100
1101 assert!(stream.is_empty());
1102
1103 assert_let_timeout!(
1104 Ok(RoomEventCacheGenericUpdate { room_id: expected_room_id }) =
1105 generic_stream.recv()
1106 );
1107 assert_eq!(room_id, expected_room_id);
1108 assert!(generic_stream.is_empty());
1109 }
1110
1111 event_cache.clear_all_rooms().await.unwrap();
1113
1114 assert_let_timeout!(
1116 Ok(RoomEventCacheUpdate::UpdateTimelineEvents(TimelineVectorDiffs { diffs, .. })) =
1117 stream.recv()
1118 );
1119 assert_eq!(diffs.len(), 1);
1120 assert_let!(VectorDiff::Clear = &diffs[0]);
1121
1122 assert_let_timeout!(
1124 Ok(RoomEventCacheGenericUpdate { room_id: received_room_id }) = generic_stream.recv()
1125 );
1126 assert_eq!(received_room_id, room_id);
1127 assert!(generic_stream.is_empty());
1128
1129 assert!(room_event_cache.find_event(event_id1).await.unwrap().is_none());
1131
1132 let items = room_event_cache.events().await.unwrap();
1134 assert!(items.is_empty());
1135
1136 assert!(
1138 event_cache_store
1139 .load_all_chunks(LinkedChunkId::Room(room_id))
1140 .await
1141 .unwrap()
1142 .is_empty()
1143 );
1144 }
1145
1146 #[async_test]
1147 async fn test_load_from_storage() {
1148 let room_id = room_id!("!galette:saucisse.bzh");
1149 let f = EventFactory::new().room(room_id).sender(user_id!("@ben:saucisse.bzh"));
1150
1151 let event_cache_store = Arc::new(MemoryStore::new());
1152
1153 let event_id1 = event_id!("$1");
1154 let event_id2 = event_id!("$2");
1155
1156 let ev1 = f.text_msg("hello world").sender(*ALICE).event_id(event_id1).into_event();
1157 let ev2 = f.text_msg("how's it going").sender(*BOB).event_id(event_id2).into_event();
1158
1159 event_cache_store
1161 .handle_linked_chunk_updates(
1162 LinkedChunkId::Room(room_id),
1163 vec![
1164 Update::NewItemsChunk {
1166 previous: None,
1167 new: ChunkIdentifier::new(0),
1168 next: None,
1169 },
1170 Update::NewGapChunk {
1172 previous: Some(ChunkIdentifier::new(0)),
1173 new: ChunkIdentifier::new(42),
1175 next: None,
1176 gap: Gap { token: "cheddar".to_owned() },
1177 },
1178 Update::NewItemsChunk {
1180 previous: Some(ChunkIdentifier::new(42)),
1181 new: ChunkIdentifier::new(1),
1182 next: None,
1183 },
1184 Update::PushItems {
1185 at: Position::new(ChunkIdentifier::new(1), 0),
1186 items: vec![ev1.clone()],
1187 },
1188 Update::NewItemsChunk {
1190 previous: Some(ChunkIdentifier::new(1)),
1191 new: ChunkIdentifier::new(2),
1192 next: None,
1193 },
1194 Update::PushItems {
1195 at: Position::new(ChunkIdentifier::new(2), 0),
1196 items: vec![ev2.clone()],
1197 },
1198 ],
1199 )
1200 .await
1201 .unwrap();
1202
1203 let client = MockClientBuilder::new(None)
1204 .on_builder(|builder| {
1205 builder.store_config(
1206 StoreConfig::new(CrossProcessLockConfig::multi_process("hodor"))
1207 .event_cache_store(event_cache_store.clone()),
1208 )
1209 })
1210 .build()
1211 .await;
1212
1213 let event_cache = client.event_cache();
1214
1215 event_cache.subscribe().unwrap();
1217
1218 let mut generic_stream = event_cache.subscribe_to_room_generic_updates();
1220
1221 client.base_client().get_or_create_room(room_id, RoomState::Joined);
1222 let room = client.get_room(room_id).unwrap();
1223
1224 let (room_event_cache, _drop_handles) = room.event_cache().await.unwrap();
1225
1226 assert_matches!(
1229 generic_stream.recv().await,
1230 Ok(RoomEventCacheGenericUpdate { room_id: expected_room_id }) => {
1231 assert_eq!(room_id, expected_room_id);
1232 }
1233 );
1234 assert!(generic_stream.is_empty());
1235
1236 let (items, mut stream) = room_event_cache.subscribe().await.unwrap();
1237
1238 assert_eq!(items.len(), 1);
1241 assert_eq!(items[0].event_id().unwrap(), event_id2);
1242 assert!(stream.is_empty());
1243
1244 assert!(room_event_cache.find_event(event_id1).await.unwrap().is_some());
1246 assert!(room_event_cache.find_event(event_id2).await.unwrap().is_some());
1247
1248 room_event_cache.pagination().run_backwards_once(20).await.unwrap();
1250
1251 assert_let_timeout!(
1252 Ok(RoomEventCacheUpdate::UpdateTimelineEvents(TimelineVectorDiffs { diffs, .. })) =
1253 stream.recv()
1254 );
1255 assert_eq!(diffs.len(), 1);
1256 assert_matches!(&diffs[0], VectorDiff::Insert { index: 0, value: event } => {
1257 assert_eq!(event.event_id().unwrap(), event_id1);
1258 });
1259
1260 assert!(stream.is_empty());
1261
1262 assert_matches!(
1264 generic_stream.recv().await,
1265 Ok(RoomEventCacheGenericUpdate { room_id: expected_room_id }) => {
1266 assert_eq!(expected_room_id, room_id);
1267 }
1268 );
1269 assert!(generic_stream.is_empty());
1270
1271 let timeline = Timeline { limited: false, prev_batch: None, events: vec![ev2] };
1273
1274 room_event_cache
1275 .handle_joined_room_update(
1276 timeline,
1277 MaybeReceiptEventContent::none(),
1278 Default::default(),
1279 Default::default(),
1280 Default::default(),
1281 )
1282 .await
1283 .unwrap();
1284
1285 assert!(generic_stream.recv().now_or_never().is_none());
1288
1289 let items = room_event_cache.events().await.unwrap();
1294 assert_eq!(items.len(), 2);
1295 assert_eq!(items[0].event_id().unwrap(), event_id1);
1296 assert_eq!(items[1].event_id().unwrap(), event_id2);
1297 }
1298
1299 #[async_test]
1300 async fn test_load_from_storage_resilient_to_failure() {
1301 let room_id = room_id!("!fondue:patate.ch");
1302 let event_cache_store = Arc::new(MemoryStore::new());
1303
1304 let event = EventFactory::new()
1305 .room(room_id)
1306 .sender(user_id!("@ben:saucisse.bzh"))
1307 .text_msg("foo")
1308 .event_id(event_id!("$42"))
1309 .into_event();
1310
1311 event_cache_store
1313 .handle_linked_chunk_updates(
1314 LinkedChunkId::Room(room_id),
1315 vec![
1316 Update::NewItemsChunk {
1317 previous: None,
1318 new: ChunkIdentifier::new(0),
1319 next: None,
1320 },
1321 Update::PushItems {
1322 at: Position::new(ChunkIdentifier::new(0), 0),
1323 items: vec![event],
1324 },
1325 Update::NewItemsChunk {
1326 previous: Some(ChunkIdentifier::new(0)),
1327 new: ChunkIdentifier::new(1),
1328 next: Some(ChunkIdentifier::new(0)),
1329 },
1330 ],
1331 )
1332 .await
1333 .unwrap();
1334
1335 let client = MockClientBuilder::new(None)
1336 .on_builder(|builder| {
1337 builder.store_config(
1338 StoreConfig::new(CrossProcessLockConfig::multi_process("holder"))
1339 .event_cache_store(event_cache_store.clone()),
1340 )
1341 })
1342 .build()
1343 .await;
1344
1345 let event_cache = client.event_cache();
1346
1347 event_cache.subscribe().unwrap();
1349
1350 client.base_client().get_or_create_room(room_id, RoomState::Joined);
1351 let room = client.get_room(room_id).unwrap();
1352
1353 let (room_event_cache, _drop_handles) = room.event_cache().await.unwrap();
1354
1355 let items = room_event_cache.events().await.unwrap();
1356
1357 assert!(items.is_empty());
1360
1361 let raw_chunks =
1364 event_cache_store.load_all_chunks(LinkedChunkId::Room(room_id)).await.unwrap();
1365 assert!(raw_chunks.is_empty());
1366 }
1367
1368 #[async_test]
1369 async fn test_no_useless_gaps() {
1370 let room_id = room_id!("!galette:saucisse.bzh");
1371
1372 let client = MockClientBuilder::new(None).build().await;
1373
1374 let event_cache = client.event_cache();
1375 event_cache.subscribe().unwrap();
1376
1377 client.base_client().get_or_create_room(room_id, RoomState::Joined);
1378 let room = client.get_room(room_id).unwrap();
1379 let (room_event_cache, _drop_handles) = room.event_cache().await.unwrap();
1380 let mut generic_stream = event_cache.subscribe_to_room_generic_updates();
1381
1382 let f = EventFactory::new().room(room_id).sender(*ALICE);
1383
1384 room_event_cache
1387 .handle_joined_room_update(
1388 Timeline {
1389 limited: true,
1390 prev_batch: Some("raclette".to_owned()),
1391 events: vec![f.text_msg("hey yo").into_event()],
1392 },
1393 MaybeReceiptEventContent::none(),
1394 Default::default(),
1395 Default::default(),
1396 Default::default(),
1397 )
1398 .await
1399 .unwrap();
1400
1401 assert_matches!(
1403 generic_stream.recv().await,
1404 Ok(RoomEventCacheGenericUpdate { room_id: expected_room_id }) => {
1405 assert_eq!(expected_room_id, room_id);
1406 }
1407 );
1408 assert!(generic_stream.is_empty());
1409
1410 {
1411 let state = room_event_cache.inner.state.read().await.unwrap();
1412
1413 let mut num_gaps = 0;
1414 let mut num_events = 0;
1415
1416 for c in state.room_linked_chunk().chunks() {
1417 match c.content() {
1418 ChunkContent::Items(items) => num_events += items.len(),
1419 ChunkContent::Gap(_) => num_gaps += 1,
1420 }
1421 }
1422
1423 assert_eq!(num_gaps, 0);
1426 assert_eq!(num_events, 1);
1427 }
1428
1429 assert_matches!(
1431 room_event_cache.pagination().load_more_events_backwards().await.unwrap(),
1432 LoadMoreEventsBackwardsOutcome::Gap { .. }
1433 );
1434
1435 {
1436 let state = room_event_cache.inner.state.read().await.unwrap();
1437
1438 let mut num_gaps = 0;
1439 let mut num_events = 0;
1440
1441 for c in state.room_linked_chunk().chunks() {
1442 match c.content() {
1443 ChunkContent::Items(items) => num_events += items.len(),
1444 ChunkContent::Gap(_) => num_gaps += 1,
1445 }
1446 }
1447
1448 assert_eq!(num_gaps, 1);
1450 assert_eq!(num_events, 1);
1451 }
1452
1453 room_event_cache
1456 .handle_joined_room_update(
1457 Timeline {
1458 limited: false,
1459 prev_batch: Some("fondue".to_owned()),
1460 events: vec![f.text_msg("sup").into_event()],
1461 },
1462 MaybeReceiptEventContent::none(),
1463 Default::default(),
1464 Default::default(),
1465 Default::default(),
1466 )
1467 .await
1468 .unwrap();
1469
1470 assert_matches!(
1472 generic_stream.recv().await,
1473 Ok(RoomEventCacheGenericUpdate { room_id: expected_room_id }) => {
1474 assert_eq!(expected_room_id, room_id);
1475 }
1476 );
1477 assert!(generic_stream.is_empty());
1478
1479 {
1480 let state = room_event_cache.inner.state.read().await.unwrap();
1481
1482 let mut num_gaps = 0;
1483 let mut num_events = 0;
1484
1485 for c in state.room_linked_chunk().chunks() {
1486 match c.content() {
1487 ChunkContent::Items(items) => num_events += items.len(),
1488 ChunkContent::Gap(gap) => {
1489 assert_eq!(gap.token, "raclette");
1490 num_gaps += 1;
1491 }
1492 }
1493 }
1494
1495 assert_eq!(num_gaps, 1);
1497 assert_eq!(num_events, 2);
1498 }
1499 }
1500
1501 #[async_test]
1502 async fn test_shrink_to_last_chunk() {
1503 let room_id = room_id!("!galette:saucisse.bzh");
1504
1505 let client = MockClientBuilder::new(None).build().await;
1506
1507 let f = EventFactory::new().room(room_id);
1508
1509 let evid1 = event_id!("$1");
1510 let evid2 = event_id!("$2");
1511
1512 let ev1 = f.text_msg("hello world").sender(*ALICE).event_id(evid1).into_event();
1513 let ev2 = f.text_msg("howdy").sender(*BOB).event_id(evid2).into_event();
1514
1515 {
1517 client
1518 .event_cache_store()
1519 .lock()
1520 .await
1521 .expect("Could not acquire the event cache lock")
1522 .as_clean()
1523 .expect("Could not acquire a clean event cache lock")
1524 .handle_linked_chunk_updates(
1525 LinkedChunkId::Room(room_id),
1526 vec![
1527 Update::NewItemsChunk {
1528 previous: None,
1529 new: ChunkIdentifier::new(0),
1530 next: None,
1531 },
1532 Update::PushItems {
1533 at: Position::new(ChunkIdentifier::new(0), 0),
1534 items: vec![ev1],
1535 },
1536 Update::NewItemsChunk {
1537 previous: Some(ChunkIdentifier::new(0)),
1538 new: ChunkIdentifier::new(1),
1539 next: None,
1540 },
1541 Update::PushItems {
1542 at: Position::new(ChunkIdentifier::new(1), 0),
1543 items: vec![ev2],
1544 },
1545 ],
1546 )
1547 .await
1548 .unwrap();
1549 }
1550
1551 let event_cache = client.event_cache();
1552 event_cache.subscribe().unwrap();
1553
1554 client.base_client().get_or_create_room(room_id, RoomState::Joined);
1555 let room = client.get_room(room_id).unwrap();
1556 let (room_event_cache, _drop_handles) = room.event_cache().await.unwrap();
1557
1558 let (events, mut stream) = room_event_cache.subscribe().await.unwrap();
1560 assert_eq!(events.len(), 1);
1561 assert_eq!(events[0].event_id(), Some(evid2));
1562 assert!(stream.is_empty());
1563
1564 let mut generic_stream = event_cache.subscribe_to_room_generic_updates();
1565
1566 let outcome = room_event_cache.pagination().run_backwards_once(20).await.unwrap();
1568 assert_eq!(outcome.events.len(), 1);
1569 assert_eq!(outcome.events[0].event_id(), Some(evid1));
1570 assert!(outcome.reached_start);
1571
1572 assert_let_timeout!(
1574 Ok(RoomEventCacheUpdate::UpdateTimelineEvents(TimelineVectorDiffs { diffs, .. })) =
1575 stream.recv()
1576 );
1577 assert_eq!(diffs.len(), 1);
1578 assert_matches!(&diffs[0], VectorDiff::Insert { index: 0, value } => {
1579 assert_eq!(value.event_id(), Some(evid1));
1580 });
1581
1582 assert!(stream.is_empty());
1583
1584 assert_let_timeout!(
1586 Ok(RoomEventCacheGenericUpdate { room_id: expected_room_id }) = generic_stream.recv()
1587 );
1588 assert_eq!(expected_room_id, room_id);
1589 assert!(generic_stream.is_empty());
1590
1591 room_event_cache
1593 .inner
1594 .state
1595 .reload_no_preprocessing()
1596 .await
1597 .expect("shrinking should succeed");
1598
1599 assert_let_timeout!(
1601 Ok(RoomEventCacheUpdate::UpdateTimelineEvents(TimelineVectorDiffs { diffs, .. })) =
1602 stream.recv()
1603 );
1604 assert_eq!(diffs.len(), 2);
1605 assert_matches!(&diffs[0], VectorDiff::Clear);
1606 assert_matches!(&diffs[1], VectorDiff::Append { values} => {
1607 assert_eq!(values.len(), 1);
1608 assert_eq!(values[0].event_id(), Some(evid2));
1609 });
1610
1611 assert!(stream.is_empty());
1612
1613 assert_let_timeout!(Ok(RoomEventCacheGenericUpdate { .. }) = generic_stream.recv());
1615 assert!(generic_stream.is_empty());
1616
1617 let events = room_event_cache.events().await.unwrap();
1619 assert_eq!(events.len(), 1);
1620 assert_eq!(events[0].event_id(), Some(evid2));
1621
1622 let outcome = room_event_cache.pagination().run_backwards_once(20).await.unwrap();
1625 assert_eq!(outcome.events.len(), 1);
1626 assert_eq!(outcome.events[0].event_id(), Some(evid1));
1627 assert!(outcome.reached_start);
1628 }
1629
1630 #[async_test]
1631 async fn test_room_ordering() {
1632 let room_id = room_id!("!galette:saucisse.bzh");
1633
1634 let client = MockClientBuilder::new(None).build().await;
1635
1636 let f = EventFactory::new().room(room_id).sender(*ALICE);
1637
1638 let evid1 = event_id!("$1");
1639 let evid2 = event_id!("$2");
1640 let evid3 = event_id!("$3");
1641
1642 let ev1 = f.text_msg("hello world").event_id(evid1).into_event();
1643 let ev2 = f.text_msg("howdy").sender(*BOB).event_id(evid2).into_event();
1644 let ev3 = f.text_msg("yo").event_id(evid3).into_event();
1645
1646 {
1648 client
1649 .event_cache_store()
1650 .lock()
1651 .await
1652 .expect("Could not acquire the event cache lock")
1653 .as_clean()
1654 .expect("Could not acquire a clean event cache lock")
1655 .handle_linked_chunk_updates(
1656 LinkedChunkId::Room(room_id),
1657 vec![
1658 Update::NewItemsChunk {
1659 previous: None,
1660 new: ChunkIdentifier::new(0),
1661 next: None,
1662 },
1663 Update::PushItems {
1664 at: Position::new(ChunkIdentifier::new(0), 0),
1665 items: vec![ev1, ev2],
1666 },
1667 Update::NewItemsChunk {
1668 previous: Some(ChunkIdentifier::new(0)),
1669 new: ChunkIdentifier::new(1),
1670 next: None,
1671 },
1672 Update::PushItems {
1673 at: Position::new(ChunkIdentifier::new(1), 0),
1674 items: vec![ev3.clone()],
1675 },
1676 ],
1677 )
1678 .await
1679 .unwrap();
1680 }
1681
1682 let event_cache = client.event_cache();
1683 event_cache.subscribe().unwrap();
1684
1685 client.base_client().get_or_create_room(room_id, RoomState::Joined);
1686 let room = client.get_room(room_id).unwrap();
1687 let (room_event_cache, _drop_handles) = room.event_cache().await.unwrap();
1688
1689 {
1692 let state = room_event_cache.inner.state.read().await.unwrap();
1693 let room_linked_chunk = state.room_linked_chunk();
1694
1695 assert_eq!(
1697 room_linked_chunk.event_order(Position::new(ChunkIdentifier::new(0), 0)),
1698 Some(0)
1699 );
1700
1701 assert_eq!(
1703 room_linked_chunk.event_order(Position::new(ChunkIdentifier::new(0), 1)),
1704 Some(1)
1705 );
1706
1707 let mut events = room_linked_chunk.events();
1709 let (pos, ev) = events.next().unwrap();
1710 assert_eq!(pos, Position::new(ChunkIdentifier::new(1), 0));
1711 assert_eq!(ev.event_id(), Some(evid3));
1712 assert_eq!(room_linked_chunk.event_order(pos), Some(2));
1713
1714 assert!(events.next().is_none());
1716 }
1717
1718 let outcome = room_event_cache.pagination().run_backwards_once(20).await.unwrap();
1720 assert!(outcome.reached_start);
1721
1722 {
1725 let state = room_event_cache.inner.state.read().await.unwrap();
1726 let room_linked_chunk = state.room_linked_chunk();
1727
1728 for (i, (pos, _)) in room_linked_chunk.events().enumerate() {
1729 assert_eq!(room_linked_chunk.event_order(pos), Some(i));
1730 }
1731 }
1732
1733 let evid4 = event_id!("$4");
1738 room_event_cache
1739 .handle_joined_room_update(
1740 Timeline {
1741 limited: true,
1742 prev_batch: Some("fondue".to_owned()),
1743 events: vec![ev3, f.text_msg("sup").event_id(evid4).into_event()],
1744 },
1745 MaybeReceiptEventContent::none(),
1746 Default::default(),
1747 Default::default(),
1748 Default::default(),
1749 )
1750 .await
1751 .unwrap();
1752
1753 {
1754 let state = room_event_cache.inner.state.read().await.unwrap();
1755 let room_linked_chunk = state.room_linked_chunk();
1756
1757 let mut events = room_linked_chunk.events();
1759
1760 let (pos, ev) = events.next().unwrap();
1761 assert_eq!(ev.event_id(), Some(evid3));
1762 assert_eq!(room_linked_chunk.event_order(pos), Some(2));
1763
1764 let (pos, ev) = events.next().unwrap();
1765 assert_eq!(ev.event_id(), Some(evid4));
1766 assert_eq!(room_linked_chunk.event_order(pos), Some(3));
1767
1768 assert!(events.next().is_none());
1770
1771 assert_eq!(
1773 room_linked_chunk.event_order(Position::new(ChunkIdentifier::new(0), 0)),
1774 Some(0)
1775 );
1776 assert_eq!(
1777 room_linked_chunk.event_order(Position::new(ChunkIdentifier::new(0), 1)),
1778 Some(1)
1779 );
1780
1781 assert_eq!(
1784 room_linked_chunk.event_order(Position::new(ChunkIdentifier::new(1), 0)),
1785 None
1786 );
1787 }
1788 }
1789
1790 #[async_test]
1791 async fn test_auto_shrink_after_all_subscribers_are_gone() {
1792 let room_id = room_id!("!galette:saucisse.bzh");
1793
1794 let client = MockClientBuilder::new(None).build().await;
1795
1796 let f = EventFactory::new().room(room_id);
1797
1798 let evid1 = event_id!("$1");
1799 let evid2 = event_id!("$2");
1800
1801 let ev1 = f.text_msg("hello world").sender(*ALICE).event_id(evid1).into_event();
1802 let ev2 = f.text_msg("howdy").sender(*BOB).event_id(evid2).into_event();
1803
1804 {
1806 client
1807 .event_cache_store()
1808 .lock()
1809 .await
1810 .expect("Could not acquire the event cache lock")
1811 .as_clean()
1812 .expect("Could not acquire a clean event cache lock")
1813 .handle_linked_chunk_updates(
1814 LinkedChunkId::Room(room_id),
1815 vec![
1816 Update::NewItemsChunk {
1817 previous: None,
1818 new: ChunkIdentifier::new(0),
1819 next: None,
1820 },
1821 Update::PushItems {
1822 at: Position::new(ChunkIdentifier::new(0), 0),
1823 items: vec![ev1],
1824 },
1825 Update::NewItemsChunk {
1826 previous: Some(ChunkIdentifier::new(0)),
1827 new: ChunkIdentifier::new(1),
1828 next: None,
1829 },
1830 Update::PushItems {
1831 at: Position::new(ChunkIdentifier::new(1), 0),
1832 items: vec![ev2],
1833 },
1834 ],
1835 )
1836 .await
1837 .unwrap();
1838 }
1839
1840 let event_cache = client.event_cache();
1841 event_cache.subscribe().unwrap();
1842
1843 client.base_client().get_or_create_room(room_id, RoomState::Joined);
1844 let room = client.get_room(room_id).unwrap();
1845 let (room_event_cache, _drop_handles) = room.event_cache().await.unwrap();
1846
1847 let (events1, mut stream1) = room_event_cache.subscribe().await.unwrap();
1849 assert_eq!(events1.len(), 1);
1850 assert_eq!(events1[0].event_id(), Some(evid2));
1851 assert!(stream1.is_empty());
1852
1853 let mut generic_stream = event_cache.subscribe_to_room_generic_updates();
1854
1855 let outcome = room_event_cache.pagination().run_backwards_once(20).await.unwrap();
1857 assert_eq!(outcome.events.len(), 1);
1858 assert_eq!(outcome.events[0].event_id(), Some(evid1));
1859 assert!(outcome.reached_start);
1860
1861 assert_let_timeout!(
1864 Ok(RoomEventCacheUpdate::UpdateTimelineEvents(TimelineVectorDiffs { diffs, .. })) =
1865 stream1.recv()
1866 );
1867 assert_eq!(diffs.len(), 1);
1868 assert_matches!(&diffs[0], VectorDiff::Insert { index: 0, value } => {
1869 assert_eq!(value.event_id(), Some(evid1));
1870 });
1871
1872 assert!(stream1.is_empty());
1873
1874 assert_let_timeout!(
1875 Ok(RoomEventCacheGenericUpdate { room_id: expected_room_id }) = generic_stream.recv()
1876 );
1877 assert_eq!(expected_room_id, room_id);
1878 assert!(generic_stream.is_empty());
1879
1880 let (events2, stream2) = room_event_cache.subscribe().await.unwrap();
1884 assert_eq!(events2.len(), 2);
1885 assert_eq!(events2[0].event_id(), Some(evid1));
1886 assert_eq!(events2[1].event_id(), Some(evid2));
1887 assert!(stream2.is_empty());
1888
1889 let subscriber = {
1891 let state = room_event_cache.inner.state.read().await.unwrap();
1892 state.update_sender.new_room_receiver()
1893 };
1894
1895 drop(stream1);
1897 yield_now().await;
1898
1899 assert!(stream2.is_empty());
1901
1902 drop(stream2);
1904 yield_now().await;
1905
1906 {
1909 let state = room_event_cache.inner.state.read().await.unwrap();
1911 assert_eq!(state.subscribers_handle().count(), 0);
1912
1913 assert!(subscriber.is_empty());
1915 assert!(generic_stream.is_empty());
1916 }
1917
1918 let events3 = room_event_cache.events().await.unwrap();
1920 assert_eq!(events3.len(), 1);
1921 assert_eq!(events3[0].event_id(), Some(evid2));
1922 }
1923
1924 #[async_test]
1925 async fn test_rfind_map_event_in_memory_by() {
1926 let user_id = user_id!("@mnt_io:matrix.org");
1927 let room_id = room_id!("!raclette:patate.ch");
1928 let client = MockClientBuilder::new(None).build().await;
1929
1930 let event_factory = EventFactory::new().room(room_id);
1931
1932 let event_id_0 = event_id!("$ev0");
1933 let event_id_1 = event_id!("$ev1");
1934 let event_id_2 = event_id!("$ev2");
1935 let event_id_3 = event_id!("$ev3");
1936
1937 let event_0 =
1938 event_factory.text_msg("hello").sender(*BOB).event_id(event_id_0).into_event();
1939 let event_1 =
1940 event_factory.text_msg("world").sender(*ALICE).event_id(event_id_1).into_event();
1941 let event_2 = event_factory.text_msg("!").sender(*ALICE).event_id(event_id_2).into_event();
1942 let event_3 =
1943 event_factory.text_msg("eh!").sender(user_id).event_id(event_id_3).into_event();
1944
1945 {
1948 client
1949 .event_cache_store()
1950 .lock()
1951 .await
1952 .expect("Could not acquire the event cache lock")
1953 .as_clean()
1954 .expect("Could not acquire a clean event cache lock")
1955 .handle_linked_chunk_updates(
1956 LinkedChunkId::Room(room_id),
1957 vec![
1958 Update::NewItemsChunk {
1959 previous: None,
1960 new: ChunkIdentifier::new(0),
1961 next: None,
1962 },
1963 Update::PushItems {
1964 at: Position::new(ChunkIdentifier::new(0), 0),
1965 items: vec![event_3],
1966 },
1967 Update::NewItemsChunk {
1968 previous: Some(ChunkIdentifier::new(0)),
1969 new: ChunkIdentifier::new(1),
1970 next: None,
1971 },
1972 Update::PushItems {
1973 at: Position::new(ChunkIdentifier::new(1), 0),
1974 items: vec![event_0, event_1, event_2],
1975 },
1976 ],
1977 )
1978 .await
1979 .unwrap();
1980 }
1981
1982 let event_cache = client.event_cache();
1983 event_cache.subscribe().unwrap();
1984
1985 client.base_client().get_or_create_room(room_id, RoomState::Joined);
1986 let room = client.get_room(room_id).unwrap();
1987 let (room_event_cache, _drop_handles) = room.event_cache().await.unwrap();
1988
1989 assert_matches!(
1991 room_event_cache
1992 .rfind_map_event_in_memory_by(|event| {
1993 (event.sender().as_deref() == Some(*BOB)).then(|| event.event_id().map(ToOwned::to_owned))
1994 })
1995 .await,
1996 Ok(Some(event_id)) => {
1997 assert_eq!(event_id.as_deref(), Some(event_id_0));
1998 }
1999 );
2000
2001 assert_matches!(
2004 room_event_cache
2005 .rfind_map_event_in_memory_by(|event| {
2006 (event.sender().as_deref() == Some(*ALICE)).then(|| event.event_id().map(ToOwned::to_owned))
2007 })
2008 .await,
2009 Ok(Some(event_id)) => {
2010 assert_eq!(event_id.as_deref(), Some(event_id_2));
2011 }
2012 );
2013
2014 assert!(
2016 room_event_cache
2017 .rfind_map_event_in_memory_by(|event| {
2018 (event.sender().as_deref() == Some(user_id))
2019 .then(|| event.event_id().map(ToOwned::to_owned))
2020 })
2021 .await
2022 .unwrap()
2023 .is_none()
2024 );
2025
2026 assert!(
2028 room_event_cache.rfind_map_event_in_memory_by(|_| None::<()>).await.unwrap().is_none()
2029 );
2030 }
2031
2032 #[async_test]
2033 async fn test_reload_when_dirty() {
2034 let user_id = user_id!("@mnt_io:matrix.org");
2035 let room_id = room_id!("!raclette:patate.ch");
2036
2037 let event_cache_store = MemoryStore::new();
2039
2040 let client_p0 = MockClientBuilder::new(None)
2042 .on_builder(|builder| {
2043 builder.store_config(
2044 StoreConfig::new(CrossProcessLockConfig::multi_process("process #0"))
2045 .event_cache_store(event_cache_store.clone()),
2046 )
2047 })
2048 .build()
2049 .await;
2050
2051 let client_p1 = MockClientBuilder::new(None)
2053 .on_builder(|builder| {
2054 builder.store_config(
2055 StoreConfig::new(CrossProcessLockConfig::multi_process("process #1"))
2056 .event_cache_store(event_cache_store),
2057 )
2058 })
2059 .build()
2060 .await;
2061
2062 let event_factory = EventFactory::new().room(room_id).sender(user_id);
2063
2064 let ev_id_0 = event_id!("$ev_0");
2065 let ev_id_1 = event_id!("$ev_1");
2066
2067 let ev_0 = event_factory.text_msg("comté").event_id(ev_id_0).into_event();
2068 let ev_1 = event_factory.text_msg("morbier").event_id(ev_id_1).into_event();
2069
2070 client_p0
2072 .event_cache_store()
2073 .lock()
2074 .await
2075 .expect("[p0] Could not acquire the event cache lock")
2076 .as_clean()
2077 .expect("[p0] Could not acquire a clean event cache lock")
2078 .handle_linked_chunk_updates(
2079 LinkedChunkId::Room(room_id),
2080 vec![
2081 Update::NewItemsChunk {
2082 previous: None,
2083 new: ChunkIdentifier::new(0),
2084 next: None,
2085 },
2086 Update::PushItems {
2087 at: Position::new(ChunkIdentifier::new(0), 0),
2088 items: vec![ev_0],
2089 },
2090 Update::NewItemsChunk {
2091 previous: Some(ChunkIdentifier::new(0)),
2092 new: ChunkIdentifier::new(1),
2093 next: None,
2094 },
2095 Update::PushItems {
2096 at: Position::new(ChunkIdentifier::new(1), 0),
2097 items: vec![ev_1],
2098 },
2099 ],
2100 )
2101 .await
2102 .unwrap();
2103
2104 let (room_event_cache_p0, room_event_cache_p1) = {
2106 let event_cache_p0 = client_p0.event_cache();
2107 event_cache_p0.subscribe().unwrap();
2108
2109 let event_cache_p1 = client_p1.event_cache();
2110 event_cache_p1.subscribe().unwrap();
2111
2112 client_p0.base_client().get_or_create_room(room_id, RoomState::Joined);
2113 client_p1.base_client().get_or_create_room(room_id, RoomState::Joined);
2114
2115 let (room_event_cache_p0, _drop_handles) =
2116 client_p0.get_room(room_id).unwrap().event_cache().await.unwrap();
2117 let (room_event_cache_p1, _drop_handles) =
2118 client_p1.get_room(room_id).unwrap().event_cache().await.unwrap();
2119
2120 (room_event_cache_p0, room_event_cache_p1)
2121 };
2122
2123 let mut updates_stream_p0 = {
2128 let room_event_cache = &room_event_cache_p0;
2129
2130 let (initial_updates, mut updates_stream) =
2131 room_event_cache_p0.subscribe().await.unwrap();
2132
2133 assert_eq!(initial_updates.len(), 1);
2135 assert_eq!(initial_updates[0].event_id(), Some(ev_id_1));
2136 assert!(updates_stream.is_empty());
2137
2138 assert!(event_loaded(room_event_cache, ev_id_1).await);
2140
2141 assert!(event_loaded(room_event_cache, ev_id_0).await.not());
2143
2144 room_event_cache.pagination().run_backwards_once(1).await.unwrap();
2146
2147 assert_matches!(
2149 updates_stream.recv().await.unwrap(),
2150 RoomEventCacheUpdate::UpdateTimelineEvents(TimelineVectorDiffs { diffs, .. }) => {
2151 assert_eq!(diffs.len(), 1, "{diffs:#?}");
2152 assert_matches!(
2153 &diffs[0],
2154 VectorDiff::Insert { index: 0, value: event } => {
2155 assert_eq!(event.event_id(), Some(ev_id_0));
2156 }
2157 );
2158 }
2159 );
2160
2161 assert!(event_loaded(room_event_cache, ev_id_0).await);
2163
2164 updates_stream
2165 };
2166
2167 let mut updates_stream_p1 = {
2169 let room_event_cache = &room_event_cache_p1;
2170 let (initial_updates, mut updates_stream) =
2171 room_event_cache_p1.subscribe().await.unwrap();
2172
2173 assert_eq!(initial_updates.len(), 1);
2175 assert_eq!(initial_updates[0].event_id(), Some(ev_id_1));
2176 assert!(updates_stream.is_empty());
2177
2178 assert!(event_loaded(room_event_cache, ev_id_1).await);
2180
2181 assert!(event_loaded(room_event_cache, ev_id_0).await.not());
2183
2184 room_event_cache.pagination().run_backwards_once(1).await.unwrap();
2186
2187 assert_matches!(
2189 updates_stream.recv().await.unwrap(),
2190 RoomEventCacheUpdate::UpdateTimelineEvents(TimelineVectorDiffs { diffs, .. }) => {
2191 assert_eq!(diffs.len(), 1, "{diffs:#?}");
2192 assert_matches!(
2193 &diffs[0],
2194 VectorDiff::Insert { index: 0, value: event } => {
2195 assert_eq!(event.event_id(), Some(ev_id_0));
2196 }
2197 );
2198 }
2199 );
2200
2201 assert!(event_loaded(room_event_cache, ev_id_0).await);
2203
2204 updates_stream
2205 };
2206
2207 for _ in 0..3 {
2209 {
2213 let room_event_cache = &room_event_cache_p0;
2214 let updates_stream = &mut updates_stream_p0;
2215
2216 assert!(event_loaded(room_event_cache, ev_id_1).await);
2218
2219 assert!(event_loaded(room_event_cache, ev_id_0).await.not());
2222
2223 assert_matches!(
2225 updates_stream.recv().await.unwrap(),
2226 RoomEventCacheUpdate::UpdateTimelineEvents(TimelineVectorDiffs { diffs, .. }) => {
2227 assert_eq!(diffs.len(), 2, "{diffs:#?}");
2228 assert_matches!(&diffs[0], VectorDiff::Clear);
2229 assert_matches!(
2230 &diffs[1],
2231 VectorDiff::Append { values: events } => {
2232 assert_eq!(events.len(), 1);
2233 assert_eq!(events[0].event_id(), Some(ev_id_1));
2234 }
2235 );
2236 }
2237 );
2238
2239 room_event_cache.pagination().run_backwards_once(1).await.unwrap();
2241
2242 assert!(event_loaded(room_event_cache, ev_id_0).await);
2244
2245 assert_matches!(
2247 updates_stream.recv().await.unwrap(),
2248 RoomEventCacheUpdate::UpdateTimelineEvents(TimelineVectorDiffs { diffs, .. }) => {
2249 assert_eq!(diffs.len(), 1, "{diffs:#?}");
2250 assert_matches!(
2251 &diffs[0],
2252 VectorDiff::Insert { index: 0, value: event } => {
2253 assert_eq!(event.event_id(), Some(ev_id_0));
2254 }
2255 );
2256 }
2257 );
2258 }
2259
2260 {
2264 let room_event_cache = &room_event_cache_p1;
2265 let updates_stream = &mut updates_stream_p1;
2266
2267 assert!(event_loaded(room_event_cache, ev_id_1).await);
2269
2270 assert!(event_loaded(room_event_cache, ev_id_0).await.not());
2273
2274 assert_matches!(
2276 updates_stream.recv().await.unwrap(),
2277 RoomEventCacheUpdate::UpdateTimelineEvents(TimelineVectorDiffs { diffs, .. }) => {
2278 assert_eq!(diffs.len(), 2, "{diffs:#?}");
2279 assert_matches!(&diffs[0], VectorDiff::Clear);
2280 assert_matches!(
2281 &diffs[1],
2282 VectorDiff::Append { values: events } => {
2283 assert_eq!(events.len(), 1);
2284 assert_eq!(events[0].event_id(), Some(ev_id_1));
2285 }
2286 );
2287 }
2288 );
2289
2290 room_event_cache.pagination().run_backwards_once(1).await.unwrap();
2292
2293 assert!(event_loaded(room_event_cache, ev_id_0).await);
2295
2296 assert_matches!(
2298 updates_stream.recv().await.unwrap(),
2299 RoomEventCacheUpdate::UpdateTimelineEvents(TimelineVectorDiffs { diffs, .. }) => {
2300 assert_eq!(diffs.len(), 1, "{diffs:#?}");
2301 assert_matches!(
2302 &diffs[0],
2303 VectorDiff::Insert { index: 0, value: event } => {
2304 assert_eq!(event.event_id(), Some(ev_id_0));
2305 }
2306 );
2307 }
2308 );
2309 }
2310 }
2311
2312 for _ in 0..3 {
2315 {
2316 let room_event_cache = &room_event_cache_p0;
2317 let updates_stream = &mut updates_stream_p0;
2318
2319 let guard = room_event_cache.inner.state.read().await.unwrap();
2320
2321 assert!(guard.is_dirty().not());
2327
2328 assert_matches!(
2330 updates_stream.recv().await.unwrap(),
2331 RoomEventCacheUpdate::UpdateTimelineEvents(TimelineVectorDiffs { diffs, .. }) => {
2332 assert_eq!(diffs.len(), 2, "{diffs:#?}");
2333 assert_matches!(&diffs[0], VectorDiff::Clear);
2334 assert_matches!(
2335 &diffs[1],
2336 VectorDiff::Append { values: events } => {
2337 assert_eq!(events.len(), 1);
2338 assert_eq!(events[0].event_id(), Some(ev_id_1));
2339 }
2340 );
2341 }
2342 );
2343
2344 assert!(event_loaded(room_event_cache, ev_id_1).await);
2345 assert!(event_loaded(room_event_cache, ev_id_0).await.not());
2346
2347 drop(guard);
2353
2354 room_event_cache.pagination().run_backwards_once(1).await.unwrap();
2355 assert!(event_loaded(room_event_cache, ev_id_0).await);
2356
2357 assert_matches!(
2359 updates_stream.recv().await.unwrap(),
2360 RoomEventCacheUpdate::UpdateTimelineEvents(TimelineVectorDiffs { diffs, .. }) => {
2361 assert_eq!(diffs.len(), 1, "{diffs:#?}");
2362 assert_matches!(
2363 &diffs[0],
2364 VectorDiff::Insert { index: 0, value: event } => {
2365 assert_eq!(event.event_id(), Some(ev_id_0));
2366 }
2367 );
2368 }
2369 );
2370 }
2371
2372 {
2373 let room_event_cache = &room_event_cache_p1;
2374 let updates_stream = &mut updates_stream_p1;
2375
2376 let guard = room_event_cache.inner.state.read().await.unwrap();
2377
2378 assert!(guard.is_dirty().not());
2383
2384 assert_matches!(
2386 updates_stream.recv().await.unwrap(),
2387 RoomEventCacheUpdate::UpdateTimelineEvents(TimelineVectorDiffs { diffs, .. }) => {
2388 assert_eq!(diffs.len(), 2, "{diffs:#?}");
2389 assert_matches!(&diffs[0], VectorDiff::Clear);
2390 assert_matches!(
2391 &diffs[1],
2392 VectorDiff::Append { values: events } => {
2393 assert_eq!(events.len(), 1);
2394 assert_eq!(events[0].event_id(), Some(ev_id_1));
2395 }
2396 );
2397 }
2398 );
2399
2400 assert!(event_loaded(room_event_cache, ev_id_1).await);
2401 assert!(event_loaded(room_event_cache, ev_id_0).await.not());
2402
2403 drop(guard);
2409
2410 room_event_cache.pagination().run_backwards_once(1).await.unwrap();
2411 assert!(event_loaded(room_event_cache, ev_id_0).await);
2412
2413 assert_matches!(
2415 updates_stream.recv().await.unwrap(),
2416 RoomEventCacheUpdate::UpdateTimelineEvents(TimelineVectorDiffs { diffs, .. }) => {
2417 assert_eq!(diffs.len(), 1, "{diffs:#?}");
2418 assert_matches!(
2419 &diffs[0],
2420 VectorDiff::Insert { index: 0, value: event } => {
2421 assert_eq!(event.event_id(), Some(ev_id_0));
2422 }
2423 );
2424 }
2425 );
2426 }
2427 }
2428
2429 for _ in 0..3 {
2431 {
2432 let room_event_cache = &room_event_cache_p0;
2433 let updates_stream = &mut updates_stream_p0;
2434
2435 let guard = room_event_cache.inner.state.write().await.unwrap();
2436
2437 assert!(guard.is_dirty().not());
2439
2440 assert_matches!(
2442 updates_stream.recv().await.unwrap(),
2443 RoomEventCacheUpdate::UpdateTimelineEvents(TimelineVectorDiffs { diffs, .. }) => {
2444 assert_eq!(diffs.len(), 2, "{diffs:#?}");
2445 assert_matches!(&diffs[0], VectorDiff::Clear);
2446 assert_matches!(
2447 &diffs[1],
2448 VectorDiff::Append { values: events } => {
2449 assert_eq!(events.len(), 1);
2450 assert_eq!(events[0].event_id(), Some(ev_id_1));
2451 }
2452 );
2453 }
2454 );
2455
2456 drop(guard);
2459
2460 assert!(event_loaded(room_event_cache, ev_id_1).await);
2461 assert!(event_loaded(room_event_cache, ev_id_0).await.not());
2462
2463 room_event_cache.pagination().run_backwards_once(1).await.unwrap();
2464 assert!(event_loaded(room_event_cache, ev_id_0).await);
2465
2466 assert_matches!(
2468 updates_stream.recv().await.unwrap(),
2469 RoomEventCacheUpdate::UpdateTimelineEvents(TimelineVectorDiffs { diffs, .. }) => {
2470 assert_eq!(diffs.len(), 1, "{diffs:#?}");
2471 assert_matches!(
2472 &diffs[0],
2473 VectorDiff::Insert { index: 0, value: event } => {
2474 assert_eq!(event.event_id(), Some(ev_id_0));
2475 }
2476 );
2477 }
2478 );
2479 }
2480
2481 {
2482 let room_event_cache = &room_event_cache_p1;
2483 let updates_stream = &mut updates_stream_p1;
2484
2485 let guard = room_event_cache.inner.state.write().await.unwrap();
2486
2487 assert!(guard.is_dirty().not());
2489
2490 assert_matches!(
2492 updates_stream.recv().await.unwrap(),
2493 RoomEventCacheUpdate::UpdateTimelineEvents(TimelineVectorDiffs { diffs, .. }) => {
2494 assert_eq!(diffs.len(), 2, "{diffs:#?}");
2495 assert_matches!(&diffs[0], VectorDiff::Clear);
2496 assert_matches!(
2497 &diffs[1],
2498 VectorDiff::Append { values: events } => {
2499 assert_eq!(events.len(), 1);
2500 assert_eq!(events[0].event_id(), Some(ev_id_1));
2501 }
2502 );
2503 }
2504 );
2505
2506 drop(guard);
2509
2510 assert!(event_loaded(room_event_cache, ev_id_1).await);
2511 assert!(event_loaded(room_event_cache, ev_id_0).await.not());
2512
2513 room_event_cache.pagination().run_backwards_once(1).await.unwrap();
2514 assert!(event_loaded(room_event_cache, ev_id_0).await);
2515
2516 assert_matches!(
2518 updates_stream.recv().await.unwrap(),
2519 RoomEventCacheUpdate::UpdateTimelineEvents(TimelineVectorDiffs { diffs, .. }) => {
2520 assert_eq!(diffs.len(), 1, "{diffs:#?}");
2521 assert_matches!(
2522 &diffs[0],
2523 VectorDiff::Insert { index: 0, value: event } => {
2524 assert_eq!(event.event_id(), Some(ev_id_0));
2525 }
2526 );
2527 }
2528 );
2529 }
2530 }
2531 }
2532
2533 #[async_test]
2534 async fn test_load_when_dirty() {
2535 let room_id_0 = room_id!("!raclette:patate.ch");
2536 let room_id_1 = room_id!("!morbiflette:patate.ch");
2537
2538 let event_cache_store = MemoryStore::new();
2540
2541 let client_p0 = MockClientBuilder::new(None)
2543 .on_builder(|builder| {
2544 builder.store_config(
2545 StoreConfig::new(CrossProcessLockConfig::multi_process("process #0"))
2546 .event_cache_store(event_cache_store.clone()),
2547 )
2548 })
2549 .build()
2550 .await;
2551
2552 let client_p1 = MockClientBuilder::new(None)
2554 .on_builder(|builder| {
2555 builder.store_config(
2556 StoreConfig::new(CrossProcessLockConfig::multi_process("process #1"))
2557 .event_cache_store(event_cache_store),
2558 )
2559 })
2560 .build()
2561 .await;
2562
2563 let (room_event_cache_0_p0, room_event_cache_0_p1) = {
2565 let event_cache_p0 = client_p0.event_cache();
2566 event_cache_p0.subscribe().unwrap();
2567
2568 let event_cache_p1 = client_p1.event_cache();
2569 event_cache_p1.subscribe().unwrap();
2570
2571 client_p0.base_client().get_or_create_room(room_id_0, RoomState::Joined);
2572 client_p0.base_client().get_or_create_room(room_id_1, RoomState::Joined);
2573
2574 client_p1.base_client().get_or_create_room(room_id_0, RoomState::Joined);
2575 client_p1.base_client().get_or_create_room(room_id_1, RoomState::Joined);
2576
2577 let (room_event_cache_0_p0, _drop_handles) =
2578 client_p0.get_room(room_id_0).unwrap().event_cache().await.unwrap();
2579 let (room_event_cache_0_p1, _drop_handles) =
2580 client_p1.get_room(room_id_0).unwrap().event_cache().await.unwrap();
2581
2582 (room_event_cache_0_p0, room_event_cache_0_p1)
2583 };
2584
2585 {
2587 drop(room_event_cache_0_p0.inner.state.read().await.unwrap());
2588 drop(room_event_cache_0_p1.inner.state.read().await.unwrap());
2589 }
2590
2591 let (room_event_cache_1_p0, _) =
2595 client_p0.get_room(room_id_1).unwrap().event_cache().await.unwrap();
2596
2597 {
2599 let guard = room_event_cache_1_p0.inner.state.read().await.unwrap();
2600 assert!(guard.is_dirty().not());
2601 }
2602
2603 }
2606
2607 #[async_test]
2608 async fn test_uniq_read_marker() {
2609 let client = MockClientBuilder::new(None).build().await;
2610 let room_id = room_id!("!galette:saucisse.bzh");
2611 client.base_client().get_or_create_room(room_id, RoomState::Joined);
2612
2613 let event_cache = client.event_cache();
2614
2615 event_cache.subscribe().unwrap();
2616
2617 let mut generic_stream = event_cache.subscribe_to_room_generic_updates();
2618 let (room_event_cache, _drop_handles) = event_cache.room(room_id).await.unwrap();
2619 let (events, mut stream) = room_event_cache.subscribe().await.unwrap();
2620
2621 assert!(events.is_empty());
2622
2623 let read_marker_event = Raw::from_json_string(
2625 json!({
2626 "content": {
2627 "event_id": "$crepe:saucisse.bzh"
2628 },
2629 "room_id": "!galette:saucisse.bzh",
2630 "type": "m.fully_read"
2631 })
2632 .to_string(),
2633 )
2634 .unwrap();
2635 let account_data = vec![read_marker_event; 100];
2636
2637 room_event_cache
2638 .handle_joined_room_update(
2639 Default::default(),
2640 MaybeReceiptEventContent::none(),
2641 account_data,
2642 Default::default(),
2643 Default::default(),
2644 )
2645 .await
2646 .unwrap();
2647
2648 assert_matches!(
2650 stream.recv().await.unwrap(),
2651 RoomEventCacheUpdate::MoveReadMarkerTo { .. }
2652 );
2653
2654 assert!(stream.recv().now_or_never().is_none());
2655
2656 assert!(generic_stream.recv().now_or_never().is_none());
2658 }
2659
2660 async fn event_loaded(room_event_cache: &RoomEventCache, event_id: &EventId) -> bool {
2661 room_event_cache
2662 .rfind_map_event_in_memory_by(|event| {
2663 (event.event_id() == Some(event_id)).then_some(())
2664 })
2665 .await
2666 .unwrap()
2667 .is_some()
2668 }
2669}