Skip to main content

matrix_sdk_base/store/
memory_store.rs

1// Copyright 2021 The Matrix.org Foundation C.I.C.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15use std::{
16    cmp::Reverse,
17    collections::{BTreeMap, BTreeSet, HashMap},
18    sync::RwLock,
19};
20
21use async_trait::async_trait;
22use growable_bloom_filter::GrowableBloom;
23use matrix_sdk_common::{ROOM_VERSION_FALLBACK, ROOM_VERSION_RULES_FALLBACK, ttl::TtlValue};
24use ruma::{
25    CanonicalJsonObject, EventId, MilliSecondsSinceUnixEpoch, OwnedEventId, OwnedMxcUri,
26    OwnedRoomId, OwnedTransactionId, OwnedUserId, RoomId, TransactionId, UserId,
27    api::client::discovery::get_capabilities::v3::Capabilities,
28    canonical_json::{RedactedBecause, redact},
29    events::{
30        AnyGlobalAccountDataEvent, AnyRoomAccountDataEvent, AnyStrippedStateEvent,
31        AnySyncStateEvent, GlobalAccountDataEventType, RoomAccountDataEventType, StateEventType,
32        presence::PresenceEvent,
33        receipt::{Receipt, ReceiptThread, ReceiptType},
34        room::member::{MembershipState, StrippedRoomMemberEvent, SyncRoomMemberEvent},
35    },
36    profile::UserProfile,
37    serde::Raw,
38    time::Instant,
39};
40use tracing::{debug, instrument, warn};
41
42use super::{
43    DependentQueuedRequest, DependentQueuedRequestKind, QueuedRequestKind, Result, RoomInfo,
44    RoomLoadSettings, StateChanges, StateStore, StoreError, SupportedVersionsResponse,
45    WellKnownResponse,
46    send_queue::{ChildTransactionId, QueuedRequest, SentRequestKey},
47    traits::ComposerDraft,
48};
49use crate::{
50    MinimalRoomMemberEvent, RoomMemberships, StateStoreDataKey, StateStoreDataValue,
51    deserialized_responses::{DisplayName, RawAnySyncOrStrippedState},
52    store::{
53        QueueWedgeError, StoredThreadSubscription,
54        traits::{ThreadSubscriptionCatchupToken, compare_thread_subscription_bump_stamps},
55    },
56};
57
58#[derive(Debug, Default)]
59#[allow(clippy::type_complexity)]
60struct MemoryStoreInner {
61    recently_visited_rooms: HashMap<OwnedUserId, Vec<OwnedRoomId>>,
62    composer_drafts: HashMap<(OwnedRoomId, Option<OwnedEventId>), ComposerDraft>,
63    user_avatar_url: HashMap<OwnedUserId, OwnedMxcUri>,
64    sync_token: Option<String>,
65    supported_versions: Option<TtlValue<SupportedVersionsResponse>>,
66    well_known: Option<TtlValue<Option<WellKnownResponse>>>,
67    filters: HashMap<String, String>,
68    utd_hook_manager_data: Option<GrowableBloom>,
69    one_time_key_uploaded_error: bool,
70    account_data: HashMap<GlobalAccountDataEventType, Raw<AnyGlobalAccountDataEvent>>,
71    profiles: HashMap<OwnedRoomId, HashMap<OwnedUserId, MinimalRoomMemberEvent>>,
72    display_names: HashMap<OwnedRoomId, HashMap<DisplayName, BTreeSet<OwnedUserId>>>,
73    members: HashMap<OwnedRoomId, HashMap<OwnedUserId, MembershipState>>,
74    room_info: HashMap<OwnedRoomId, RoomInfo>,
75    room_state:
76        HashMap<OwnedRoomId, HashMap<StateEventType, HashMap<String, Raw<AnySyncStateEvent>>>>,
77    room_account_data:
78        HashMap<OwnedRoomId, HashMap<RoomAccountDataEventType, Raw<AnyRoomAccountDataEvent>>>,
79    stripped_room_state:
80        HashMap<OwnedRoomId, HashMap<StateEventType, HashMap<String, Raw<AnyStrippedStateEvent>>>>,
81    stripped_members: HashMap<OwnedRoomId, HashMap<OwnedUserId, MembershipState>>,
82    presence: HashMap<OwnedUserId, Raw<PresenceEvent>>,
83    room_user_receipts: HashMap<
84        OwnedRoomId,
85        HashMap<(String, Option<String>), HashMap<OwnedUserId, (OwnedEventId, Receipt)>>,
86    >,
87    room_event_receipts: HashMap<
88        OwnedRoomId,
89        HashMap<(String, Option<String>), HashMap<OwnedEventId, HashMap<OwnedUserId, Receipt>>>,
90    >,
91    custom: HashMap<Vec<u8>, Vec<u8>>,
92    send_queue_events: BTreeMap<OwnedRoomId, Vec<QueuedRequest>>,
93    dependent_send_queue_events: BTreeMap<OwnedRoomId, Vec<DependentQueuedRequest>>,
94    seen_knock_requests: BTreeMap<OwnedRoomId, BTreeMap<OwnedEventId, OwnedUserId>>,
95    thread_subscriptions: BTreeMap<OwnedRoomId, BTreeMap<OwnedEventId, StoredThreadSubscription>>,
96    thread_subscriptions_catchup_tokens: Option<Vec<ThreadSubscriptionCatchupToken>>,
97    global_profiles: HashMap<OwnedUserId, UserProfile>,
98    homeserver_capabilities: Option<TtlValue<Capabilities>>,
99}
100
101/// In-memory, non-persistent implementation of the `StateStore`.
102///
103/// Default if no other is configured at startup.
104#[derive(Debug, Default)]
105pub struct MemoryStore {
106    inner: RwLock<MemoryStoreInner>,
107}
108
109impl MemoryStore {
110    /// Create a new empty MemoryStore
111    pub fn new() -> Self {
112        Self::default()
113    }
114
115    fn get_user_room_receipt_event_impl(
116        &self,
117        room_id: &RoomId,
118        receipt_type: ReceiptType,
119        thread: ReceiptThread,
120        user_id: &UserId,
121    ) -> Option<(OwnedEventId, Receipt)> {
122        self.inner
123            .read()
124            .unwrap()
125            .room_user_receipts
126            .get(room_id)?
127            .get(&(receipt_type.to_string(), thread.as_str().map(ToOwned::to_owned)))?
128            .get(user_id)
129            .cloned()
130    }
131
132    fn get_event_room_receipt_events_impl(
133        &self,
134        room_id: &RoomId,
135        receipt_type: ReceiptType,
136        thread: ReceiptThread,
137        event_id: &EventId,
138    ) -> Option<Vec<(OwnedUserId, Receipt)>> {
139        Some(
140            self.inner
141                .read()
142                .unwrap()
143                .room_event_receipts
144                .get(room_id)?
145                .get(&(receipt_type.to_string(), thread.as_str().map(ToOwned::to_owned)))?
146                .get(event_id)?
147                .iter()
148                .map(|(key, value)| (key.clone(), value.clone()))
149                .collect(),
150        )
151    }
152}
153
154#[cfg_attr(target_family = "wasm", async_trait(?Send))]
155#[cfg_attr(not(target_family = "wasm"), async_trait)]
156impl StateStore for MemoryStore {
157    type Error = StoreError;
158
159    async fn close(&self) -> Result<(), Self::Error> {
160        Ok(())
161    }
162
163    async fn reopen(&self) -> Result<(), Self::Error> {
164        Ok(())
165    }
166
167    async fn get_kv_data(&self, key: StateStoreDataKey<'_>) -> Result<Option<StateStoreDataValue>> {
168        let inner = self.inner.read().unwrap();
169
170        Ok(match key {
171            StateStoreDataKey::SyncToken => {
172                inner.sync_token.clone().map(StateStoreDataValue::SyncToken)
173            }
174            StateStoreDataKey::SupportedVersions => {
175                inner.supported_versions.clone().map(StateStoreDataValue::SupportedVersions)
176            }
177            StateStoreDataKey::WellKnown => {
178                inner.well_known.clone().map(StateStoreDataValue::WellKnown)
179            }
180            StateStoreDataKey::Filter(filter_name) => {
181                inner.filters.get(filter_name).cloned().map(StateStoreDataValue::Filter)
182            }
183            StateStoreDataKey::UserAvatarUrl(user_id) => {
184                inner.user_avatar_url.get(user_id).cloned().map(StateStoreDataValue::UserAvatarUrl)
185            }
186            StateStoreDataKey::RecentlyVisitedRooms(user_id) => inner
187                .recently_visited_rooms
188                .get(user_id)
189                .cloned()
190                .map(StateStoreDataValue::RecentlyVisitedRooms),
191            StateStoreDataKey::UtdHookManagerData => {
192                inner.utd_hook_manager_data.clone().map(StateStoreDataValue::UtdHookManagerData)
193            }
194            StateStoreDataKey::OneTimeKeyAlreadyUploaded => inner
195                .one_time_key_uploaded_error
196                .then_some(StateStoreDataValue::OneTimeKeyAlreadyUploaded),
197            StateStoreDataKey::ComposerDraft(room_id, thread_root) => {
198                let key = (room_id.to_owned(), thread_root.map(ToOwned::to_owned));
199                inner.composer_drafts.get(&key).cloned().map(StateStoreDataValue::ComposerDraft)
200            }
201            StateStoreDataKey::SeenKnockRequests(room_id) => inner
202                .seen_knock_requests
203                .get(room_id)
204                .cloned()
205                .map(StateStoreDataValue::SeenKnockRequests),
206            StateStoreDataKey::ThreadSubscriptionsCatchupTokens => inner
207                .thread_subscriptions_catchup_tokens
208                .clone()
209                .map(StateStoreDataValue::ThreadSubscriptionsCatchupTokens),
210            StateStoreDataKey::HomeserverCapabilities => inner
211                .homeserver_capabilities
212                .clone()
213                .map(StateStoreDataValue::HomeserverCapabilities),
214        })
215    }
216
217    async fn set_kv_data(
218        &self,
219        key: StateStoreDataKey<'_>,
220        value: StateStoreDataValue,
221    ) -> Result<()> {
222        let mut inner = self.inner.write().unwrap();
223        match key {
224            StateStoreDataKey::SyncToken => {
225                inner.sync_token =
226                    Some(value.into_sync_token().expect("Session data not a sync token"))
227            }
228            StateStoreDataKey::Filter(filter_name) => {
229                inner.filters.insert(
230                    filter_name.to_owned(),
231                    value.into_filter().expect("Session data not a filter"),
232                );
233            }
234            StateStoreDataKey::UserAvatarUrl(user_id) => {
235                inner.user_avatar_url.insert(
236                    user_id.to_owned(),
237                    value.into_user_avatar_url().expect("Session data not a user avatar url"),
238                );
239            }
240            StateStoreDataKey::RecentlyVisitedRooms(user_id) => {
241                inner.recently_visited_rooms.insert(
242                    user_id.to_owned(),
243                    value
244                        .into_recently_visited_rooms()
245                        .expect("Session data not a list of recently visited rooms"),
246                );
247            }
248            StateStoreDataKey::UtdHookManagerData => {
249                inner.utd_hook_manager_data = Some(
250                    value
251                        .into_utd_hook_manager_data()
252                        .expect("Session data not the hook manager data"),
253                );
254            }
255            StateStoreDataKey::OneTimeKeyAlreadyUploaded => {
256                inner.one_time_key_uploaded_error = true;
257            }
258            StateStoreDataKey::ComposerDraft(room_id, thread_root) => {
259                inner.composer_drafts.insert(
260                    (room_id.to_owned(), thread_root.map(ToOwned::to_owned)),
261                    value.into_composer_draft().expect("Session data not a composer draft"),
262                );
263            }
264            StateStoreDataKey::SupportedVersions => {
265                inner.supported_versions = Some(
266                    value
267                        .into_supported_versions()
268                        .expect("Session data not containing supported versions"),
269                );
270            }
271            StateStoreDataKey::WellKnown => {
272                inner.well_known =
273                    Some(value.into_well_known().expect("Session data not containing well-known"));
274            }
275            StateStoreDataKey::SeenKnockRequests(room_id) => {
276                inner.seen_knock_requests.insert(
277                    room_id.to_owned(),
278                    value
279                        .into_seen_knock_requests()
280                        .expect("Session data is not a set of seen join request ids"),
281                );
282            }
283            StateStoreDataKey::ThreadSubscriptionsCatchupTokens => {
284                inner.thread_subscriptions_catchup_tokens =
285                    Some(value.into_thread_subscriptions_catchup_tokens().expect(
286                        "Session data is not a list of thread subscription catchup tokens",
287                    ));
288            }
289            StateStoreDataKey::HomeserverCapabilities => {
290                inner.homeserver_capabilities = Some(
291                    value
292                        .into_homeserver_capabilities()
293                        .expect("Session data is not a homeserver capabilities"),
294                );
295            }
296        }
297
298        Ok(())
299    }
300
301    async fn remove_kv_data(&self, key: StateStoreDataKey<'_>) -> Result<()> {
302        let mut inner = self.inner.write().unwrap();
303        match key {
304            StateStoreDataKey::SyncToken => inner.sync_token = None,
305            StateStoreDataKey::SupportedVersions => inner.supported_versions = None,
306            StateStoreDataKey::WellKnown => inner.well_known = None,
307            StateStoreDataKey::Filter(filter_name) => {
308                inner.filters.remove(filter_name);
309            }
310            StateStoreDataKey::UserAvatarUrl(user_id) => {
311                inner.user_avatar_url.remove(user_id);
312            }
313            StateStoreDataKey::RecentlyVisitedRooms(user_id) => {
314                inner.recently_visited_rooms.remove(user_id);
315            }
316            StateStoreDataKey::UtdHookManagerData => inner.utd_hook_manager_data = None,
317            StateStoreDataKey::OneTimeKeyAlreadyUploaded => {
318                inner.one_time_key_uploaded_error = false
319            }
320            StateStoreDataKey::ComposerDraft(room_id, thread_root) => {
321                let key = (room_id.to_owned(), thread_root.map(ToOwned::to_owned));
322                inner.composer_drafts.remove(&key);
323            }
324            StateStoreDataKey::SeenKnockRequests(room_id) => {
325                inner.seen_knock_requests.remove(room_id);
326            }
327            StateStoreDataKey::ThreadSubscriptionsCatchupTokens => {
328                inner.thread_subscriptions_catchup_tokens = None;
329            }
330            StateStoreDataKey::HomeserverCapabilities => inner.homeserver_capabilities = None,
331        }
332        Ok(())
333    }
334
335    #[instrument(skip(self, changes))]
336    async fn save_changes(&self, changes: &StateChanges) -> Result<()> {
337        let now = Instant::now();
338
339        let mut inner = self.inner.write().unwrap();
340
341        if let Some(s) = &changes.sync_token {
342            inner.sync_token = Some(s.to_owned());
343        }
344
345        for (room, users) in &changes.profiles_to_delete {
346            let Some(room_profiles) = inner.profiles.get_mut(room) else {
347                continue;
348            };
349            for user in users {
350                room_profiles.remove(user);
351            }
352        }
353
354        for (room, users) in &changes.profiles {
355            for (user_id, profile) in users {
356                inner
357                    .profiles
358                    .entry(room.clone())
359                    .or_default()
360                    .insert(user_id.clone(), profile.clone());
361            }
362        }
363
364        for (room, map) in &changes.ambiguity_maps {
365            for (display_name, display_names) in map {
366                inner
367                    .display_names
368                    .entry(room.clone())
369                    .or_default()
370                    .insert(display_name.clone(), display_names.clone());
371            }
372        }
373
374        for (event_type, event) in &changes.account_data {
375            inner.account_data.insert(event_type.clone(), event.clone());
376        }
377
378        for (room, events) in &changes.room_account_data {
379            for (event_type, event) in events {
380                inner
381                    .room_account_data
382                    .entry(room.clone())
383                    .or_default()
384                    .insert(event_type.clone(), event.clone());
385            }
386        }
387
388        for (room, event_types) in &changes.state {
389            for (event_type, events) in event_types {
390                for (state_key, raw_event) in events {
391                    inner
392                        .room_state
393                        .entry(room.clone())
394                        .or_default()
395                        .entry(event_type.clone())
396                        .or_default()
397                        .insert(state_key.to_owned(), raw_event.clone());
398                    inner.stripped_room_state.remove(room);
399
400                    if *event_type == StateEventType::RoomMember {
401                        let event =
402                            match raw_event.deserialize_as_unchecked::<SyncRoomMemberEvent>() {
403                                Ok(ev) => ev,
404                                Err(e) => {
405                                    let event_id: Option<String> =
406                                        raw_event.get_field("event_id").ok().flatten();
407                                    debug!(event_id, "Failed to deserialize member event: {e}");
408                                    continue;
409                                }
410                            };
411
412                        inner.stripped_members.remove(room);
413
414                        inner
415                            .members
416                            .entry(room.clone())
417                            .or_default()
418                            .insert(event.state_key().to_owned(), event.membership().clone());
419                    }
420                }
421            }
422        }
423
424        for (room_id, info) in &changes.room_infos {
425            inner.room_info.insert(room_id.clone(), info.clone());
426        }
427
428        for (sender, event) in &changes.presence {
429            inner.presence.insert(sender.clone(), event.clone());
430        }
431
432        for (room, event_types) in &changes.stripped_state {
433            for (event_type, events) in event_types {
434                for (state_key, raw_event) in events {
435                    inner
436                        .stripped_room_state
437                        .entry(room.clone())
438                        .or_default()
439                        .entry(event_type.clone())
440                        .or_default()
441                        .insert(state_key.to_owned(), raw_event.clone());
442
443                    if *event_type == StateEventType::RoomMember {
444                        let event =
445                            match raw_event.deserialize_as_unchecked::<StrippedRoomMemberEvent>() {
446                                Ok(ev) => ev,
447                                Err(e) => {
448                                    let event_id: Option<String> =
449                                        raw_event.get_field("event_id").ok().flatten();
450                                    debug!(
451                                        event_id,
452                                        "Failed to deserialize stripped member event: {e}"
453                                    );
454                                    continue;
455                                }
456                            };
457
458                        inner
459                            .stripped_members
460                            .entry(room.clone())
461                            .or_default()
462                            .insert(event.state_key, event.content.membership.clone());
463                    }
464                }
465            }
466        }
467
468        for (room, content) in &changes.receipts {
469            for (event_id, receipts) in &content.0 {
470                for (receipt_type, receipts) in receipts {
471                    for (user_id, receipt) in receipts {
472                        let thread = receipt.thread.as_str().map(ToOwned::to_owned);
473                        // Add the receipt to the room user receipts
474                        if let Some((old_event, _)) = inner
475                            .room_user_receipts
476                            .entry(room.clone())
477                            .or_default()
478                            .entry((receipt_type.to_string(), thread.clone()))
479                            .or_default()
480                            .insert(user_id.clone(), (event_id.clone(), receipt.clone()))
481                        {
482                            // Remove the old receipt from the room event receipts
483                            if let Some(receipt_map) = inner.room_event_receipts.get_mut(room)
484                                && let Some(event_map) =
485                                    receipt_map.get_mut(&(receipt_type.to_string(), thread.clone()))
486                                && let Some(user_map) = event_map.get_mut(&old_event)
487                            {
488                                user_map.remove(user_id);
489                            }
490                        }
491
492                        // Add the receipt to the room event receipts
493                        inner
494                            .room_event_receipts
495                            .entry(room.clone())
496                            .or_default()
497                            .entry((receipt_type.to_string(), thread))
498                            .or_default()
499                            .entry(event_id.clone())
500                            .or_default()
501                            .insert(user_id.clone(), receipt.clone());
502                    }
503                }
504            }
505        }
506
507        let make_redaction_rules = |room_info: &HashMap<OwnedRoomId, RoomInfo>, room_id| {
508            room_info.get(room_id).map(|info| info.room_version_rules_or_default()).unwrap_or_else(|| {
509                warn!(
510                    ?room_id,
511                    "Unable to get the room version rules, defaulting to rules for room version {ROOM_VERSION_FALLBACK}"
512                );
513                ROOM_VERSION_RULES_FALLBACK
514            }).redaction
515        };
516
517        let inner = &mut *inner;
518        for (room_id, redactions) in &changes.redactions {
519            let mut redaction_rules = None;
520
521            if let Some(room) = inner.room_state.get_mut(room_id) {
522                for ref_room_mu in room.values_mut() {
523                    for raw_evt in ref_room_mu.values_mut() {
524                        if let Ok(Some(event_id)) = raw_evt.get_field::<OwnedEventId>("event_id")
525                            && let Some(redaction) = redactions.get(&event_id)
526                        {
527                            let redacted = redact(
528                                raw_evt.deserialize_as::<CanonicalJsonObject>()?,
529                                redaction_rules.get_or_insert_with(|| {
530                                    make_redaction_rules(&inner.room_info, room_id)
531                                }),
532                                Some(RedactedBecause::from_raw_event(redaction)?),
533                            )
534                            .map_err(StoreError::Redaction)?;
535                            *raw_evt = Raw::new(&redacted)?.cast_unchecked();
536                        }
537                    }
538                }
539            }
540        }
541
542        for (user_id, profile_update) in &changes.global_profiles {
543            let mut profile = inner.global_profiles.get(user_id).cloned().unwrap_or_default();
544            profile.merge(profile_update.clone());
545            inner.global_profiles.insert(user_id.clone(), profile);
546        }
547
548        debug!("Saved changes in {:?}", now.elapsed());
549
550        Ok(())
551    }
552
553    async fn get_presence_event(&self, user_id: &UserId) -> Result<Option<Raw<PresenceEvent>>> {
554        Ok(self.inner.read().unwrap().presence.get(user_id).cloned())
555    }
556
557    async fn get_presence_events(
558        &self,
559        user_ids: &[OwnedUserId],
560    ) -> Result<Vec<Raw<PresenceEvent>>> {
561        let presence = &self.inner.read().unwrap().presence;
562        Ok(user_ids.iter().filter_map(|user_id| presence.get(user_id).cloned()).collect())
563    }
564
565    async fn get_state_event(
566        &self,
567        room_id: &RoomId,
568        event_type: StateEventType,
569        state_key: &str,
570    ) -> Result<Option<RawAnySyncOrStrippedState>> {
571        Ok(self
572            .get_state_events_for_keys(room_id, event_type, &[state_key])
573            .await?
574            .into_iter()
575            .next())
576    }
577
578    async fn get_state_events(
579        &self,
580        room_id: &RoomId,
581        event_type: StateEventType,
582    ) -> Result<Vec<RawAnySyncOrStrippedState>> {
583        fn get_events<T>(
584            state_map: &HashMap<OwnedRoomId, HashMap<StateEventType, HashMap<String, Raw<T>>>>,
585            room_id: &RoomId,
586            event_type: &StateEventType,
587            to_enum: fn(Raw<T>) -> RawAnySyncOrStrippedState,
588        ) -> Option<Vec<RawAnySyncOrStrippedState>> {
589            let state_events = state_map.get(room_id)?.get(event_type)?;
590            Some(state_events.values().cloned().map(to_enum).collect())
591        }
592
593        let inner = self.inner.read().unwrap();
594        Ok(get_events(
595            &inner.stripped_room_state,
596            room_id,
597            &event_type,
598            RawAnySyncOrStrippedState::Stripped,
599        )
600        .or_else(|| {
601            get_events(&inner.room_state, room_id, &event_type, RawAnySyncOrStrippedState::Sync)
602        })
603        .unwrap_or_default())
604    }
605
606    async fn get_state_events_for_keys(
607        &self,
608        room_id: &RoomId,
609        event_type: StateEventType,
610        state_keys: &[&str],
611    ) -> Result<Vec<RawAnySyncOrStrippedState>, Self::Error> {
612        let inner = self.inner.read().unwrap();
613
614        if let Some(stripped_state_events) =
615            inner.stripped_room_state.get(room_id).and_then(|events| events.get(&event_type))
616        {
617            Ok(state_keys
618                .iter()
619                .filter_map(|k| {
620                    stripped_state_events
621                        .get(*k)
622                        .map(|e| RawAnySyncOrStrippedState::Stripped(e.clone()))
623                })
624                .collect())
625        } else if let Some(sync_state_events) =
626            inner.room_state.get(room_id).and_then(|events| events.get(&event_type))
627        {
628            Ok(state_keys
629                .iter()
630                .filter_map(|k| {
631                    sync_state_events.get(*k).map(|e| RawAnySyncOrStrippedState::Sync(e.clone()))
632                })
633                .collect())
634        } else {
635            Ok(Vec::new())
636        }
637    }
638
639    async fn get_profile(
640        &self,
641        room_id: &RoomId,
642        user_id: &UserId,
643    ) -> Result<Option<MinimalRoomMemberEvent>> {
644        Ok(self
645            .inner
646            .read()
647            .unwrap()
648            .profiles
649            .get(room_id)
650            .and_then(|room_profiles| room_profiles.get(user_id))
651            .cloned())
652    }
653
654    async fn get_profiles<'a>(
655        &self,
656        room_id: &RoomId,
657        user_ids: &'a [OwnedUserId],
658    ) -> Result<BTreeMap<&'a UserId, MinimalRoomMemberEvent>> {
659        if user_ids.is_empty() {
660            return Ok(BTreeMap::new());
661        }
662
663        let profiles = &self.inner.read().unwrap().profiles;
664        let Some(room_profiles) = profiles.get(room_id) else {
665            return Ok(BTreeMap::new());
666        };
667
668        Ok(user_ids
669            .iter()
670            .filter_map(|user_id| room_profiles.get(user_id).map(|p| (&**user_id, p.clone())))
671            .collect())
672    }
673
674    #[instrument(skip(self, memberships))]
675    async fn get_user_ids(
676        &self,
677        room_id: &RoomId,
678        memberships: RoomMemberships,
679    ) -> Result<Vec<OwnedUserId>> {
680        /// Get the user IDs for the given room with the given memberships and
681        /// stripped state.
682        ///
683        /// If `memberships` is empty, returns all user IDs in the room with the
684        /// given stripped state.
685        fn get_user_ids_inner(
686            members: &HashMap<OwnedRoomId, HashMap<OwnedUserId, MembershipState>>,
687            room_id: &RoomId,
688            memberships: RoomMemberships,
689        ) -> Vec<OwnedUserId> {
690            members
691                .get(room_id)
692                .map(|members| {
693                    members
694                        .iter()
695                        .filter_map(|(user_id, membership)| {
696                            memberships.matches(membership).then_some(user_id)
697                        })
698                        .cloned()
699                        .collect()
700                })
701                .unwrap_or_default()
702        }
703        let inner = self.inner.read().unwrap();
704        let v = get_user_ids_inner(&inner.stripped_members, room_id, memberships);
705        if !v.is_empty() {
706            return Ok(v);
707        }
708        Ok(get_user_ids_inner(&inner.members, room_id, memberships))
709    }
710
711    async fn get_room_infos(&self, room_load_settings: &RoomLoadSettings) -> Result<Vec<RoomInfo>> {
712        let memory_store_inner = self.inner.read().unwrap();
713        let room_infos = &memory_store_inner.room_info;
714
715        Ok(match room_load_settings {
716            RoomLoadSettings::All => room_infos.values().cloned().collect(),
717
718            RoomLoadSettings::One(room_id) => match room_infos.get(room_id) {
719                Some(room_info) => vec![room_info.clone()],
720                None => vec![],
721            },
722        })
723    }
724
725    async fn get_users_with_display_name(
726        &self,
727        room_id: &RoomId,
728        display_name: &DisplayName,
729    ) -> Result<BTreeSet<OwnedUserId>> {
730        Ok(self
731            .inner
732            .read()
733            .unwrap()
734            .display_names
735            .get(room_id)
736            .and_then(|room_names| room_names.get(display_name).cloned())
737            .unwrap_or_default())
738    }
739
740    async fn get_users_with_display_names<'a>(
741        &self,
742        room_id: &RoomId,
743        display_names: &'a [DisplayName],
744    ) -> Result<HashMap<&'a DisplayName, BTreeSet<OwnedUserId>>> {
745        if display_names.is_empty() {
746            return Ok(HashMap::new());
747        }
748
749        let inner = self.inner.read().unwrap();
750        let Some(room_names) = inner.display_names.get(room_id) else {
751            return Ok(HashMap::new());
752        };
753
754        Ok(display_names.iter().filter_map(|n| room_names.get(n).map(|d| (n, d.clone()))).collect())
755    }
756
757    async fn get_account_data_event(
758        &self,
759        event_type: GlobalAccountDataEventType,
760    ) -> Result<Option<Raw<AnyGlobalAccountDataEvent>>> {
761        Ok(self.inner.read().unwrap().account_data.get(&event_type).cloned())
762    }
763
764    async fn get_room_account_data_event(
765        &self,
766        room_id: &RoomId,
767        event_type: RoomAccountDataEventType,
768    ) -> Result<Option<Raw<AnyRoomAccountDataEvent>>> {
769        Ok(self
770            .inner
771            .read()
772            .unwrap()
773            .room_account_data
774            .get(room_id)
775            .and_then(|m| m.get(&event_type))
776            .cloned())
777    }
778
779    async fn get_user_room_receipt_event(
780        &self,
781        room_id: &RoomId,
782        receipt_type: ReceiptType,
783        thread: ReceiptThread,
784        user_id: &UserId,
785    ) -> Result<Option<(OwnedEventId, Receipt)>> {
786        Ok(self.get_user_room_receipt_event_impl(room_id, receipt_type, thread, user_id))
787    }
788
789    async fn get_event_room_receipt_events(
790        &self,
791        room_id: &RoomId,
792        receipt_type: ReceiptType,
793        thread: ReceiptThread,
794        event_id: &EventId,
795    ) -> Result<Vec<(OwnedUserId, Receipt)>> {
796        Ok(self
797            .get_event_room_receipt_events_impl(room_id, receipt_type, thread, event_id)
798            .unwrap_or_default())
799    }
800
801    async fn get_custom_value(&self, key: &[u8]) -> Result<Option<Vec<u8>>> {
802        Ok(self.inner.read().unwrap().custom.get(key).cloned())
803    }
804
805    async fn set_custom_value(&self, key: &[u8], value: Vec<u8>) -> Result<Option<Vec<u8>>> {
806        Ok(self.inner.write().unwrap().custom.insert(key.to_vec(), value))
807    }
808
809    async fn remove_custom_value(&self, key: &[u8]) -> Result<Option<Vec<u8>>> {
810        Ok(self.inner.write().unwrap().custom.remove(key))
811    }
812
813    async fn remove_room(&self, room_id: &RoomId) -> Result<()> {
814        let mut inner = self.inner.write().unwrap();
815
816        inner.profiles.remove(room_id);
817        inner.display_names.remove(room_id);
818        inner.members.remove(room_id);
819        inner.room_info.remove(room_id);
820        inner.room_state.remove(room_id);
821        inner.room_account_data.remove(room_id);
822        inner.stripped_room_state.remove(room_id);
823        inner.stripped_members.remove(room_id);
824        inner.room_user_receipts.remove(room_id);
825        inner.room_event_receipts.remove(room_id);
826        inner.send_queue_events.remove(room_id);
827        inner.dependent_send_queue_events.remove(room_id);
828        inner.thread_subscriptions.remove(room_id);
829
830        Ok(())
831    }
832
833    async fn save_send_queue_request(
834        &self,
835        room_id: &RoomId,
836        transaction_id: OwnedTransactionId,
837        created_at: MilliSecondsSinceUnixEpoch,
838        kind: QueuedRequestKind,
839        priority: usize,
840    ) -> Result<(), Self::Error> {
841        self.inner
842            .write()
843            .unwrap()
844            .send_queue_events
845            .entry(room_id.to_owned())
846            .or_default()
847            .push(QueuedRequest { kind, transaction_id, error: None, priority, created_at });
848        Ok(())
849    }
850
851    async fn update_send_queue_request(
852        &self,
853        room_id: &RoomId,
854        transaction_id: &TransactionId,
855        kind: QueuedRequestKind,
856    ) -> Result<bool, Self::Error> {
857        if let Some(entry) = self
858            .inner
859            .write()
860            .unwrap()
861            .send_queue_events
862            .entry(room_id.to_owned())
863            .or_default()
864            .iter_mut()
865            .find(|item| item.transaction_id == transaction_id)
866        {
867            entry.kind = kind;
868            entry.error = None;
869            Ok(true)
870        } else {
871            Ok(false)
872        }
873    }
874
875    async fn remove_send_queue_request(
876        &self,
877        room_id: &RoomId,
878        transaction_id: &TransactionId,
879    ) -> Result<bool, Self::Error> {
880        let mut inner = self.inner.write().unwrap();
881        let q = &mut inner.send_queue_events;
882
883        let entry = q.get_mut(room_id);
884        if let Some(entry) = entry {
885            // Find the event by id in its room queue, and remove it if present.
886            if let Some(pos) = entry.iter().position(|item| item.transaction_id == transaction_id) {
887                entry.remove(pos);
888                // And if this was the last event before removal, remove the entire room entry.
889                if entry.is_empty() {
890                    q.remove(room_id);
891                }
892                return Ok(true);
893            }
894        }
895
896        Ok(false)
897    }
898
899    async fn load_send_queue_requests(
900        &self,
901        room_id: &RoomId,
902    ) -> Result<Vec<QueuedRequest>, Self::Error> {
903        let mut ret = self
904            .inner
905            .write()
906            .unwrap()
907            .send_queue_events
908            .entry(room_id.to_owned())
909            .or_default()
910            .clone();
911        // Inverted order of priority, use stable sort to keep insertion order.
912        ret.sort_by_key(|item| Reverse(item.priority));
913        Ok(ret)
914    }
915
916    async fn update_send_queue_request_status(
917        &self,
918        room_id: &RoomId,
919        transaction_id: &TransactionId,
920        error: Option<QueueWedgeError>,
921    ) -> Result<(), Self::Error> {
922        if let Some(entry) = self
923            .inner
924            .write()
925            .unwrap()
926            .send_queue_events
927            .entry(room_id.to_owned())
928            .or_default()
929            .iter_mut()
930            .find(|item| item.transaction_id == transaction_id)
931        {
932            entry.error = error;
933        }
934        Ok(())
935    }
936
937    async fn load_rooms_with_unsent_requests(&self) -> Result<Vec<OwnedRoomId>, Self::Error> {
938        Ok(self.inner.read().unwrap().send_queue_events.keys().cloned().collect())
939    }
940
941    async fn save_dependent_queued_request(
942        &self,
943        room: &RoomId,
944        parent_transaction_id: &TransactionId,
945        own_transaction_id: ChildTransactionId,
946        created_at: MilliSecondsSinceUnixEpoch,
947        content: DependentQueuedRequestKind,
948    ) -> Result<(), Self::Error> {
949        self.inner
950            .write()
951            .unwrap()
952            .dependent_send_queue_events
953            .entry(room.to_owned())
954            .or_default()
955            .push(DependentQueuedRequest {
956                kind: content,
957                parent_transaction_id: parent_transaction_id.to_owned(),
958                own_transaction_id,
959                parent_key: None,
960                created_at,
961            });
962        Ok(())
963    }
964
965    async fn mark_dependent_queued_requests_as_ready(
966        &self,
967        room: &RoomId,
968        parent_txn_id: &TransactionId,
969        sent_parent_key: SentRequestKey,
970    ) -> Result<usize, Self::Error> {
971        let mut inner = self.inner.write().unwrap();
972        let dependents = inner.dependent_send_queue_events.entry(room.to_owned()).or_default();
973        let mut num_updated = 0;
974        for d in dependents.iter_mut().filter(|item| item.parent_transaction_id == parent_txn_id) {
975            d.parent_key = Some(sent_parent_key.clone());
976            num_updated += 1;
977        }
978        Ok(num_updated)
979    }
980
981    async fn update_dependent_queued_request(
982        &self,
983        room: &RoomId,
984        own_transaction_id: &ChildTransactionId,
985        new_content: DependentQueuedRequestKind,
986    ) -> Result<bool, Self::Error> {
987        let mut inner = self.inner.write().unwrap();
988        let dependents = inner.dependent_send_queue_events.entry(room.to_owned()).or_default();
989        for d in dependents.iter_mut() {
990            if d.own_transaction_id == *own_transaction_id {
991                d.kind = new_content;
992                return Ok(true);
993            }
994        }
995        Ok(false)
996    }
997
998    async fn remove_dependent_queued_request(
999        &self,
1000        room: &RoomId,
1001        txn_id: &ChildTransactionId,
1002    ) -> Result<bool, Self::Error> {
1003        let mut inner = self.inner.write().unwrap();
1004        let dependents = inner.dependent_send_queue_events.entry(room.to_owned()).or_default();
1005        if let Some(pos) = dependents.iter().position(|item| item.own_transaction_id == *txn_id) {
1006            dependents.remove(pos);
1007            Ok(true)
1008        } else {
1009            Ok(false)
1010        }
1011    }
1012
1013    async fn load_dependent_queued_requests(
1014        &self,
1015        room: &RoomId,
1016    ) -> Result<Vec<DependentQueuedRequest>, Self::Error> {
1017        Ok(self
1018            .inner
1019            .read()
1020            .unwrap()
1021            .dependent_send_queue_events
1022            .get(room)
1023            .cloned()
1024            .unwrap_or_default())
1025    }
1026
1027    async fn upsert_thread_subscriptions(
1028        &self,
1029        updates: Vec<(&RoomId, &EventId, StoredThreadSubscription)>,
1030    ) -> Result<(), Self::Error> {
1031        let mut inner = self.inner.write().unwrap();
1032
1033        for (room_id, thread_id, mut new) in updates {
1034            let room_subs = inner.thread_subscriptions.entry(room_id.to_owned()).or_default();
1035
1036            if let Some(previous) = room_subs.get(thread_id) {
1037                if *previous == new {
1038                    continue;
1039                }
1040                if !compare_thread_subscription_bump_stamps(
1041                    previous.bump_stamp,
1042                    &mut new.bump_stamp,
1043                ) {
1044                    continue;
1045                }
1046            }
1047
1048            room_subs.insert(thread_id.to_owned(), new);
1049        }
1050
1051        Ok(())
1052    }
1053
1054    async fn load_thread_subscription(
1055        &self,
1056        room: &RoomId,
1057        thread_id: &EventId,
1058    ) -> Result<Option<StoredThreadSubscription>, Self::Error> {
1059        let inner = self.inner.read().unwrap();
1060        Ok(inner
1061            .thread_subscriptions
1062            .get(room)
1063            .and_then(|subscriptions| subscriptions.get(thread_id))
1064            .copied())
1065    }
1066
1067    async fn remove_thread_subscription(
1068        &self,
1069        room: &RoomId,
1070        thread_id: &EventId,
1071    ) -> Result<(), Self::Error> {
1072        let mut inner = self.inner.write().unwrap();
1073
1074        let Some(room_subs) = inner.thread_subscriptions.get_mut(room) else {
1075            return Ok(());
1076        };
1077
1078        room_subs.remove(thread_id);
1079
1080        if room_subs.is_empty() {
1081            // If there are no more subscriptions for this room, remove the room entry.
1082            inner.thread_subscriptions.remove(room);
1083        }
1084
1085        Ok(())
1086    }
1087
1088    async fn get_global_profile(
1089        &self,
1090        user_id: &UserId,
1091    ) -> Result<Option<UserProfile>, Self::Error> {
1092        let inner = self.inner.read().unwrap();
1093        Ok(inner.global_profiles.get(user_id).cloned())
1094    }
1095
1096    async fn get_global_profiles<'a>(
1097        &self,
1098        user_ids: &'a [OwnedUserId],
1099    ) -> Result<BTreeMap<&'a UserId, UserProfile>, Self::Error> {
1100        let inner = self.inner.read().unwrap();
1101        Ok(user_ids
1102            .iter()
1103            .filter_map(|user_id| {
1104                inner.global_profiles.get(user_id).map(|profile| (&**user_id, profile.clone()))
1105            })
1106            .collect())
1107    }
1108
1109    async fn optimize(&self) -> Result<(), Self::Error> {
1110        Ok(())
1111    }
1112
1113    async fn get_size(&self) -> Result<Option<usize>, Self::Error> {
1114        Ok(None)
1115    }
1116}
1117
1118#[cfg(test)]
1119mod tests {
1120    use super::{MemoryStore, Result, StateStore};
1121
1122    async fn get_store() -> Result<impl StateStore> {
1123        Ok(MemoryStore::new())
1124    }
1125
1126    statestore_integration_tests!();
1127}