matrix_sdk_base/event_cache/store/
memory_store.rs1use std::{
16 collections::{HashMap, HashSet},
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 ) -> Result<ThreadInfo, Self::Error> {
202 let mut inner = self.inner.write().unwrap();
203 let threads = &mut inner.threads;
204
205 let key = (room_id.to_owned(), thread_id.to_owned());
206
207 let thread_info = threads.entry(key).or_default();
208
209 Ok(thread_info.clone())
210 }
211
212 async fn update_thread_info(
213 &self,
214 room_id: &RoomId,
215 thread_id: &EventId,
216 thread_info: &ThreadInfo,
217 ) -> Result<(), Self::Error> {
218 let mut inner = self.inner.write().unwrap();
219 let threads = &mut inner.threads;
220
221 let key = (room_id.to_owned(), thread_id.to_owned());
222
223 *threads.get_mut(&key).expect("The thread entry must exist") = thread_info.clone();
224
225 Ok(())
226 }
227
228 async fn clear_all_events(&self, room_id: Option<&RoomId>) -> Result<(), Self::Error> {
229 match room_id {
230 Some(room_id) => {
231 self.inner.write().unwrap().events.clear_room(room_id);
232 }
233 None => {
234 self.inner.write().unwrap().events.clear();
235 }
236 }
237
238 Ok(())
239 }
240
241 async fn filter_duplicated_events(
242 &self,
243 linked_chunk_id: LinkedChunkId<'_>,
244 mut events: Vec<OwnedEventId>,
245 ) -> Result<Vec<(OwnedEventId, Position)>, Self::Error> {
246 if events.is_empty() {
247 return Ok(Vec::new());
248 }
249
250 let inner = self.inner.read().unwrap();
251
252 let mut duplicated_events = Vec::new();
253
254 for (event, position) in
255 inner.events.unordered_linked_chunk_items(&linked_chunk_id.to_owned())
256 {
257 if let Some(known_event_id) = event.event_id() {
258 if let Some(index) =
260 events.iter().position(|new_event_id| known_event_id == new_event_id)
261 {
262 duplicated_events.push((events.remove(index), position));
263 }
264 }
265 }
266
267 Ok(duplicated_events)
268 }
269
270 async fn find_event(
271 &self,
272 room_id: &RoomId,
273 event_id: &EventId,
274 ) -> Result<Option<Event>, Self::Error> {
275 let inner = self.inner.read().unwrap();
276
277 let event = inner.events.items(room_id).find_map(|(_, (event, _pos))| {
278 (event.event_id()? == event_id).then_some(event.clone())
279 });
280
281 Ok(event)
282 }
283
284 async fn find_event_relations(
285 &self,
286 room_id: &RoomId,
287 event_id: &EventId,
288 filters: Option<&[RelationType]>,
289 ) -> Result<Vec<(Event, Option<Position>)>, Self::Error> {
290 let inner = self.inner.read().unwrap();
291
292 let related_events: Vec<_> = inner
293 .events
294 .items(room_id)
295 .filter_map(|(linked_chunk_id, (event, pos))| {
296 let (related_to, rel_type) = extract_event_relation(event.raw())?;
298 let rel_type = RelationType::from(rel_type.as_str());
299
300 if related_to != event_id {
302 return None;
303 }
304
305 if let Some(filters) = &filters {
307 filters.contains(&rel_type).then_some((linked_chunk_id, (event.clone(), pos)))
308 } else {
309 Some((linked_chunk_id, (event.clone(), pos)))
310 }
311 })
312 .collect();
313
314 let mut deduplicated = HashMap::new();
318 for (linked_chunk_id, (event, position)) in related_events {
319 let event_id = event
320 .event_id()
321 .ok_or(Self::Error::InvalidData { details: String::from("missing event id") })?
322 .to_owned();
323 match linked_chunk_id.as_ref() {
324 LinkedChunkId::Room(_) => {
325 deduplicated.insert(event_id, (event, position));
327 }
328 _ => {
329 deduplicated.entry(event_id).or_insert_with(|| (event, None));
332 }
333 }
334 }
335
336 Ok(deduplicated.into_values().collect())
337 }
338
339 async fn get_room_events(
340 &self,
341 room_id: &RoomId,
342 event_type: Option<&str>,
343 session_id: Option<&str>,
344 ) -> Result<Vec<Event>, Self::Error> {
345 let inner = self.inner.read().unwrap();
346
347 let (_, event): (_, Vec<_>) = inner
348 .events
349 .items(room_id)
350 .map(|(_, (event, _pos))| event.clone())
351 .filter(|e| {
352 event_type
353 .is_none_or(|event_type| Some(event_type) == e.kind.event_type().as_deref())
354 })
355 .filter(|e| session_id.is_none_or(|s| Some(s) == e.kind.session_id()))
356 .map(|e| {
357 let id = e
358 .event_id()
359 .ok_or(Self::Error::InvalidData { details: String::from("missing event id") })?
360 .to_owned();
361
362 Ok((id, e))
363 })
364 .collect::<Result<Vec<_>>>()?
365 .into_iter()
366 .fold((HashSet::new(), Vec::new()), |(mut ids, mut es), (id, e)| {
367 if !ids.contains(&id) {
368 ids.insert(id);
369 es.push(e);
370 }
371 (ids, es)
372 });
373 Ok(event)
374 }
375
376 async fn save_event(&self, room_id: &RoomId, event: Event) -> Result<(), Self::Error> {
377 if event.event_id().is_none() {
378 error!(%room_id, "Trying to save an event with no ID");
379 return Ok(());
380 }
381 self.inner.write().unwrap().events.save_item(room_id.to_owned(), event);
382 Ok(())
383 }
384
385 async fn optimize(&self) -> Result<(), Self::Error> {
386 Ok(())
387 }
388
389 async fn get_size(&self) -> Result<Option<usize>, Self::Error> {
390 Ok(None)
391 }
392}
393
394#[cfg(test)]
395#[allow(unused_imports)] mod tests {
397 use super::{MemoryStore, Result};
398 use crate::{event_cache_store_integration_tests, event_cache_store_integration_tests_time};
399
400 async fn get_event_cache_store() -> Result<MemoryStore> {
401 Ok(MemoryStore::new())
402 }
403
404 event_cache_store_integration_tests!();
405 #[cfg(not(target_family = "wasm"))]
406 event_cache_store_integration_tests_time!();
407}