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(
369 RoomEventCacheUpdate::MoveReadMarkerTo { event_id: ev.content.event_id },
370 None,
371 );
372 }
373
374 Ok(_) => {
375 }
378
379 Err(e) => {
380 let event_type = raw_event.get_field::<String>("type").ok().flatten();
381 warn!(event_type, "Failed to deserialize account data: {e}");
382 }
383 }
384 }
385 }
386
387 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() {
416 return self
417 .handle_timeline_inner(
418 state,
419 Timeline { limited: false, prev_batch: None, events: vec![event] },
420 MaybeReceiptEventContent::none(),
421 BTreeMap::new(),
422 None,
423 )
424 .await;
425 }
426
427 Ok(())
428 }
429
430 async fn handle_timeline_inner(
431 &self,
432 mut state: StateLockWriteGuard<'_, RoomEventCacheState>,
433 timeline: Timeline,
434 read_receipts: MaybeReceiptEventContent,
435 ambiguity_changes: BTreeMap<OwnedEventId, AmbiguityChange>,
436 avatar_changes: Option<BTreeMap<OwnedUserId, Option<OwnedMxcUri>>>,
437 ) -> Result<()> {
438 if timeline.events.is_empty()
439 && timeline.prev_batch.is_none()
440 && read_receipts.is_none()
441 && ambiguity_changes.is_empty()
442 && avatar_changes.as_ref().is_none_or(|avatars| avatars.is_empty())
443 {
444 return Ok(());
445 }
446
447 trace!("adding new events");
448
449 let (stored_prev_batch_token, timeline_event_diffs) =
450 state.handle_sync(timeline, &read_receipts).await?;
451
452 drop(state);
453
454 if stored_prev_batch_token {
457 self.pagination_batch_token_notifier.notify_one();
458 }
459
460 if !timeline_event_diffs.is_empty() {
463 self.update_sender.send(
464 RoomEventCacheUpdate::UpdateTimelineEvents(TimelineVectorDiffs {
465 diffs: timeline_event_diffs,
466 origin: EventsOrigin::Sync,
467 }),
468 Some(RoomEventCacheGenericUpdate { room_id: self.room_id.clone() }),
469 );
470 }
471
472 if let Some(read_receipts) = read_receipts.into_inner() {
473 self.update_sender
474 .send(RoomEventCacheUpdate::AddReadReceiptEvent { event: read_receipts }, None);
475 }
476
477 if !ambiguity_changes.is_empty() || avatar_changes.as_ref().is_some_and(|c| !c.is_empty()) {
478 self.update_sender.send(
479 RoomEventCacheUpdate::UpdateMembers { ambiguity_changes, avatar_changes },
480 None,
481 );
482 }
483
484 Ok(())
485 }
486}
487
488#[cfg(test)]
489mod tests {
490 use matrix_sdk_base::{RoomState, event_cache::Event};
491 use matrix_sdk_test::{async_test, event_factory::EventFactory};
492 use ruma::{
493 RoomId, event_id,
494 events::{relation::RelationType, room::message::RoomMessageEventContentWithoutRelation},
495 room_id, user_id,
496 };
497
498 use crate::test_utils::logged_in_client;
499
500 #[async_test]
501 async fn test_find_event_by_id_with_edit_relation() {
502 let original_id = event_id!("$original");
503 let related_id = event_id!("$related");
504 let room_id = room_id!("!galette:saucisse.bzh");
505 let f = EventFactory::new().room(room_id).sender(user_id!("@ben:saucisse.bzh"));
506
507 assert_relations(
508 room_id,
509 f.text_msg("Original event").event_id(original_id).into(),
510 f.text_msg("* An edited event")
511 .edit(
512 original_id,
513 RoomMessageEventContentWithoutRelation::text_plain("And edited event"),
514 )
515 .event_id(related_id)
516 .into(),
517 f,
518 )
519 .await;
520 }
521
522 #[async_test]
523 async fn test_find_event_by_id_with_thread_reply_relation() {
524 let original_id = event_id!("$original");
525 let related_id = event_id!("$related");
526 let room_id = room_id!("!galette:saucisse.bzh");
527 let f = EventFactory::new().room(room_id).sender(user_id!("@ben:saucisse.bzh"));
528
529 assert_relations(
530 room_id,
531 f.text_msg("Original event").event_id(original_id).into(),
532 f.text_msg("A reply").in_thread(original_id, related_id).event_id(related_id).into(),
533 f,
534 )
535 .await;
536 }
537
538 #[async_test]
539 async fn test_find_event_by_id_with_reaction_relation() {
540 let original_id = event_id!("$original");
541 let related_id = event_id!("$related");
542 let room_id = room_id!("!galette:saucisse.bzh");
543 let f = EventFactory::new().room(room_id).sender(user_id!("@ben:saucisse.bzh"));
544
545 assert_relations(
546 room_id,
547 f.text_msg("Original event").event_id(original_id).into(),
548 f.reaction(original_id, ":D").event_id(related_id).into(),
549 f,
550 )
551 .await;
552 }
553
554 #[async_test]
555 async fn test_find_event_by_id_with_poll_response_relation() {
556 let original_id = event_id!("$original");
557 let related_id = event_id!("$related");
558 let room_id = room_id!("!galette:saucisse.bzh");
559 let f = EventFactory::new().room(room_id).sender(user_id!("@ben:saucisse.bzh"));
560
561 assert_relations(
562 room_id,
563 f.poll_start("Poll start event", "A poll question", vec!["An answer"])
564 .event_id(original_id)
565 .into(),
566 f.poll_response(vec!["1"], original_id).event_id(related_id).into(),
567 f,
568 )
569 .await;
570 }
571
572 #[async_test]
573 async fn test_find_event_by_id_with_poll_end_relation() {
574 let original_id = event_id!("$original");
575 let related_id = event_id!("$related");
576 let room_id = room_id!("!galette:saucisse.bzh");
577 let f = EventFactory::new().room(room_id).sender(user_id!("@ben:saucisse.bzh"));
578
579 assert_relations(
580 room_id,
581 f.poll_start("Poll start event", "A poll question", vec!["An answer"])
582 .event_id(original_id)
583 .into(),
584 f.poll_end("Poll ended", original_id).event_id(related_id).into(),
585 f,
586 )
587 .await;
588 }
589
590 #[async_test]
591 async fn test_find_event_by_id_with_filtered_relationships() {
592 let original_id = event_id!("$original");
593 let related_id = event_id!("$related");
594 let associated_related_id = event_id!("$recursive_related");
595 let room_id = room_id!("!galette:saucisse.bzh");
596 let event_factory = EventFactory::new().room(room_id).sender(user_id!("@ben:saucisse.bzh"));
597
598 let original_event = event_factory.text_msg("Original event").event_id(original_id).into();
599 let related_event = event_factory
600 .text_msg("* Edited event")
601 .edit(original_id, RoomMessageEventContentWithoutRelation::text_plain("Edited event"))
602 .event_id(related_id)
603 .into();
604 let associated_related_event =
605 event_factory.reaction(related_id, "🤡").event_id(associated_related_id).into();
606
607 let client = logged_in_client(None).await;
608
609 let event_cache = client.event_cache();
610 event_cache.subscribe().unwrap();
611
612 client.base_client().get_or_create_room(room_id, RoomState::Joined);
613 let room = client.get_room(room_id).unwrap();
614
615 let (room_event_cache, _drop_handles) = room.event_cache().await.unwrap();
616
617 {
618 let mut state = room_event_cache.inner.state.write().await.unwrap();
619
620 state.save_events([original_event]).await.unwrap();
622
623 state.save_events([related_event]).await.unwrap();
625
626 state.save_events([associated_related_event]).await.unwrap();
629 }
630
631 let filter = Some(vec![RelationType::Replacement]);
632 let (event, related_events) = room_event_cache
633 .find_event_with_relations(original_id, filter)
634 .await
635 .expect("Failed to find the event with relations")
636 .expect("Event has no relation");
637 let cached_event_id = event.event_id().unwrap();
639 assert_eq!(cached_event_id, original_id);
640
641 assert_eq!(related_events.len(), 1);
644
645 let related_event_id = related_events[0].event_id().unwrap();
646 assert_eq!(related_event_id, related_id);
647
648 let filter = Some(vec![RelationType::Thread]);
650 let (event, related_events) = room_event_cache
651 .find_event_with_relations(original_id, filter)
652 .await
653 .expect("Failed to find the event with relations")
654 .expect("Event has no relation");
655
656 let cached_event_id = event.event_id().unwrap();
658 assert_eq!(cached_event_id, original_id);
659 assert!(related_events.is_empty());
661 }
662
663 #[async_test]
664 async fn test_find_event_by_id_with_recursive_relation() {
665 let original_id = event_id!("$original");
666 let related_id = event_id!("$related");
667 let associated_related_id = event_id!("$recursive_related");
668 let room_id = room_id!("!galette:saucisse.bzh");
669 let event_factory = EventFactory::new().room(room_id).sender(user_id!("@ben:saucisse.bzh"));
670
671 let original_event = event_factory.text_msg("Original event").event_id(original_id).into();
672 let related_event = event_factory
673 .text_msg("* Edited event")
674 .edit(original_id, RoomMessageEventContentWithoutRelation::text_plain("Edited event"))
675 .event_id(related_id)
676 .into();
677 let associated_related_event =
678 event_factory.reaction(related_id, "👍").event_id(associated_related_id).into();
679
680 let client = logged_in_client(None).await;
681
682 let event_cache = client.event_cache();
683 event_cache.subscribe().unwrap();
684
685 client.base_client().get_or_create_room(room_id, RoomState::Joined);
686 let room = client.get_room(room_id).unwrap();
687
688 let (room_event_cache, _drop_handles) = room.event_cache().await.unwrap();
689
690 {
691 let mut state = room_event_cache.inner.state.write().await.unwrap();
692
693 state.save_events([original_event]).await.unwrap();
695
696 state.save_events([related_event]).await.unwrap();
698
699 state.save_events([associated_related_event]).await.unwrap();
702 }
703
704 let (event, related_events) = room_event_cache
705 .find_event_with_relations(original_id, None)
706 .await
707 .expect("Failed to find the event with relations")
708 .expect("Event has no relation");
709 let cached_event_id = event.event_id().unwrap();
711 assert_eq!(cached_event_id, original_id);
712
713 assert_eq!(related_events.len(), 2);
715
716 let related_event_id = related_events[0].event_id().unwrap();
717 assert_eq!(related_event_id, related_id);
718 let related_event_id = related_events[1].event_id().unwrap();
719 assert_eq!(related_event_id, associated_related_id);
720 }
721
722 async fn assert_relations(
723 room_id: &RoomId,
724 original_event: Event,
725 related_event: Event,
726 event_factory: EventFactory,
727 ) {
728 let client = logged_in_client(None).await;
729
730 let event_cache = client.event_cache();
731 event_cache.subscribe().unwrap();
732
733 client.base_client().get_or_create_room(room_id, RoomState::Joined);
734 let room = client.get_room(room_id).unwrap();
735
736 let (room_event_cache, _drop_handles) = room.event_cache().await.unwrap();
737
738 let original_event_id = original_event.event_id().unwrap().to_owned();
739 let related_id = related_event.event_id().unwrap().to_owned();
740
741 {
742 let mut state = room_event_cache.inner.state.write().await.unwrap();
743
744 state.save_events([original_event]).await.unwrap();
746
747 let unrelated_id = event_id!("$2");
750 state
751 .save_events([event_factory
752 .text_msg("An unrelated event")
753 .event_id(unrelated_id)
754 .into()])
755 .await
756 .unwrap();
757
758 state.save_events([related_event]).await.unwrap();
760 }
761
762 let (event, related_events) = room_event_cache
763 .find_event_with_relations(&original_event_id, None)
764 .await
765 .expect("Failed to find the event with relations")
766 .expect("Event has no relation");
767 let cached_event_id = event.event_id().unwrap();
769 assert_eq!(cached_event_id, original_event_id);
770
771 let related_event_id = related_events[0].event_id().unwrap();
773 assert_eq!(related_event_id, related_id);
774 }
775}
776
777#[cfg(all(test, not(target_family = "wasm")))] mod timed_tests {
779 use std::{ops::Not, sync::Arc};
780
781 use assert_matches::assert_matches;
782 use eyeball_im::VectorDiff;
783 use futures_util::FutureExt;
784 use matrix_sdk_base::{
785 RoomState,
786 event_cache::{
787 Gap,
788 store::{EventCacheStore as _, MemoryStore},
789 },
790 linked_chunk::{
791 ChunkContent, ChunkIdentifier, LinkedChunkId, Position, Update,
792 lazy_loader::from_all_chunks,
793 },
794 store::StoreConfig,
795 sync::Timeline,
796 };
797 use matrix_sdk_common::cross_process_lock::CrossProcessLockConfig;
798 use matrix_sdk_test::{ALICE, BOB, async_test, event_factory::EventFactory};
799 use ruma::{
800 EventId, event_id,
801 events::{AnySyncMessageLikeEvent, AnySyncTimelineEvent},
802 room_id,
803 serde::Raw,
804 user_id,
805 };
806 use serde_json::json;
807 use strass::assert_let;
808 use tokio::task::yield_now;
809
810 use super::{
811 super::{super::TimelineVectorDiffs, pagination::LoadMoreEventsBackwardsOutcome},
812 MaybeReceiptEventContent, RoomEventCache, RoomEventCacheGenericUpdate,
813 RoomEventCacheUpdate,
814 };
815 use crate::{assert_let_timeout, test_utils::client::MockClientBuilder};
816
817 #[async_test]
818 async fn test_write_to_storage() {
819 let room_id = room_id!("!galette:saucisse.bzh");
820 let event_id_0 = event_id!("$ev0");
821 let f = EventFactory::new().room(room_id).sender(user_id!("@ben:saucisse.bzh"));
822
823 let event_cache_store = Arc::new(MemoryStore::new());
824
825 let client = MockClientBuilder::new(None)
826 .on_builder(|builder| {
827 builder.store_config(
828 StoreConfig::new(CrossProcessLockConfig::multi_process("hodor"))
829 .event_cache_store(event_cache_store.clone()),
830 )
831 })
832 .build()
833 .await;
834
835 let event_cache = client.event_cache();
836
837 event_cache.subscribe().unwrap();
839
840 client.base_client().get_or_create_room(room_id, RoomState::Joined);
841 let room = client.get_room(room_id).unwrap();
842
843 let mut generic_stream = event_cache.subscribe_to_room_generic_updates();
844 let (room_event_cache, _drop_handles) = room.event_cache().await.unwrap();
845
846 let timeline = Timeline {
848 limited: true,
849 prev_batch: Some("raclette".to_owned()),
850 events: vec![f.text_msg("hey yo").event_id(event_id_0).into_event()],
851 };
852
853 room_event_cache
854 .handle_joined_room_update(
855 timeline,
856 MaybeReceiptEventContent::none(),
857 Default::default(),
858 Default::default(),
859 Default::default(),
860 )
861 .await
862 .unwrap();
863
864 assert_matches!(
866 generic_stream.recv().await,
867 Ok(RoomEventCacheGenericUpdate { room_id: expected_room_id }) => {
868 assert_eq!(expected_room_id, room_id);
869 }
870 );
871 assert!(generic_stream.is_empty());
872
873 let linked_chunk = from_all_chunks::<3, _, _>(
875 event_cache_store.load_all_chunks(LinkedChunkId::Room(room_id)).await.unwrap(),
876 )
877 .unwrap()
878 .unwrap();
879
880 assert_eq!(linked_chunk.chunks().count(), 2);
881
882 let mut chunks = linked_chunk.chunks();
883
884 assert_matches!(chunks.next().unwrap().content(), ChunkContent::Gap(gap) => {
886 assert_eq!(gap.token, "raclette");
887 });
888
889 assert_matches!(chunks.next().unwrap().content(), ChunkContent::Items(events) => {
891 assert_eq!(events.len(), 1);
892 assert_eq!(events[0].event_id(), Some(event_id_0));
893 });
894
895 assert!(chunks.next().is_none());
897 }
898
899 #[async_test]
900 async fn test_write_to_storage_strips_bundled_relations() {
901 let room_id = room_id!("!galette:saucisse.bzh");
902 let f = EventFactory::new().room(room_id).sender(user_id!("@ben:saucisse.bzh"));
903
904 let event_cache_store = Arc::new(MemoryStore::new());
905
906 let client = MockClientBuilder::new(None)
907 .on_builder(|builder| {
908 builder.store_config(
909 StoreConfig::new(CrossProcessLockConfig::multi_process("hodor"))
910 .event_cache_store(event_cache_store.clone()),
911 )
912 })
913 .build()
914 .await;
915
916 let event_cache = client.event_cache();
917
918 event_cache.subscribe().unwrap();
920
921 client.base_client().get_or_create_room(room_id, RoomState::Joined);
922 let room = client.get_room(room_id).unwrap();
923
924 let mut generic_stream = event_cache.subscribe_to_room_generic_updates();
925 let (room_event_cache, _drop_handles) = room.event_cache().await.unwrap();
926
927 let ev = f
929 .text_msg("hey yo")
930 .sender(*ALICE)
931 .with_bundled_edit(f.text_msg("Hello, Kind Sir").sender(*ALICE))
932 .into_event();
933
934 let timeline = Timeline { limited: false, prev_batch: None, events: vec![ev] };
935
936 room_event_cache
937 .handle_joined_room_update(
938 timeline,
939 MaybeReceiptEventContent::none(),
940 Default::default(),
941 Default::default(),
942 Default::default(),
943 )
944 .await
945 .unwrap();
946
947 assert_matches!(
949 generic_stream.recv().await,
950 Ok(RoomEventCacheGenericUpdate { room_id: expected_room_id }) => {
951 assert_eq!(expected_room_id, room_id);
952 }
953 );
954 assert!(generic_stream.is_empty());
955
956 {
958 let events = room_event_cache.events().await.unwrap();
959
960 assert_eq!(events.len(), 1);
961
962 let ev = events[0].raw().deserialize().unwrap();
963 assert_let!(
964 AnySyncTimelineEvent::MessageLike(AnySyncMessageLikeEvent::RoomMessage(msg)) = ev
965 );
966
967 let original = msg.as_original().unwrap();
968 assert_eq!(original.content.body(), "hey yo");
969 assert!(original.unsigned.relations.replace.is_some());
970 }
971
972 let linked_chunk = from_all_chunks::<3, _, _>(
974 event_cache_store.load_all_chunks(LinkedChunkId::Room(room_id)).await.unwrap(),
975 )
976 .unwrap()
977 .unwrap();
978
979 assert_eq!(linked_chunk.chunks().count(), 1);
980
981 let mut chunks = linked_chunk.chunks();
982 assert_matches!(chunks.next().unwrap().content(), ChunkContent::Items(events) => {
983 assert_eq!(events.len(), 1);
984
985 let ev = events[0].raw().deserialize().unwrap();
986 assert_let!(AnySyncTimelineEvent::MessageLike(AnySyncMessageLikeEvent::RoomMessage(msg)) = ev);
987
988 let original = msg.as_original().unwrap();
989 assert_eq!(original.content.body(), "hey yo");
990 assert!(original.unsigned.relations.replace.is_none());
991 });
992
993 assert!(chunks.next().is_none());
995 }
996
997 #[async_test]
998 async fn test_clear() {
999 let room_id = room_id!("!galette:saucisse.bzh");
1000 let f = EventFactory::new().room(room_id).sender(user_id!("@ben:saucisse.bzh"));
1001
1002 let event_cache_store = Arc::new(MemoryStore::new());
1003
1004 let event_id1 = event_id!("$1");
1005 let event_id2 = event_id!("$2");
1006
1007 let ev1 = f.text_msg("hello world").event_id(event_id1).into_event();
1008 let ev2 = f.text_msg("how's it going").event_id(event_id2).into_event();
1009
1010 event_cache_store
1012 .handle_linked_chunk_updates(
1013 LinkedChunkId::Room(room_id),
1014 vec![
1015 Update::NewItemsChunk {
1017 previous: None,
1018 new: ChunkIdentifier::new(0),
1019 next: None,
1020 },
1021 Update::NewGapChunk {
1023 previous: Some(ChunkIdentifier::new(0)),
1024 new: ChunkIdentifier::new(42),
1026 next: None,
1027 gap: Gap { token: "comté".to_owned() },
1028 },
1029 Update::NewItemsChunk {
1031 previous: Some(ChunkIdentifier::new(42)),
1032 new: ChunkIdentifier::new(1),
1033 next: None,
1034 },
1035 Update::PushItems {
1036 at: Position::new(ChunkIdentifier::new(1), 0),
1037 items: vec![ev1.clone()],
1038 },
1039 Update::NewItemsChunk {
1041 previous: Some(ChunkIdentifier::new(1)),
1042 new: ChunkIdentifier::new(2),
1043 next: None,
1044 },
1045 Update::PushItems {
1046 at: Position::new(ChunkIdentifier::new(2), 0),
1047 items: vec![ev2.clone()],
1048 },
1049 ],
1050 )
1051 .await
1052 .unwrap();
1053
1054 let client = MockClientBuilder::new(None)
1055 .on_builder(|builder| {
1056 builder.store_config(
1057 StoreConfig::new(CrossProcessLockConfig::multi_process("hodor"))
1058 .event_cache_store(event_cache_store.clone()),
1059 )
1060 })
1061 .build()
1062 .await;
1063
1064 let event_cache = client.event_cache();
1065
1066 event_cache.subscribe().unwrap();
1068
1069 client.base_client().get_or_create_room(room_id, RoomState::Joined);
1070 let room = client.get_room(room_id).unwrap();
1071
1072 let (room_event_cache, _drop_handles) = room.event_cache().await.unwrap();
1073
1074 let (items, mut stream) = room_event_cache.subscribe().await.unwrap();
1075 let mut generic_stream = event_cache.subscribe_to_room_generic_updates();
1076
1077 {
1079 assert!(room_event_cache.find_event(event_id1).await.unwrap().is_some());
1080 assert!(room_event_cache.find_event(event_id2).await.unwrap().is_some());
1081 }
1082
1083 {
1085 assert_eq!(items.len(), 1);
1088 assert_eq!(items[0].event_id().unwrap(), event_id2);
1089
1090 assert!(stream.is_empty());
1091 }
1092
1093 {
1095 room_event_cache.pagination().run_backwards_once(20).await.unwrap();
1096
1097 assert_let_timeout!(
1098 Ok(RoomEventCacheUpdate::UpdateTimelineEvents(TimelineVectorDiffs { diffs, .. })) =
1099 stream.recv()
1100 );
1101 assert_eq!(diffs.len(), 1);
1102 assert_matches!(&diffs[0], VectorDiff::Insert { index: 0, value: event } => {
1103 assert_eq!(event.event_id().unwrap(), event_id1);
1105 });
1106
1107 assert!(stream.is_empty());
1108
1109 assert_let_timeout!(
1110 Ok(RoomEventCacheGenericUpdate { room_id: expected_room_id }) =
1111 generic_stream.recv()
1112 );
1113 assert_eq!(room_id, expected_room_id);
1114 assert!(generic_stream.is_empty());
1115 }
1116
1117 event_cache.clear_all_rooms().await.unwrap();
1119
1120 assert_let_timeout!(
1122 Ok(RoomEventCacheUpdate::UpdateTimelineEvents(TimelineVectorDiffs { diffs, .. })) =
1123 stream.recv()
1124 );
1125 assert_eq!(diffs.len(), 1);
1126 assert_let!(VectorDiff::Clear = &diffs[0]);
1127
1128 assert_let_timeout!(
1130 Ok(RoomEventCacheGenericUpdate { room_id: received_room_id }) = generic_stream.recv()
1131 );
1132 assert_eq!(received_room_id, room_id);
1133 assert!(generic_stream.is_empty());
1134
1135 assert!(room_event_cache.find_event(event_id1).await.unwrap().is_none());
1137
1138 let items = room_event_cache.events().await.unwrap();
1140 assert!(items.is_empty());
1141
1142 assert!(
1144 event_cache_store
1145 .load_all_chunks(LinkedChunkId::Room(room_id))
1146 .await
1147 .unwrap()
1148 .is_empty()
1149 );
1150 }
1151
1152 #[async_test]
1153 async fn test_load_from_storage() {
1154 let room_id = room_id!("!galette:saucisse.bzh");
1155 let f = EventFactory::new().room(room_id).sender(user_id!("@ben:saucisse.bzh"));
1156
1157 let event_cache_store = Arc::new(MemoryStore::new());
1158
1159 let event_id1 = event_id!("$1");
1160 let event_id2 = event_id!("$2");
1161
1162 let ev1 = f.text_msg("hello world").sender(*ALICE).event_id(event_id1).into_event();
1163 let ev2 = f.text_msg("how's it going").sender(*BOB).event_id(event_id2).into_event();
1164
1165 event_cache_store
1167 .handle_linked_chunk_updates(
1168 LinkedChunkId::Room(room_id),
1169 vec![
1170 Update::NewItemsChunk {
1172 previous: None,
1173 new: ChunkIdentifier::new(0),
1174 next: None,
1175 },
1176 Update::NewGapChunk {
1178 previous: Some(ChunkIdentifier::new(0)),
1179 new: ChunkIdentifier::new(42),
1181 next: None,
1182 gap: Gap { token: "cheddar".to_owned() },
1183 },
1184 Update::NewItemsChunk {
1186 previous: Some(ChunkIdentifier::new(42)),
1187 new: ChunkIdentifier::new(1),
1188 next: None,
1189 },
1190 Update::PushItems {
1191 at: Position::new(ChunkIdentifier::new(1), 0),
1192 items: vec![ev1.clone()],
1193 },
1194 Update::NewItemsChunk {
1196 previous: Some(ChunkIdentifier::new(1)),
1197 new: ChunkIdentifier::new(2),
1198 next: None,
1199 },
1200 Update::PushItems {
1201 at: Position::new(ChunkIdentifier::new(2), 0),
1202 items: vec![ev2.clone()],
1203 },
1204 ],
1205 )
1206 .await
1207 .unwrap();
1208
1209 let client = MockClientBuilder::new(None)
1210 .on_builder(|builder| {
1211 builder.store_config(
1212 StoreConfig::new(CrossProcessLockConfig::multi_process("hodor"))
1213 .event_cache_store(event_cache_store.clone()),
1214 )
1215 })
1216 .build()
1217 .await;
1218
1219 let event_cache = client.event_cache();
1220
1221 event_cache.subscribe().unwrap();
1223
1224 let mut generic_stream = event_cache.subscribe_to_room_generic_updates();
1227
1228 client.base_client().get_or_create_room(room_id, RoomState::Joined);
1229 let room = client.get_room(room_id).unwrap();
1230
1231 let (room_event_cache, _drop_handles) = room.event_cache().await.unwrap();
1232
1233 assert_matches!(
1236 generic_stream.recv().await,
1237 Ok(RoomEventCacheGenericUpdate { room_id: expected_room_id }) => {
1238 assert_eq!(room_id, expected_room_id);
1239 }
1240 );
1241 assert!(generic_stream.is_empty());
1242
1243 let (items, mut stream) = room_event_cache.subscribe().await.unwrap();
1244
1245 assert_eq!(items.len(), 1);
1248 assert_eq!(items[0].event_id().unwrap(), event_id2);
1249 assert!(stream.is_empty());
1250
1251 assert!(room_event_cache.find_event(event_id1).await.unwrap().is_some());
1254 assert!(room_event_cache.find_event(event_id2).await.unwrap().is_some());
1255
1256 room_event_cache.pagination().run_backwards_once(20).await.unwrap();
1258
1259 assert_let_timeout!(
1260 Ok(RoomEventCacheUpdate::UpdateTimelineEvents(TimelineVectorDiffs { diffs, .. })) =
1261 stream.recv()
1262 );
1263 assert_eq!(diffs.len(), 1);
1264 assert_matches!(&diffs[0], VectorDiff::Insert { index: 0, value: event } => {
1265 assert_eq!(event.event_id().unwrap(), event_id1);
1266 });
1267
1268 assert!(stream.is_empty());
1269
1270 assert_matches!(
1272 generic_stream.recv().await,
1273 Ok(RoomEventCacheGenericUpdate { room_id: expected_room_id }) => {
1274 assert_eq!(expected_room_id, room_id);
1275 }
1276 );
1277 assert!(generic_stream.is_empty());
1278
1279 let timeline = Timeline { limited: false, prev_batch: None, events: vec![ev2] };
1281
1282 room_event_cache
1283 .handle_joined_room_update(
1284 timeline,
1285 MaybeReceiptEventContent::none(),
1286 Default::default(),
1287 Default::default(),
1288 Default::default(),
1289 )
1290 .await
1291 .unwrap();
1292
1293 assert!(generic_stream.recv().now_or_never().is_none());
1296
1297 let items = room_event_cache.events().await.unwrap();
1302 assert_eq!(items.len(), 2);
1303 assert_eq!(items[0].event_id().unwrap(), event_id1);
1304 assert_eq!(items[1].event_id().unwrap(), event_id2);
1305 }
1306
1307 #[async_test]
1308 async fn test_load_from_storage_resilient_to_failure() {
1309 let room_id = room_id!("!fondue:patate.ch");
1310 let event_cache_store = Arc::new(MemoryStore::new());
1311
1312 let event = EventFactory::new()
1313 .room(room_id)
1314 .sender(user_id!("@ben:saucisse.bzh"))
1315 .text_msg("foo")
1316 .event_id(event_id!("$42"))
1317 .into_event();
1318
1319 event_cache_store
1321 .handle_linked_chunk_updates(
1322 LinkedChunkId::Room(room_id),
1323 vec![
1324 Update::NewItemsChunk {
1325 previous: None,
1326 new: ChunkIdentifier::new(0),
1327 next: None,
1328 },
1329 Update::PushItems {
1330 at: Position::new(ChunkIdentifier::new(0), 0),
1331 items: vec![event],
1332 },
1333 Update::NewItemsChunk {
1334 previous: Some(ChunkIdentifier::new(0)),
1335 new: ChunkIdentifier::new(1),
1336 next: Some(ChunkIdentifier::new(0)),
1337 },
1338 ],
1339 )
1340 .await
1341 .unwrap();
1342
1343 let client = MockClientBuilder::new(None)
1344 .on_builder(|builder| {
1345 builder.store_config(
1346 StoreConfig::new(CrossProcessLockConfig::multi_process("holder"))
1347 .event_cache_store(event_cache_store.clone()),
1348 )
1349 })
1350 .build()
1351 .await;
1352
1353 let event_cache = client.event_cache();
1354
1355 event_cache.subscribe().unwrap();
1357
1358 client.base_client().get_or_create_room(room_id, RoomState::Joined);
1359 let room = client.get_room(room_id).unwrap();
1360
1361 let (room_event_cache, _drop_handles) = room.event_cache().await.unwrap();
1362
1363 let items = room_event_cache.events().await.unwrap();
1364
1365 assert!(items.is_empty());
1368
1369 let raw_chunks =
1372 event_cache_store.load_all_chunks(LinkedChunkId::Room(room_id)).await.unwrap();
1373 assert!(raw_chunks.is_empty());
1374 }
1375
1376 #[async_test]
1377 async fn test_no_useless_gaps() {
1378 let room_id = room_id!("!galette:saucisse.bzh");
1379
1380 let client = MockClientBuilder::new(None).build().await;
1381
1382 let event_cache = client.event_cache();
1383 event_cache.subscribe().unwrap();
1384
1385 client.base_client().get_or_create_room(room_id, RoomState::Joined);
1386 let room = client.get_room(room_id).unwrap();
1387 let (room_event_cache, _drop_handles) = room.event_cache().await.unwrap();
1388 let mut generic_stream = event_cache.subscribe_to_room_generic_updates();
1389
1390 let f = EventFactory::new().room(room_id).sender(*ALICE);
1391
1392 room_event_cache
1395 .handle_joined_room_update(
1396 Timeline {
1397 limited: true,
1398 prev_batch: Some("raclette".to_owned()),
1399 events: vec![f.text_msg("hey yo").into_event()],
1400 },
1401 MaybeReceiptEventContent::none(),
1402 Default::default(),
1403 Default::default(),
1404 Default::default(),
1405 )
1406 .await
1407 .unwrap();
1408
1409 assert_matches!(
1411 generic_stream.recv().await,
1412 Ok(RoomEventCacheGenericUpdate { room_id: expected_room_id }) => {
1413 assert_eq!(expected_room_id, room_id);
1414 }
1415 );
1416 assert!(generic_stream.is_empty());
1417
1418 {
1419 let state = room_event_cache.inner.state.read().await.unwrap();
1420
1421 let mut num_gaps = 0;
1422 let mut num_events = 0;
1423
1424 for c in state.room_linked_chunk().chunks() {
1425 match c.content() {
1426 ChunkContent::Items(items) => num_events += items.len(),
1427 ChunkContent::Gap(_) => num_gaps += 1,
1428 }
1429 }
1430
1431 assert_eq!(num_gaps, 0);
1434 assert_eq!(num_events, 1);
1435 }
1436
1437 assert_matches!(
1439 room_event_cache.pagination().load_more_events_backwards().await.unwrap(),
1440 LoadMoreEventsBackwardsOutcome::Gap { .. }
1441 );
1442
1443 {
1444 let state = room_event_cache.inner.state.read().await.unwrap();
1445
1446 let mut num_gaps = 0;
1447 let mut num_events = 0;
1448
1449 for c in state.room_linked_chunk().chunks() {
1450 match c.content() {
1451 ChunkContent::Items(items) => num_events += items.len(),
1452 ChunkContent::Gap(_) => num_gaps += 1,
1453 }
1454 }
1455
1456 assert_eq!(num_gaps, 1);
1458 assert_eq!(num_events, 1);
1459 }
1460
1461 room_event_cache
1464 .handle_joined_room_update(
1465 Timeline {
1466 limited: false,
1467 prev_batch: Some("fondue".to_owned()),
1468 events: vec![f.text_msg("sup").into_event()],
1469 },
1470 MaybeReceiptEventContent::none(),
1471 Default::default(),
1472 Default::default(),
1473 Default::default(),
1474 )
1475 .await
1476 .unwrap();
1477
1478 assert_matches!(
1480 generic_stream.recv().await,
1481 Ok(RoomEventCacheGenericUpdate { room_id: expected_room_id }) => {
1482 assert_eq!(expected_room_id, room_id);
1483 }
1484 );
1485 assert!(generic_stream.is_empty());
1486
1487 {
1488 let state = room_event_cache.inner.state.read().await.unwrap();
1489
1490 let mut num_gaps = 0;
1491 let mut num_events = 0;
1492
1493 for c in state.room_linked_chunk().chunks() {
1494 match c.content() {
1495 ChunkContent::Items(items) => num_events += items.len(),
1496 ChunkContent::Gap(gap) => {
1497 assert_eq!(gap.token, "raclette");
1498 num_gaps += 1;
1499 }
1500 }
1501 }
1502
1503 assert_eq!(num_gaps, 1);
1505 assert_eq!(num_events, 2);
1506 }
1507 }
1508
1509 #[async_test]
1510 async fn test_shrink_to_last_chunk() {
1511 let room_id = room_id!("!galette:saucisse.bzh");
1512
1513 let client = MockClientBuilder::new(None).build().await;
1514
1515 let f = EventFactory::new().room(room_id);
1516
1517 let evid1 = event_id!("$1");
1518 let evid2 = event_id!("$2");
1519
1520 let ev1 = f.text_msg("hello world").sender(*ALICE).event_id(evid1).into_event();
1521 let ev2 = f.text_msg("howdy").sender(*BOB).event_id(evid2).into_event();
1522
1523 {
1526 client
1527 .event_cache_store()
1528 .lock()
1529 .await
1530 .expect("Could not acquire the event cache lock")
1531 .as_clean()
1532 .expect("Could not acquire a clean event cache lock")
1533 .handle_linked_chunk_updates(
1534 LinkedChunkId::Room(room_id),
1535 vec![
1536 Update::NewItemsChunk {
1537 previous: None,
1538 new: ChunkIdentifier::new(0),
1539 next: None,
1540 },
1541 Update::PushItems {
1542 at: Position::new(ChunkIdentifier::new(0), 0),
1543 items: vec![ev1],
1544 },
1545 Update::NewItemsChunk {
1546 previous: Some(ChunkIdentifier::new(0)),
1547 new: ChunkIdentifier::new(1),
1548 next: None,
1549 },
1550 Update::PushItems {
1551 at: Position::new(ChunkIdentifier::new(1), 0),
1552 items: vec![ev2],
1553 },
1554 ],
1555 )
1556 .await
1557 .unwrap();
1558 }
1559
1560 let event_cache = client.event_cache();
1561 event_cache.subscribe().unwrap();
1562
1563 client.base_client().get_or_create_room(room_id, RoomState::Joined);
1564 let room = client.get_room(room_id).unwrap();
1565 let (room_event_cache, _drop_handles) = room.event_cache().await.unwrap();
1566
1567 let (events, mut stream) = room_event_cache.subscribe().await.unwrap();
1569 assert_eq!(events.len(), 1);
1570 assert_eq!(events[0].event_id(), Some(evid2));
1571 assert!(stream.is_empty());
1572
1573 let mut generic_stream = event_cache.subscribe_to_room_generic_updates();
1574
1575 let outcome = room_event_cache.pagination().run_backwards_once(20).await.unwrap();
1577 assert_eq!(outcome.events.len(), 1);
1578 assert_eq!(outcome.events[0].event_id(), Some(evid1));
1579 assert!(outcome.reached_start);
1580
1581 assert_let_timeout!(
1583 Ok(RoomEventCacheUpdate::UpdateTimelineEvents(TimelineVectorDiffs { diffs, .. })) =
1584 stream.recv()
1585 );
1586 assert_eq!(diffs.len(), 1);
1587 assert_matches!(&diffs[0], VectorDiff::Insert { index: 0, value } => {
1588 assert_eq!(value.event_id(), Some(evid1));
1589 });
1590
1591 assert!(stream.is_empty());
1592
1593 assert_let_timeout!(
1595 Ok(RoomEventCacheGenericUpdate { room_id: expected_room_id }) = generic_stream.recv()
1596 );
1597 assert_eq!(expected_room_id, room_id);
1598 assert!(generic_stream.is_empty());
1599
1600 room_event_cache
1602 .inner
1603 .state
1604 .reload_no_preprocessing()
1605 .await
1606 .expect("shrinking should succeed");
1607
1608 assert_let_timeout!(
1610 Ok(RoomEventCacheUpdate::UpdateTimelineEvents(TimelineVectorDiffs { diffs, .. })) =
1611 stream.recv()
1612 );
1613 assert_eq!(diffs.len(), 2);
1614 assert_matches!(&diffs[0], VectorDiff::Clear);
1615 assert_matches!(&diffs[1], VectorDiff::Append { values} => {
1616 assert_eq!(values.len(), 1);
1617 assert_eq!(values[0].event_id(), Some(evid2));
1618 });
1619
1620 assert!(stream.is_empty());
1621
1622 assert_let_timeout!(Ok(RoomEventCacheGenericUpdate { .. }) = generic_stream.recv());
1624 assert!(generic_stream.is_empty());
1625
1626 let events = room_event_cache.events().await.unwrap();
1628 assert_eq!(events.len(), 1);
1629 assert_eq!(events[0].event_id(), Some(evid2));
1630
1631 let outcome = room_event_cache.pagination().run_backwards_once(20).await.unwrap();
1634 assert_eq!(outcome.events.len(), 1);
1635 assert_eq!(outcome.events[0].event_id(), Some(evid1));
1636 assert!(outcome.reached_start);
1637 }
1638
1639 #[async_test]
1640 async fn test_room_ordering() {
1641 let room_id = room_id!("!galette:saucisse.bzh");
1642
1643 let client = MockClientBuilder::new(None).build().await;
1644
1645 let f = EventFactory::new().room(room_id).sender(*ALICE);
1646
1647 let evid1 = event_id!("$1");
1648 let evid2 = event_id!("$2");
1649 let evid3 = event_id!("$3");
1650
1651 let ev1 = f.text_msg("hello world").event_id(evid1).into_event();
1652 let ev2 = f.text_msg("howdy").sender(*BOB).event_id(evid2).into_event();
1653 let ev3 = f.text_msg("yo").event_id(evid3).into_event();
1654
1655 {
1658 client
1659 .event_cache_store()
1660 .lock()
1661 .await
1662 .expect("Could not acquire the event cache lock")
1663 .as_clean()
1664 .expect("Could not acquire a clean event cache lock")
1665 .handle_linked_chunk_updates(
1666 LinkedChunkId::Room(room_id),
1667 vec![
1668 Update::NewItemsChunk {
1669 previous: None,
1670 new: ChunkIdentifier::new(0),
1671 next: None,
1672 },
1673 Update::PushItems {
1674 at: Position::new(ChunkIdentifier::new(0), 0),
1675 items: vec![ev1, ev2],
1676 },
1677 Update::NewItemsChunk {
1678 previous: Some(ChunkIdentifier::new(0)),
1679 new: ChunkIdentifier::new(1),
1680 next: None,
1681 },
1682 Update::PushItems {
1683 at: Position::new(ChunkIdentifier::new(1), 0),
1684 items: vec![ev3.clone()],
1685 },
1686 ],
1687 )
1688 .await
1689 .unwrap();
1690 }
1691
1692 let event_cache = client.event_cache();
1693 event_cache.subscribe().unwrap();
1694
1695 client.base_client().get_or_create_room(room_id, RoomState::Joined);
1696 let room = client.get_room(room_id).unwrap();
1697 let (room_event_cache, _drop_handles) = room.event_cache().await.unwrap();
1698
1699 {
1702 let state = room_event_cache.inner.state.read().await.unwrap();
1703 let room_linked_chunk = state.room_linked_chunk();
1704
1705 assert_eq!(
1707 room_linked_chunk.event_order(Position::new(ChunkIdentifier::new(0), 0)),
1708 Some(0)
1709 );
1710
1711 assert_eq!(
1713 room_linked_chunk.event_order(Position::new(ChunkIdentifier::new(0), 1)),
1714 Some(1)
1715 );
1716
1717 let mut events = room_linked_chunk.events();
1719 let (pos, ev) = events.next().unwrap();
1720 assert_eq!(pos, Position::new(ChunkIdentifier::new(1), 0));
1721 assert_eq!(ev.event_id(), Some(evid3));
1722 assert_eq!(room_linked_chunk.event_order(pos), Some(2));
1723
1724 assert!(events.next().is_none());
1726 }
1727
1728 let outcome = room_event_cache.pagination().run_backwards_once(20).await.unwrap();
1730 assert!(outcome.reached_start);
1731
1732 {
1735 let state = room_event_cache.inner.state.read().await.unwrap();
1736 let room_linked_chunk = state.room_linked_chunk();
1737
1738 for (i, (pos, _)) in room_linked_chunk.events().enumerate() {
1739 assert_eq!(room_linked_chunk.event_order(pos), Some(i));
1740 }
1741 }
1742
1743 let evid4 = event_id!("$4");
1748 room_event_cache
1749 .handle_joined_room_update(
1750 Timeline {
1751 limited: true,
1752 prev_batch: Some("fondue".to_owned()),
1753 events: vec![ev3, f.text_msg("sup").event_id(evid4).into_event()],
1754 },
1755 MaybeReceiptEventContent::none(),
1756 Default::default(),
1757 Default::default(),
1758 Default::default(),
1759 )
1760 .await
1761 .unwrap();
1762
1763 {
1764 let state = room_event_cache.inner.state.read().await.unwrap();
1765 let room_linked_chunk = state.room_linked_chunk();
1766
1767 let mut events = room_linked_chunk.events();
1769
1770 let (pos, ev) = events.next().unwrap();
1771 assert_eq!(ev.event_id(), Some(evid3));
1772 assert_eq!(room_linked_chunk.event_order(pos), Some(2));
1773
1774 let (pos, ev) = events.next().unwrap();
1775 assert_eq!(ev.event_id(), Some(evid4));
1776 assert_eq!(room_linked_chunk.event_order(pos), Some(3));
1777
1778 assert!(events.next().is_none());
1780
1781 assert_eq!(
1783 room_linked_chunk.event_order(Position::new(ChunkIdentifier::new(0), 0)),
1784 Some(0)
1785 );
1786 assert_eq!(
1787 room_linked_chunk.event_order(Position::new(ChunkIdentifier::new(0), 1)),
1788 Some(1)
1789 );
1790
1791 assert_eq!(
1794 room_linked_chunk.event_order(Position::new(ChunkIdentifier::new(1), 0)),
1795 None
1796 );
1797 }
1798 }
1799
1800 #[async_test]
1801 async fn test_auto_shrink_after_all_subscribers_are_gone() {
1802 let room_id = room_id!("!galette:saucisse.bzh");
1803
1804 let client = MockClientBuilder::new(None).build().await;
1805
1806 let f = EventFactory::new().room(room_id);
1807
1808 let evid1 = event_id!("$1");
1809 let evid2 = event_id!("$2");
1810
1811 let ev1 = f.text_msg("hello world").sender(*ALICE).event_id(evid1).into_event();
1812 let ev2 = f.text_msg("howdy").sender(*BOB).event_id(evid2).into_event();
1813
1814 {
1817 client
1818 .event_cache_store()
1819 .lock()
1820 .await
1821 .expect("Could not acquire the event cache lock")
1822 .as_clean()
1823 .expect("Could not acquire a clean event cache lock")
1824 .handle_linked_chunk_updates(
1825 LinkedChunkId::Room(room_id),
1826 vec![
1827 Update::NewItemsChunk {
1828 previous: None,
1829 new: ChunkIdentifier::new(0),
1830 next: None,
1831 },
1832 Update::PushItems {
1833 at: Position::new(ChunkIdentifier::new(0), 0),
1834 items: vec![ev1],
1835 },
1836 Update::NewItemsChunk {
1837 previous: Some(ChunkIdentifier::new(0)),
1838 new: ChunkIdentifier::new(1),
1839 next: None,
1840 },
1841 Update::PushItems {
1842 at: Position::new(ChunkIdentifier::new(1), 0),
1843 items: vec![ev2],
1844 },
1845 ],
1846 )
1847 .await
1848 .unwrap();
1849 }
1850
1851 let event_cache = client.event_cache();
1852 event_cache.subscribe().unwrap();
1853
1854 client.base_client().get_or_create_room(room_id, RoomState::Joined);
1855 let room = client.get_room(room_id).unwrap();
1856 let (room_event_cache, _drop_handles) = room.event_cache().await.unwrap();
1857
1858 let (events1, mut stream1) = room_event_cache.subscribe().await.unwrap();
1860 assert_eq!(events1.len(), 1);
1861 assert_eq!(events1[0].event_id(), Some(evid2));
1862 assert!(stream1.is_empty());
1863
1864 let mut generic_stream = event_cache.subscribe_to_room_generic_updates();
1865
1866 let outcome = room_event_cache.pagination().run_backwards_once(20).await.unwrap();
1868 assert_eq!(outcome.events.len(), 1);
1869 assert_eq!(outcome.events[0].event_id(), Some(evid1));
1870 assert!(outcome.reached_start);
1871
1872 assert_let_timeout!(
1875 Ok(RoomEventCacheUpdate::UpdateTimelineEvents(TimelineVectorDiffs { diffs, .. })) =
1876 stream1.recv()
1877 );
1878 assert_eq!(diffs.len(), 1);
1879 assert_matches!(&diffs[0], VectorDiff::Insert { index: 0, value } => {
1880 assert_eq!(value.event_id(), Some(evid1));
1881 });
1882
1883 assert!(stream1.is_empty());
1884
1885 assert_let_timeout!(
1886 Ok(RoomEventCacheGenericUpdate { room_id: expected_room_id }) = generic_stream.recv()
1887 );
1888 assert_eq!(expected_room_id, room_id);
1889 assert!(generic_stream.is_empty());
1890
1891 let (events2, stream2) = room_event_cache.subscribe().await.unwrap();
1895 assert_eq!(events2.len(), 2);
1896 assert_eq!(events2[0].event_id(), Some(evid1));
1897 assert_eq!(events2[1].event_id(), Some(evid2));
1898 assert!(stream2.is_empty());
1899
1900 let subscriber = {
1902 let state = room_event_cache.inner.state.read().await.unwrap();
1903 state.update_sender.new_room_receiver()
1904 };
1905
1906 drop(stream1);
1908 yield_now().await;
1909
1910 assert!(stream2.is_empty());
1912
1913 drop(stream2);
1915 yield_now().await;
1916
1917 {
1920 let state = room_event_cache.inner.state.read().await.unwrap();
1922 assert_eq!(state.subscribers_handle().count(), 0);
1923
1924 assert!(subscriber.is_empty());
1926 assert!(generic_stream.is_empty());
1927 }
1928
1929 let events3 = room_event_cache.events().await.unwrap();
1931 assert_eq!(events3.len(), 1);
1932 assert_eq!(events3[0].event_id(), Some(evid2));
1933 }
1934
1935 #[async_test]
1936 async fn test_rfind_map_event_in_memory_by() {
1937 let user_id = user_id!("@mnt_io:matrix.org");
1938 let room_id = room_id!("!raclette:patate.ch");
1939 let client = MockClientBuilder::new(None).build().await;
1940
1941 let event_factory = EventFactory::new().room(room_id);
1942
1943 let event_id_0 = event_id!("$ev0");
1944 let event_id_1 = event_id!("$ev1");
1945 let event_id_2 = event_id!("$ev2");
1946 let event_id_3 = event_id!("$ev3");
1947
1948 let event_0 =
1949 event_factory.text_msg("hello").sender(*BOB).event_id(event_id_0).into_event();
1950 let event_1 =
1951 event_factory.text_msg("world").sender(*ALICE).event_id(event_id_1).into_event();
1952 let event_2 = event_factory.text_msg("!").sender(*ALICE).event_id(event_id_2).into_event();
1953 let event_3 =
1954 event_factory.text_msg("eh!").sender(user_id).event_id(event_id_3).into_event();
1955
1956 {
1959 client
1960 .event_cache_store()
1961 .lock()
1962 .await
1963 .expect("Could not acquire the event cache lock")
1964 .as_clean()
1965 .expect("Could not acquire a clean event cache lock")
1966 .handle_linked_chunk_updates(
1967 LinkedChunkId::Room(room_id),
1968 vec![
1969 Update::NewItemsChunk {
1970 previous: None,
1971 new: ChunkIdentifier::new(0),
1972 next: None,
1973 },
1974 Update::PushItems {
1975 at: Position::new(ChunkIdentifier::new(0), 0),
1976 items: vec![event_3],
1977 },
1978 Update::NewItemsChunk {
1979 previous: Some(ChunkIdentifier::new(0)),
1980 new: ChunkIdentifier::new(1),
1981 next: None,
1982 },
1983 Update::PushItems {
1984 at: Position::new(ChunkIdentifier::new(1), 0),
1985 items: vec![event_0, event_1, event_2],
1986 },
1987 ],
1988 )
1989 .await
1990 .unwrap();
1991 }
1992
1993 let event_cache = client.event_cache();
1994 event_cache.subscribe().unwrap();
1995
1996 client.base_client().get_or_create_room(room_id, RoomState::Joined);
1997 let room = client.get_room(room_id).unwrap();
1998 let (room_event_cache, _drop_handles) = room.event_cache().await.unwrap();
1999
2000 assert_matches!(
2002 room_event_cache
2003 .rfind_map_event_in_memory_by(|event| {
2004 (event.sender().as_deref() == Some(*BOB)).then(|| event.event_id().map(ToOwned::to_owned))
2005 })
2006 .await,
2007 Ok(Some(event_id)) => {
2008 assert_eq!(event_id.as_deref(), Some(event_id_0));
2009 }
2010 );
2011
2012 assert_matches!(
2015 room_event_cache
2016 .rfind_map_event_in_memory_by(|event| {
2017 (event.sender().as_deref() == Some(*ALICE)).then(|| event.event_id().map(ToOwned::to_owned))
2018 })
2019 .await,
2020 Ok(Some(event_id)) => {
2021 assert_eq!(event_id.as_deref(), Some(event_id_2));
2022 }
2023 );
2024
2025 assert!(
2027 room_event_cache
2028 .rfind_map_event_in_memory_by(|event| {
2029 (event.sender().as_deref() == Some(user_id))
2030 .then(|| event.event_id().map(ToOwned::to_owned))
2031 })
2032 .await
2033 .unwrap()
2034 .is_none()
2035 );
2036
2037 assert!(
2039 room_event_cache.rfind_map_event_in_memory_by(|_| None::<()>).await.unwrap().is_none()
2040 );
2041 }
2042
2043 #[async_test]
2044 async fn test_reload_when_dirty() {
2045 let user_id = user_id!("@mnt_io:matrix.org");
2046 let room_id = room_id!("!raclette:patate.ch");
2047
2048 let event_cache_store = MemoryStore::new();
2050
2051 let client_p0 = MockClientBuilder::new(None)
2053 .on_builder(|builder| {
2054 builder.store_config(
2055 StoreConfig::new(CrossProcessLockConfig::multi_process("process #0"))
2056 .event_cache_store(event_cache_store.clone()),
2057 )
2058 })
2059 .build()
2060 .await;
2061
2062 let client_p1 = MockClientBuilder::new(None)
2064 .on_builder(|builder| {
2065 builder.store_config(
2066 StoreConfig::new(CrossProcessLockConfig::multi_process("process #1"))
2067 .event_cache_store(event_cache_store),
2068 )
2069 })
2070 .build()
2071 .await;
2072
2073 let event_factory = EventFactory::new().room(room_id).sender(user_id);
2074
2075 let ev_id_0 = event_id!("$ev_0");
2076 let ev_id_1 = event_id!("$ev_1");
2077
2078 let ev_0 = event_factory.text_msg("comté").event_id(ev_id_0).into_event();
2079 let ev_1 = event_factory.text_msg("morbier").event_id(ev_id_1).into_event();
2080
2081 client_p0
2083 .event_cache_store()
2084 .lock()
2085 .await
2086 .expect("[p0] Could not acquire the event cache lock")
2087 .as_clean()
2088 .expect("[p0] Could not acquire a clean event cache lock")
2089 .handle_linked_chunk_updates(
2090 LinkedChunkId::Room(room_id),
2091 vec![
2092 Update::NewItemsChunk {
2093 previous: None,
2094 new: ChunkIdentifier::new(0),
2095 next: None,
2096 },
2097 Update::PushItems {
2098 at: Position::new(ChunkIdentifier::new(0), 0),
2099 items: vec![ev_0],
2100 },
2101 Update::NewItemsChunk {
2102 previous: Some(ChunkIdentifier::new(0)),
2103 new: ChunkIdentifier::new(1),
2104 next: None,
2105 },
2106 Update::PushItems {
2107 at: Position::new(ChunkIdentifier::new(1), 0),
2108 items: vec![ev_1],
2109 },
2110 ],
2111 )
2112 .await
2113 .unwrap();
2114
2115 let (room_event_cache_p0, room_event_cache_p1) = {
2117 let event_cache_p0 = client_p0.event_cache();
2118 event_cache_p0.subscribe().unwrap();
2119
2120 let event_cache_p1 = client_p1.event_cache();
2121 event_cache_p1.subscribe().unwrap();
2122
2123 client_p0.base_client().get_or_create_room(room_id, RoomState::Joined);
2124 client_p1.base_client().get_or_create_room(room_id, RoomState::Joined);
2125
2126 let (room_event_cache_p0, _drop_handles) =
2127 client_p0.get_room(room_id).unwrap().event_cache().await.unwrap();
2128 let (room_event_cache_p1, _drop_handles) =
2129 client_p1.get_room(room_id).unwrap().event_cache().await.unwrap();
2130
2131 (room_event_cache_p0, room_event_cache_p1)
2132 };
2133
2134 let mut updates_stream_p0 = {
2139 let room_event_cache = &room_event_cache_p0;
2140
2141 let (initial_updates, mut updates_stream) =
2142 room_event_cache_p0.subscribe().await.unwrap();
2143
2144 assert_eq!(initial_updates.len(), 1);
2146 assert_eq!(initial_updates[0].event_id(), Some(ev_id_1));
2147 assert!(updates_stream.is_empty());
2148
2149 assert!(event_loaded(room_event_cache, ev_id_1).await);
2151
2152 assert!(event_loaded(room_event_cache, ev_id_0).await.not());
2154
2155 room_event_cache.pagination().run_backwards_once(1).await.unwrap();
2157
2158 assert_matches!(
2160 updates_stream.recv().await.unwrap(),
2161 RoomEventCacheUpdate::UpdateTimelineEvents(TimelineVectorDiffs { diffs, .. }) => {
2162 assert_eq!(diffs.len(), 1, "{diffs:#?}");
2163 assert_matches!(
2164 &diffs[0],
2165 VectorDiff::Insert { index: 0, value: event } => {
2166 assert_eq!(event.event_id(), Some(ev_id_0));
2167 }
2168 );
2169 }
2170 );
2171
2172 assert!(event_loaded(room_event_cache, ev_id_0).await);
2174
2175 updates_stream
2176 };
2177
2178 let mut updates_stream_p1 = {
2180 let room_event_cache = &room_event_cache_p1;
2181 let (initial_updates, mut updates_stream) =
2182 room_event_cache_p1.subscribe().await.unwrap();
2183
2184 assert_eq!(initial_updates.len(), 1);
2186 assert_eq!(initial_updates[0].event_id(), Some(ev_id_1));
2187 assert!(updates_stream.is_empty());
2188
2189 assert!(event_loaded(room_event_cache, ev_id_1).await);
2191
2192 assert!(event_loaded(room_event_cache, ev_id_0).await.not());
2194
2195 room_event_cache.pagination().run_backwards_once(1).await.unwrap();
2197
2198 assert_matches!(
2200 updates_stream.recv().await.unwrap(),
2201 RoomEventCacheUpdate::UpdateTimelineEvents(TimelineVectorDiffs { diffs, .. }) => {
2202 assert_eq!(diffs.len(), 1, "{diffs:#?}");
2203 assert_matches!(
2204 &diffs[0],
2205 VectorDiff::Insert { index: 0, value: event } => {
2206 assert_eq!(event.event_id(), Some(ev_id_0));
2207 }
2208 );
2209 }
2210 );
2211
2212 assert!(event_loaded(room_event_cache, ev_id_0).await);
2214
2215 updates_stream
2216 };
2217
2218 for _ in 0..3 {
2220 {
2224 let room_event_cache = &room_event_cache_p0;
2225 let updates_stream = &mut updates_stream_p0;
2226
2227 assert!(event_loaded(room_event_cache, ev_id_1).await);
2229
2230 assert!(event_loaded(room_event_cache, ev_id_0).await.not());
2233
2234 assert_matches!(
2236 updates_stream.recv().await.unwrap(),
2237 RoomEventCacheUpdate::UpdateTimelineEvents(TimelineVectorDiffs { diffs, .. }) => {
2238 assert_eq!(diffs.len(), 2, "{diffs:#?}");
2239 assert_matches!(&diffs[0], VectorDiff::Clear);
2240 assert_matches!(
2241 &diffs[1],
2242 VectorDiff::Append { values: events } => {
2243 assert_eq!(events.len(), 1);
2244 assert_eq!(events[0].event_id(), Some(ev_id_1));
2245 }
2246 );
2247 }
2248 );
2249
2250 room_event_cache.pagination().run_backwards_once(1).await.unwrap();
2252
2253 assert!(event_loaded(room_event_cache, ev_id_0).await);
2255
2256 assert_matches!(
2258 updates_stream.recv().await.unwrap(),
2259 RoomEventCacheUpdate::UpdateTimelineEvents(TimelineVectorDiffs { diffs, .. }) => {
2260 assert_eq!(diffs.len(), 1, "{diffs:#?}");
2261 assert_matches!(
2262 &diffs[0],
2263 VectorDiff::Insert { index: 0, value: event } => {
2264 assert_eq!(event.event_id(), Some(ev_id_0));
2265 }
2266 );
2267 }
2268 );
2269 }
2270
2271 {
2275 let room_event_cache = &room_event_cache_p1;
2276 let updates_stream = &mut updates_stream_p1;
2277
2278 assert!(event_loaded(room_event_cache, ev_id_1).await);
2280
2281 assert!(event_loaded(room_event_cache, ev_id_0).await.not());
2284
2285 assert_matches!(
2287 updates_stream.recv().await.unwrap(),
2288 RoomEventCacheUpdate::UpdateTimelineEvents(TimelineVectorDiffs { diffs, .. }) => {
2289 assert_eq!(diffs.len(), 2, "{diffs:#?}");
2290 assert_matches!(&diffs[0], VectorDiff::Clear);
2291 assert_matches!(
2292 &diffs[1],
2293 VectorDiff::Append { values: events } => {
2294 assert_eq!(events.len(), 1);
2295 assert_eq!(events[0].event_id(), Some(ev_id_1));
2296 }
2297 );
2298 }
2299 );
2300
2301 room_event_cache.pagination().run_backwards_once(1).await.unwrap();
2303
2304 assert!(event_loaded(room_event_cache, ev_id_0).await);
2306
2307 assert_matches!(
2309 updates_stream.recv().await.unwrap(),
2310 RoomEventCacheUpdate::UpdateTimelineEvents(TimelineVectorDiffs { diffs, .. }) => {
2311 assert_eq!(diffs.len(), 1, "{diffs:#?}");
2312 assert_matches!(
2313 &diffs[0],
2314 VectorDiff::Insert { index: 0, value: event } => {
2315 assert_eq!(event.event_id(), Some(ev_id_0));
2316 }
2317 );
2318 }
2319 );
2320 }
2321 }
2322
2323 for _ in 0..3 {
2326 {
2327 let room_event_cache = &room_event_cache_p0;
2328 let updates_stream = &mut updates_stream_p0;
2329
2330 let guard = room_event_cache.inner.state.read().await.unwrap();
2331
2332 assert!(guard.is_dirty().not());
2338
2339 assert_matches!(
2341 updates_stream.recv().await.unwrap(),
2342 RoomEventCacheUpdate::UpdateTimelineEvents(TimelineVectorDiffs { diffs, .. }) => {
2343 assert_eq!(diffs.len(), 2, "{diffs:#?}");
2344 assert_matches!(&diffs[0], VectorDiff::Clear);
2345 assert_matches!(
2346 &diffs[1],
2347 VectorDiff::Append { values: events } => {
2348 assert_eq!(events.len(), 1);
2349 assert_eq!(events[0].event_id(), Some(ev_id_1));
2350 }
2351 );
2352 }
2353 );
2354
2355 assert!(event_loaded(room_event_cache, ev_id_1).await);
2356 assert!(event_loaded(room_event_cache, ev_id_0).await.not());
2357
2358 drop(guard);
2364
2365 room_event_cache.pagination().run_backwards_once(1).await.unwrap();
2366 assert!(event_loaded(room_event_cache, ev_id_0).await);
2367
2368 assert_matches!(
2370 updates_stream.recv().await.unwrap(),
2371 RoomEventCacheUpdate::UpdateTimelineEvents(TimelineVectorDiffs { diffs, .. }) => {
2372 assert_eq!(diffs.len(), 1, "{diffs:#?}");
2373 assert_matches!(
2374 &diffs[0],
2375 VectorDiff::Insert { index: 0, value: event } => {
2376 assert_eq!(event.event_id(), Some(ev_id_0));
2377 }
2378 );
2379 }
2380 );
2381 }
2382
2383 {
2384 let room_event_cache = &room_event_cache_p1;
2385 let updates_stream = &mut updates_stream_p1;
2386
2387 let guard = room_event_cache.inner.state.read().await.unwrap();
2388
2389 assert!(guard.is_dirty().not());
2394
2395 assert_matches!(
2397 updates_stream.recv().await.unwrap(),
2398 RoomEventCacheUpdate::UpdateTimelineEvents(TimelineVectorDiffs { diffs, .. }) => {
2399 assert_eq!(diffs.len(), 2, "{diffs:#?}");
2400 assert_matches!(&diffs[0], VectorDiff::Clear);
2401 assert_matches!(
2402 &diffs[1],
2403 VectorDiff::Append { values: events } => {
2404 assert_eq!(events.len(), 1);
2405 assert_eq!(events[0].event_id(), Some(ev_id_1));
2406 }
2407 );
2408 }
2409 );
2410
2411 assert!(event_loaded(room_event_cache, ev_id_1).await);
2412 assert!(event_loaded(room_event_cache, ev_id_0).await.not());
2413
2414 drop(guard);
2420
2421 room_event_cache.pagination().run_backwards_once(1).await.unwrap();
2422 assert!(event_loaded(room_event_cache, ev_id_0).await);
2423
2424 assert_matches!(
2426 updates_stream.recv().await.unwrap(),
2427 RoomEventCacheUpdate::UpdateTimelineEvents(TimelineVectorDiffs { diffs, .. }) => {
2428 assert_eq!(diffs.len(), 1, "{diffs:#?}");
2429 assert_matches!(
2430 &diffs[0],
2431 VectorDiff::Insert { index: 0, value: event } => {
2432 assert_eq!(event.event_id(), Some(ev_id_0));
2433 }
2434 );
2435 }
2436 );
2437 }
2438 }
2439
2440 for _ in 0..3 {
2442 {
2443 let room_event_cache = &room_event_cache_p0;
2444 let updates_stream = &mut updates_stream_p0;
2445
2446 let guard = room_event_cache.inner.state.write().await.unwrap();
2447
2448 assert!(guard.is_dirty().not());
2450
2451 assert_matches!(
2453 updates_stream.recv().await.unwrap(),
2454 RoomEventCacheUpdate::UpdateTimelineEvents(TimelineVectorDiffs { diffs, .. }) => {
2455 assert_eq!(diffs.len(), 2, "{diffs:#?}");
2456 assert_matches!(&diffs[0], VectorDiff::Clear);
2457 assert_matches!(
2458 &diffs[1],
2459 VectorDiff::Append { values: events } => {
2460 assert_eq!(events.len(), 1);
2461 assert_eq!(events[0].event_id(), Some(ev_id_1));
2462 }
2463 );
2464 }
2465 );
2466
2467 drop(guard);
2470
2471 assert!(event_loaded(room_event_cache, ev_id_1).await);
2472 assert!(event_loaded(room_event_cache, ev_id_0).await.not());
2473
2474 room_event_cache.pagination().run_backwards_once(1).await.unwrap();
2475 assert!(event_loaded(room_event_cache, ev_id_0).await);
2476
2477 assert_matches!(
2479 updates_stream.recv().await.unwrap(),
2480 RoomEventCacheUpdate::UpdateTimelineEvents(TimelineVectorDiffs { diffs, .. }) => {
2481 assert_eq!(diffs.len(), 1, "{diffs:#?}");
2482 assert_matches!(
2483 &diffs[0],
2484 VectorDiff::Insert { index: 0, value: event } => {
2485 assert_eq!(event.event_id(), Some(ev_id_0));
2486 }
2487 );
2488 }
2489 );
2490 }
2491
2492 {
2493 let room_event_cache = &room_event_cache_p1;
2494 let updates_stream = &mut updates_stream_p1;
2495
2496 let guard = room_event_cache.inner.state.write().await.unwrap();
2497
2498 assert!(guard.is_dirty().not());
2500
2501 assert_matches!(
2503 updates_stream.recv().await.unwrap(),
2504 RoomEventCacheUpdate::UpdateTimelineEvents(TimelineVectorDiffs { diffs, .. }) => {
2505 assert_eq!(diffs.len(), 2, "{diffs:#?}");
2506 assert_matches!(&diffs[0], VectorDiff::Clear);
2507 assert_matches!(
2508 &diffs[1],
2509 VectorDiff::Append { values: events } => {
2510 assert_eq!(events.len(), 1);
2511 assert_eq!(events[0].event_id(), Some(ev_id_1));
2512 }
2513 );
2514 }
2515 );
2516
2517 drop(guard);
2520
2521 assert!(event_loaded(room_event_cache, ev_id_1).await);
2522 assert!(event_loaded(room_event_cache, ev_id_0).await.not());
2523
2524 room_event_cache.pagination().run_backwards_once(1).await.unwrap();
2525 assert!(event_loaded(room_event_cache, ev_id_0).await);
2526
2527 assert_matches!(
2529 updates_stream.recv().await.unwrap(),
2530 RoomEventCacheUpdate::UpdateTimelineEvents(TimelineVectorDiffs { diffs, .. }) => {
2531 assert_eq!(diffs.len(), 1, "{diffs:#?}");
2532 assert_matches!(
2533 &diffs[0],
2534 VectorDiff::Insert { index: 0, value: event } => {
2535 assert_eq!(event.event_id(), Some(ev_id_0));
2536 }
2537 );
2538 }
2539 );
2540 }
2541 }
2542 }
2543
2544 #[async_test]
2545 async fn test_load_when_dirty() {
2546 let room_id_0 = room_id!("!raclette:patate.ch");
2547 let room_id_1 = room_id!("!morbiflette:patate.ch");
2548
2549 let event_cache_store = MemoryStore::new();
2551
2552 let client_p0 = MockClientBuilder::new(None)
2554 .on_builder(|builder| {
2555 builder.store_config(
2556 StoreConfig::new(CrossProcessLockConfig::multi_process("process #0"))
2557 .event_cache_store(event_cache_store.clone()),
2558 )
2559 })
2560 .build()
2561 .await;
2562
2563 let client_p1 = MockClientBuilder::new(None)
2565 .on_builder(|builder| {
2566 builder.store_config(
2567 StoreConfig::new(CrossProcessLockConfig::multi_process("process #1"))
2568 .event_cache_store(event_cache_store),
2569 )
2570 })
2571 .build()
2572 .await;
2573
2574 let (room_event_cache_0_p0, room_event_cache_0_p1) = {
2576 let event_cache_p0 = client_p0.event_cache();
2577 event_cache_p0.subscribe().unwrap();
2578
2579 let event_cache_p1 = client_p1.event_cache();
2580 event_cache_p1.subscribe().unwrap();
2581
2582 client_p0.base_client().get_or_create_room(room_id_0, RoomState::Joined);
2583 client_p0.base_client().get_or_create_room(room_id_1, RoomState::Joined);
2584
2585 client_p1.base_client().get_or_create_room(room_id_0, RoomState::Joined);
2586 client_p1.base_client().get_or_create_room(room_id_1, RoomState::Joined);
2587
2588 let (room_event_cache_0_p0, _drop_handles) =
2589 client_p0.get_room(room_id_0).unwrap().event_cache().await.unwrap();
2590 let (room_event_cache_0_p1, _drop_handles) =
2591 client_p1.get_room(room_id_0).unwrap().event_cache().await.unwrap();
2592
2593 (room_event_cache_0_p0, room_event_cache_0_p1)
2594 };
2595
2596 {
2598 drop(room_event_cache_0_p0.inner.state.read().await.unwrap());
2599 drop(room_event_cache_0_p1.inner.state.read().await.unwrap());
2600 }
2601
2602 let (room_event_cache_1_p0, _) =
2606 client_p0.get_room(room_id_1).unwrap().event_cache().await.unwrap();
2607
2608 {
2610 let guard = room_event_cache_1_p0.inner.state.read().await.unwrap();
2611 assert!(guard.is_dirty().not());
2612 }
2613
2614 }
2617
2618 #[async_test]
2619 async fn test_uniq_read_marker() {
2620 let client = MockClientBuilder::new(None).build().await;
2621 let room_id = room_id!("!galette:saucisse.bzh");
2622 client.base_client().get_or_create_room(room_id, RoomState::Joined);
2623
2624 let event_cache = client.event_cache();
2625
2626 event_cache.subscribe().unwrap();
2627
2628 let mut generic_stream = event_cache.subscribe_to_room_generic_updates();
2629 let (room_event_cache, _drop_handles) = event_cache.room(room_id).await.unwrap();
2630 let (events, mut stream) = room_event_cache.subscribe().await.unwrap();
2631
2632 assert!(events.is_empty());
2633
2634 let read_marker_event = Raw::from_json_string(
2636 json!({
2637 "content": {
2638 "event_id": "$crepe:saucisse.bzh"
2639 },
2640 "room_id": "!galette:saucisse.bzh",
2641 "type": "m.fully_read"
2642 })
2643 .to_string(),
2644 )
2645 .unwrap();
2646 let account_data = vec![read_marker_event; 100];
2647
2648 room_event_cache
2649 .handle_joined_room_update(
2650 Default::default(),
2651 MaybeReceiptEventContent::none(),
2652 account_data,
2653 Default::default(),
2654 Default::default(),
2655 )
2656 .await
2657 .unwrap();
2658
2659 assert_matches!(
2661 stream.recv().await.unwrap(),
2662 RoomEventCacheUpdate::MoveReadMarkerTo { .. }
2663 );
2664
2665 assert!(stream.recv().now_or_never().is_none());
2666
2667 assert!(generic_stream.recv().now_or_never().is_none());
2669 }
2670
2671 async fn event_loaded(room_event_cache: &RoomEventCache, event_id: &EventId) -> bool {
2672 room_event_cache
2673 .rfind_map_event_in_memory_by(|event| {
2674 (event.event_id() == Some(event_id)).then_some(())
2675 })
2676 .await
2677 .unwrap()
2678 .is_some()
2679 }
2680}