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::{EventCacheStore, EventCacheStoreError, Result, extract_event_relation};
35use crate::event_cache::{Event, Gap};
36
37#[derive(Debug, Clone)]
47pub struct MemoryStore {
48 inner: Arc<StdRwLock<MemoryStoreInner>>,
49}
50
51#[derive(Debug)]
52struct MemoryStoreInner {
53 leases: HashMap<String, Lease>,
55
56 events: RelationalLinkedChunk<OwnedEventId, Event, Gap>,
58
59 threads: Vec<(OwnedRoomId, OwnedEventId)>,
61}
62
63impl Default for MemoryStore {
64 fn default() -> Self {
65 Self {
66 inner: Arc::new(StdRwLock::new(MemoryStoreInner {
67 leases: Default::default(),
68 events: RelationalLinkedChunk::new(),
69 threads: Vec::new(),
70 })),
71 }
72 }
73}
74
75impl MemoryStore {
76 pub fn new() -> Self {
78 Self::default()
79 }
80}
81
82#[cfg_attr(target_family = "wasm", async_trait(?Send))]
83#[cfg_attr(not(target_family = "wasm"), async_trait)]
84impl EventCacheStore for MemoryStore {
85 type Error = EventCacheStoreError;
86
87 async fn close(&self) -> Result<(), Self::Error> {
88 Ok(())
89 }
90
91 async fn reopen(&self) -> Result<(), Self::Error> {
92 Ok(())
93 }
94
95 async fn try_take_leased_lock(
96 &self,
97 lease_duration_ms: u32,
98 key: &str,
99 holder: &str,
100 ) -> Result<Option<CrossProcessLockGeneration>, Self::Error> {
101 let mut inner = self.inner.write().unwrap();
102
103 Ok(try_take_leased_lock(&mut inner.leases, lease_duration_ms, key, holder))
104 }
105
106 async fn handle_linked_chunk_updates(
107 &self,
108 linked_chunk_id: LinkedChunkId<'_>,
109 updates: Vec<Update<Event, Gap>>,
110 ) -> Result<(), Self::Error> {
111 let mut inner = self.inner.write().unwrap();
112 inner
113 .events
114 .apply_updates(linked_chunk_id, updates)
115 .map_err(|e| Self::Error::Backend(Arc::new(e)))?;
116
117 Ok(())
118 }
119
120 async fn load_all_chunks(
121 &self,
122 linked_chunk_id: LinkedChunkId<'_>,
123 ) -> Result<Vec<RawChunk<Event, Gap>>, Self::Error> {
124 let inner = self.inner.read().unwrap();
125 inner
126 .events
127 .load_all_chunks(linked_chunk_id)
128 .map_err(|err| EventCacheStoreError::InvalidData { details: err })
129 }
130
131 async fn load_all_chunks_metadata(
132 &self,
133 linked_chunk_id: LinkedChunkId<'_>,
134 ) -> Result<Vec<ChunkMetadata>, Self::Error> {
135 let inner = self.inner.read().unwrap();
136 inner
137 .events
138 .load_all_chunks_metadata(linked_chunk_id)
139 .map_err(|err| EventCacheStoreError::InvalidData { details: err })
140 }
141
142 async fn load_last_chunk(
143 &self,
144 linked_chunk_id: LinkedChunkId<'_>,
145 ) -> Result<(Option<RawChunk<Event, Gap>>, ChunkIdentifierGenerator), Self::Error> {
146 let inner = self.inner.read().unwrap();
147 inner
148 .events
149 .load_last_chunk(linked_chunk_id)
150 .map_err(|err| EventCacheStoreError::InvalidData { details: err })
151 }
152
153 async fn load_previous_chunk(
154 &self,
155 linked_chunk_id: LinkedChunkId<'_>,
156 before_chunk_identifier: ChunkIdentifier,
157 ) -> Result<Option<RawChunk<Event, Gap>>, Self::Error> {
158 let inner = self.inner.read().unwrap();
159 inner
160 .events
161 .load_previous_chunk(linked_chunk_id, before_chunk_identifier)
162 .map_err(|err| EventCacheStoreError::InvalidData { details: err })
163 }
164
165 async fn remember_thread(
166 &self,
167 room_id: &RoomId,
168 thread_id: &EventId,
169 ) -> Result<(), Self::Error> {
170 let mut inner = self.inner.write().unwrap();
171 let threads = &mut inner.threads;
172
173 let pair = (room_id.to_owned(), thread_id.to_owned());
174
175 if !threads.contains(&pair) {
176 threads.push(pair);
177 }
178
179 Ok(())
180 }
181
182 async fn clear_all_events(&self, room_id: Option<&RoomId>) -> Result<(), Self::Error> {
183 match room_id {
184 Some(room_id) => {
185 self.inner.write().unwrap().events.clear_room(room_id);
186 }
187 None => {
188 self.inner.write().unwrap().events.clear();
189 }
190 }
191
192 Ok(())
193 }
194
195 async fn filter_duplicated_events(
196 &self,
197 linked_chunk_id: LinkedChunkId<'_>,
198 mut events: Vec<OwnedEventId>,
199 ) -> Result<Vec<(OwnedEventId, Position)>, Self::Error> {
200 if events.is_empty() {
201 return Ok(Vec::new());
202 }
203
204 let inner = self.inner.read().unwrap();
205
206 let mut duplicated_events = Vec::new();
207
208 for (event, position) in
209 inner.events.unordered_linked_chunk_items(&linked_chunk_id.to_owned())
210 {
211 if let Some(known_event_id) = event.event_id() {
212 if let Some(index) =
214 events.iter().position(|new_event_id| known_event_id == new_event_id)
215 {
216 duplicated_events.push((events.remove(index), position));
217 }
218 }
219 }
220
221 Ok(duplicated_events)
222 }
223
224 async fn find_event(
225 &self,
226 room_id: &RoomId,
227 event_id: &EventId,
228 ) -> Result<Option<Event>, Self::Error> {
229 let inner = self.inner.read().unwrap();
230
231 let event = inner.events.items(room_id).find_map(|(_, (event, _pos))| {
232 (event.event_id()? == event_id).then_some(event.clone())
233 });
234
235 Ok(event)
236 }
237
238 async fn find_event_relations(
239 &self,
240 room_id: &RoomId,
241 event_id: &EventId,
242 filters: Option<&[RelationType]>,
243 ) -> Result<Vec<(Event, Option<Position>)>, Self::Error> {
244 let inner = self.inner.read().unwrap();
245
246 let related_events: Vec<_> = inner
247 .events
248 .items(room_id)
249 .filter_map(|(linked_chunk_id, (event, pos))| {
250 let (related_to, rel_type) = extract_event_relation(event.raw())?;
252 let rel_type = RelationType::from(rel_type.as_str());
253
254 if related_to != event_id {
256 return None;
257 }
258
259 if let Some(filters) = &filters {
261 filters.contains(&rel_type).then_some((linked_chunk_id, (event.clone(), pos)))
262 } else {
263 Some((linked_chunk_id, (event.clone(), pos)))
264 }
265 })
266 .collect();
267
268 let mut deduplicated = HashMap::new();
272 for (linked_chunk_id, (event, position)) in related_events {
273 let event_id = event
274 .event_id()
275 .ok_or(Self::Error::InvalidData { details: String::from("missing event id") })?
276 .to_owned();
277 match linked_chunk_id.as_ref() {
278 LinkedChunkId::Room(_) => {
279 deduplicated.insert(event_id, (event, position));
281 }
282 _ => {
283 deduplicated.entry(event_id).or_insert_with(|| (event, None));
286 }
287 }
288 }
289
290 Ok(deduplicated.into_values().collect())
291 }
292
293 async fn get_room_events(
294 &self,
295 room_id: &RoomId,
296 event_type: Option<&str>,
297 session_id: Option<&str>,
298 ) -> Result<Vec<Event>, Self::Error> {
299 let inner = self.inner.read().unwrap();
300
301 let (_, event): (_, Vec<_>) = inner
302 .events
303 .items(room_id)
304 .map(|(_, (event, _pos))| event.clone())
305 .filter(|e| {
306 event_type
307 .is_none_or(|event_type| Some(event_type) == e.kind.event_type().as_deref())
308 })
309 .filter(|e| session_id.is_none_or(|s| Some(s) == e.kind.session_id()))
310 .map(|e| {
311 let id = e
312 .event_id()
313 .ok_or(Self::Error::InvalidData { details: String::from("missing event id") })?
314 .to_owned();
315
316 Ok((id, e))
317 })
318 .collect::<Result<Vec<_>>>()?
319 .into_iter()
320 .fold((HashSet::new(), Vec::new()), |(mut ids, mut es), (id, e)| {
321 if !ids.contains(&id) {
322 ids.insert(id);
323 es.push(e);
324 }
325 (ids, es)
326 });
327 Ok(event)
328 }
329
330 async fn save_event(&self, room_id: &RoomId, event: Event) -> Result<(), Self::Error> {
331 if event.event_id().is_none() {
332 error!(%room_id, "Trying to save an event with no ID");
333 return Ok(());
334 }
335 self.inner.write().unwrap().events.save_item(room_id.to_owned(), event);
336 Ok(())
337 }
338
339 async fn optimize(&self) -> Result<(), Self::Error> {
340 Ok(())
341 }
342
343 async fn get_size(&self) -> Result<Option<usize>, Self::Error> {
344 Ok(None)
345 }
346}
347
348#[cfg(test)]
349#[allow(unused_imports)] mod tests {
351 use super::{MemoryStore, Result};
352 use crate::{event_cache_store_integration_tests, event_cache_store_integration_tests_time};
353
354 async fn get_event_cache_store() -> Result<MemoryStore> {
355 Ok(MemoryStore::new())
356 }
357
358 event_cache_store_integration_tests!();
359 #[cfg(not(target_family = "wasm"))]
360 event_cache_store_integration_tests_time!();
361}