matrix_sdk_base/event_cache/store/
memory_store.rs1use std::{
16 collections::{HashMap, HashSet, hash_map::Entry},
17 sync::{Arc, RwLock as StdRwLock},
18};
19
20use async_trait::async_trait;
21use matrix_sdk_common::{
22 cross_process_lock::{
23 CrossProcessLockGeneration,
24 memory_store_helper::{Lease, try_take_leased_lock},
25 },
26 linked_chunk::{
27 ChunkIdentifier, ChunkIdentifierGenerator, ChunkMetadata, LinkedChunkId, Position,
28 RawChunk, Update, relational::RelationalLinkedChunk,
29 },
30};
31use ruma::{EventId, OwnedEventId, OwnedRoomId, RoomId, events::relation::RelationType};
32use tracing::error;
33
34use super::{
35 super::{Event, Gap, thread::ThreadInfo},
36 EventCacheStore, EventCacheStoreError, Result, extract_event_relation,
37};
38
39#[derive(Debug, Clone)]
48pub struct MemoryStore {
49 inner: Arc<StdRwLock<MemoryStoreInner>>,
50}
51
52#[derive(Debug)]
53struct MemoryStoreInner {
54 leases: HashMap<String, Lease>,
56
57 events: RelationalLinkedChunk<OwnedEventId, Event, Gap>,
59
60 threads: HashMap<(OwnedRoomId, OwnedEventId), ThreadInfo>,
62}
63
64impl Default for MemoryStore {
65 fn default() -> Self {
66 Self {
67 inner: Arc::new(StdRwLock::new(MemoryStoreInner {
68 leases: Default::default(),
69 events: RelationalLinkedChunk::new(),
70 threads: HashMap::new(),
71 })),
72 }
73 }
74}
75
76impl MemoryStore {
77 pub fn new() -> Self {
79 Self::default()
80 }
81}
82
83#[cfg_attr(target_family = "wasm", async_trait(?Send))]
84#[cfg_attr(not(target_family = "wasm"), async_trait)]
85impl EventCacheStore for MemoryStore {
86 type Error = EventCacheStoreError;
87
88 async fn close(&self) -> Result<(), Self::Error> {
89 Ok(())
90 }
91
92 async fn reopen(&self) -> Result<(), Self::Error> {
93 Ok(())
94 }
95
96 async fn try_take_leased_lock(
97 &self,
98 lease_duration_ms: u32,
99 key: &str,
100 holder: &str,
101 ) -> Result<Option<CrossProcessLockGeneration>, Self::Error> {
102 let mut inner = self.inner.write().unwrap();
103
104 Ok(try_take_leased_lock(&mut inner.leases, lease_duration_ms, key, holder))
105 }
106
107 async fn handle_linked_chunk_updates(
108 &self,
109 linked_chunk_id: LinkedChunkId<'_>,
110 updates: Vec<Update<Event, Gap>>,
111 ) -> Result<(), Self::Error> {
112 let mut inner = self.inner.write().unwrap();
113
114 let is_complete_event = |event: &Event| {
115 let Some(event_id) = event.event_id() else {
116 error!("Found event with no ID");
117 return false;
118 };
119 if event.kind.event_type().is_none() {
120 error!(%event_id, "Found an event with no event type");
121 return false;
122 }
123 true
124 };
125
126 let updates = updates
127 .into_iter()
128 .filter_map(|update| match update {
129 Update::PushItems { at, items } => Some(Update::PushItems {
130 at,
131 items: items.into_iter().filter(is_complete_event).collect(),
132 }),
133 Update::ReplaceItem { at, item } => {
134 if is_complete_event(&item) {
135 Some(Update::ReplaceItem { at, item })
136 } else {
137 None
138 }
139 }
140 update => Some(update),
141 })
142 .collect();
143
144 inner
145 .events
146 .apply_updates(linked_chunk_id, updates)
147 .map_err(|e| Self::Error::Backend(Arc::new(e)))?;
148
149 Ok(())
150 }
151
152 async fn load_all_chunks(
153 &self,
154 linked_chunk_id: LinkedChunkId<'_>,
155 ) -> Result<Vec<RawChunk<Event, Gap>>, Self::Error> {
156 let inner = self.inner.read().unwrap();
157 inner
158 .events
159 .load_all_chunks(linked_chunk_id)
160 .map_err(|err| EventCacheStoreError::InvalidData { details: err })
161 }
162
163 async fn load_all_chunks_metadata(
164 &self,
165 linked_chunk_id: LinkedChunkId<'_>,
166 ) -> Result<Vec<ChunkMetadata>, Self::Error> {
167 let inner = self.inner.read().unwrap();
168 inner
169 .events
170 .load_all_chunks_metadata(linked_chunk_id)
171 .map_err(|err| EventCacheStoreError::InvalidData { details: err })
172 }
173
174 async fn load_last_chunk(
175 &self,
176 linked_chunk_id: LinkedChunkId<'_>,
177 ) -> Result<(Option<RawChunk<Event, Gap>>, ChunkIdentifierGenerator), Self::Error> {
178 let inner = self.inner.read().unwrap();
179 inner
180 .events
181 .load_last_chunk(linked_chunk_id)
182 .map_err(|err| EventCacheStoreError::InvalidData { details: err })
183 }
184
185 async fn load_previous_chunk(
186 &self,
187 linked_chunk_id: LinkedChunkId<'_>,
188 before_chunk_identifier: ChunkIdentifier,
189 ) -> Result<Option<RawChunk<Event, Gap>>, Self::Error> {
190 let inner = self.inner.read().unwrap();
191 inner
192 .events
193 .load_previous_chunk(linked_chunk_id, before_chunk_identifier)
194 .map_err(|err| EventCacheStoreError::InvalidData { details: err })
195 }
196
197 async fn load_thread_info(
198 &self,
199 room_id: &RoomId,
200 thread_id: &EventId,
201 insert_default_if_missing: bool,
202 ) -> Result<Option<ThreadInfo>, Self::Error> {
203 let mut inner = self.inner.write().unwrap();
204 let threads = &mut inner.threads;
205
206 let key = (room_id.to_owned(), thread_id.to_owned());
207 let thread_info_entry = threads.entry(key);
208
209 Ok(match thread_info_entry {
210 Entry::Occupied(entry) => Some(entry.get().clone()),
211
212 Entry::Vacant(entry) if insert_default_if_missing => {
213 Some(entry.insert_entry(ThreadInfo::default()).get().clone())
214 }
215
216 Entry::Vacant(_) => None,
217 })
218 }
219
220 async fn update_thread_info(
221 &self,
222 room_id: &RoomId,
223 thread_id: &EventId,
224 thread_info: &ThreadInfo,
225 ) -> Result<(), Self::Error> {
226 let mut inner = self.inner.write().unwrap();
227 let threads = &mut inner.threads;
228
229 let key = (room_id.to_owned(), thread_id.to_owned());
230
231 *threads.get_mut(&key).expect("The thread entry must exist") = thread_info.clone();
232
233 Ok(())
234 }
235
236 async fn clear_all_events(&self, room_id: Option<&RoomId>) -> Result<(), Self::Error> {
237 match room_id {
238 Some(room_id) => {
239 self.inner.write().unwrap().events.clear_room(room_id);
240 }
241 None => {
242 self.inner.write().unwrap().events.clear();
243 }
244 }
245
246 Ok(())
247 }
248
249 async fn filter_duplicated_events(
250 &self,
251 linked_chunk_id: LinkedChunkId<'_>,
252 mut events: Vec<OwnedEventId>,
253 ) -> Result<Vec<(OwnedEventId, Position)>, Self::Error> {
254 if events.is_empty() {
255 return Ok(Vec::new());
256 }
257
258 let inner = self.inner.read().unwrap();
259
260 let mut duplicated_events = Vec::new();
261
262 for (event, position) in
263 inner.events.unordered_linked_chunk_items(&linked_chunk_id.to_owned())
264 {
265 if let Some(known_event_id) = event.event_id() {
266 if let Some(index) =
268 events.iter().position(|new_event_id| known_event_id == new_event_id)
269 {
270 duplicated_events.push((events.remove(index), position));
271 }
272 }
273 }
274
275 Ok(duplicated_events)
276 }
277
278 async fn find_event(
279 &self,
280 room_id: &RoomId,
281 event_id: &EventId,
282 ) -> Result<Option<Event>, Self::Error> {
283 let inner = self.inner.read().unwrap();
284
285 let event = inner.events.items(room_id).find_map(|(_, (event, _pos))| {
286 (event.event_id()? == event_id).then_some(event.clone())
287 });
288
289 Ok(event)
290 }
291
292 async fn find_event_relations(
293 &self,
294 room_id: &RoomId,
295 event_id: &EventId,
296 filters: Option<&[RelationType]>,
297 ) -> Result<Vec<(Event, Option<Position>)>, Self::Error> {
298 let inner = self.inner.read().unwrap();
299
300 let related_events: Vec<_> = inner
301 .events
302 .items(room_id)
303 .filter_map(|(linked_chunk_id, (event, pos))| {
304 let (related_to, rel_type) = extract_event_relation(event.raw())?;
306 let rel_type = RelationType::from(rel_type.as_str());
307
308 if related_to != event_id {
310 return None;
311 }
312
313 if let Some(filters) = &filters {
315 filters.contains(&rel_type).then_some((linked_chunk_id, (event.clone(), pos)))
316 } else {
317 Some((linked_chunk_id, (event.clone(), pos)))
318 }
319 })
320 .collect();
321
322 let mut deduplicated = HashMap::new();
326 for (linked_chunk_id, (event, position)) in related_events {
327 let event_id = event
328 .event_id()
329 .ok_or(Self::Error::InvalidData { details: String::from("missing event id") })?
330 .to_owned();
331 match linked_chunk_id.as_ref() {
332 LinkedChunkId::Room(_) => {
333 deduplicated.insert(event_id, (event, position));
335 }
336 _ => {
337 deduplicated.entry(event_id).or_insert_with(|| (event, None));
340 }
341 }
342 }
343
344 Ok(deduplicated.into_values().collect())
345 }
346
347 async fn get_room_events(
348 &self,
349 room_id: &RoomId,
350 event_type: Option<&str>,
351 session_id: Option<&str>,
352 ) -> Result<Vec<Event>, Self::Error> {
353 let inner = self.inner.read().unwrap();
354
355 let (_, event): (_, Vec<_>) = inner
356 .events
357 .items(room_id)
358 .map(|(_, (event, _pos))| event.clone())
359 .filter(|e| {
360 event_type
361 .is_none_or(|event_type| Some(event_type) == e.kind.event_type().as_deref())
362 })
363 .filter(|e| session_id.is_none_or(|s| Some(s) == e.kind.session_id()))
364 .map(|e| {
365 let id = e
366 .event_id()
367 .ok_or(Self::Error::InvalidData { details: String::from("missing event id") })?
368 .to_owned();
369
370 Ok((id, e))
371 })
372 .collect::<Result<Vec<_>>>()?
373 .into_iter()
374 .fold((HashSet::new(), Vec::new()), |(mut ids, mut es), (id, e)| {
375 if !ids.contains(&id) {
376 ids.insert(id);
377 es.push(e);
378 }
379 (ids, es)
380 });
381 Ok(event)
382 }
383
384 async fn save_event(&self, room_id: &RoomId, event: Event) -> Result<(), Self::Error> {
385 if event.event_id().is_none() {
386 error!(%room_id, "Trying to save an event with no ID");
387 return Ok(());
388 }
389 self.inner.write().unwrap().events.save_item(room_id.to_owned(), event);
390 Ok(())
391 }
392
393 async fn optimize(&self) -> Result<(), Self::Error> {
394 Ok(())
395 }
396
397 async fn get_size(&self) -> Result<Option<usize>, Self::Error> {
398 Ok(None)
399 }
400}
401
402#[cfg(test)]
403#[allow(unused_imports)] mod tests {
405 use super::{MemoryStore, Result};
406 use crate::{event_cache_store_integration_tests, event_cache_store_integration_tests_time};
407
408 async fn get_event_cache_store() -> Result<MemoryStore> {
409 Ok(MemoryStore::new())
410 }
411
412 event_cache_store_integration_tests!();
413 #[cfg(not(target_family = "wasm"))]
414 event_cache_store_integration_tests_time!();
415}