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