Skip to main content

yugendb_memory/
lib.rs

1//! In-memory Rust driver for yugendb.
2//!
3//! The memory driver stores serialised typed values in-process. It is useful
4//! for temporary local storage and application-managed caching. It is not a
5//! durable storage backend.
6
7use std::{collections::BTreeMap, sync::Arc};
8
9use async_trait::async_trait;
10use parking_lot::RwLock;
11use yugendb_core::{
12    Batch, BatchOperation, BatchResult, Capabilities, CollectionName, DeleteOptions, Driver, Key,
13    Namespace, ReadOptions, Result, ScanOptions, StoredValue, WriteOptions, YugenDbError,
14};
15
16/// Convenience constructor for a memory driver.
17///
18/// The returned value can be passed directly to `Store::builder().driver(...)`.
19#[must_use]
20pub fn memory() -> MemoryDriver {
21    MemoryDriver::new()
22}
23
24/// In-process memory driver.
25#[derive(Debug, Clone, Default)]
26pub struct MemoryDriver {
27    entries: Arc<RwLock<BTreeMap<StorageKey, StoredValue>>>,
28}
29
30impl MemoryDriver {
31    /// Creates an empty memory driver.
32    #[must_use]
33    pub fn new() -> Self {
34        Self::default()
35    }
36
37    /// Returns the number of records currently retained by the driver.
38    ///
39    /// Expired records may still be retained until overwritten, deleted, or the
40    /// driver is dropped.
41    #[must_use]
42    pub fn len(&self) -> usize {
43        self.entries.read().len()
44    }
45
46    /// Returns true when no records are currently retained by the driver.
47    #[must_use]
48    pub fn is_empty(&self) -> bool {
49        self.entries.read().is_empty()
50    }
51
52    /// Removes all retained records.
53    pub fn clear(&self) {
54        self.entries.write().clear();
55    }
56}
57
58#[async_trait]
59impl Driver for MemoryDriver {
60    fn name(&self) -> &'static str {
61        "memory"
62    }
63
64    fn capabilities(&self) -> Capabilities {
65        Capabilities::memory()
66    }
67
68    async fn initialise(&self) -> Result<()> {
69        Ok(())
70    }
71
72    async fn finalise(&self) -> Result<()> {
73        Ok(())
74    }
75
76    async fn get(
77        &self,
78        namespace: &Namespace,
79        collection: &CollectionName,
80        key: &Key,
81        options: &ReadOptions,
82    ) -> Result<Option<StoredValue>> {
83        let storage_key = StorageKey::new(namespace, collection, key);
84        let entries = self.entries.read();
85        let Some(value) = entries.get(&storage_key) else {
86            return Ok(None);
87        };
88
89        if value.is_expired() && !options.allow_expired {
90            return Ok(None);
91        }
92
93        Ok(Some(value.clone()))
94    }
95
96    async fn set(
97        &self,
98        namespace: &Namespace,
99        collection: &CollectionName,
100        key: &Key,
101        value: StoredValue,
102        options: &WriteOptions,
103    ) -> Result<()> {
104        let storage_key = StorageKey::new(namespace, collection, key);
105        let mut entries = self.entries.write();
106
107        if !options.overwrite
108            && entries
109                .get(&storage_key)
110                .is_some_and(|existing| !existing.is_expired())
111        {
112            return Err(YugenDbError::conflict(
113                "memory driver refused to overwrite an existing value",
114            ));
115        }
116
117        entries.insert(storage_key, value);
118        Ok(())
119    }
120
121    async fn delete(
122        &self,
123        namespace: &Namespace,
124        collection: &CollectionName,
125        key: &Key,
126        options: &DeleteOptions,
127    ) -> Result<bool> {
128        let storage_key = StorageKey::new(namespace, collection, key);
129        let removed = self.entries.write().remove(&storage_key).is_some();
130
131        if options.must_exist && !removed {
132            return Err(YugenDbError::not_found(
133                "memory driver could not delete a missing value",
134            ));
135        }
136
137        Ok(removed)
138    }
139
140    async fn exists(
141        &self,
142        namespace: &Namespace,
143        collection: &CollectionName,
144        key: &Key,
145    ) -> Result<bool> {
146        let storage_key = StorageKey::new(namespace, collection, key);
147        let entries = self.entries.read();
148        Ok(entries
149            .get(&storage_key)
150            .is_some_and(|value| !value.is_expired()))
151    }
152
153    async fn scan_prefix(
154        &self,
155        namespace: &Namespace,
156        collection: &CollectionName,
157        options: &ScanOptions,
158    ) -> Result<Vec<(Key, StoredValue)>> {
159        let prefix = options.prefix.as_deref();
160        let entries = self.entries.read();
161        let mut results = Vec::new();
162
163        for (storage_key, value) in entries.iter() {
164            if storage_key.namespace != namespace.as_str() {
165                continue;
166            }
167
168            if storage_key.collection != collection.as_str() {
169                continue;
170            }
171
172            if prefix.is_some_and(|prefix| !storage_key.key.starts_with(prefix)) {
173                continue;
174            }
175
176            if value.is_expired() && !options.include_expired {
177                continue;
178            }
179
180            let key = Key::try_from(storage_key.key.as_str())?;
181            results.push((key, value.clone()));
182        }
183
184        results.sort_by(|(left, _), (right, _)| left.cmp(right));
185
186        if let Some(limit) = options.limit {
187            results.truncate(limit);
188        }
189
190        Ok(results)
191    }
192
193    async fn batch(&self, batch: Batch) -> Result<BatchResult> {
194        let mut entries = self.entries.write();
195        let mut set_count = 0;
196        let mut delete_count = 0;
197
198        for operation in batch.into_operations() {
199            match operation {
200                BatchOperation::Set {
201                    namespace,
202                    collection,
203                    key,
204                    value,
205                } => {
206                    entries.insert(StorageKey::new(&namespace, &collection, &key), value);
207                    set_count += 1;
208                }
209                BatchOperation::Delete {
210                    namespace,
211                    collection,
212                    key,
213                } => {
214                    if entries
215                        .remove(&StorageKey::new(&namespace, &collection, &key))
216                        .is_some()
217                    {
218                        delete_count += 1;
219                    }
220                }
221            }
222        }
223
224        Ok(BatchResult::new(set_count, delete_count))
225    }
226}
227
228#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
229struct StorageKey {
230    namespace: String,
231    collection: String,
232    key: String,
233}
234
235impl StorageKey {
236    fn new(namespace: &Namespace, collection: &CollectionName, key: &Key) -> Self {
237        Self {
238            namespace: namespace.as_str().to_owned(),
239            collection: collection.as_str().to_owned(),
240            key: key.as_str().to_owned(),
241        }
242    }
243}