Skip to main content

fjall/tx/single_writer/
keyspace.rs

1// Copyright (c) 2024-present, fjall-rs
2// This source code is licensed under both the Apache 2.0 and MIT License
3// (found in the LICENSE-* files in the repository)
4
5use crate::{Guard, Keyspace, Readable, SingleWriterTxDatabase};
6use lsm_tree::{UserKey, UserValue};
7use std::path::PathBuf;
8
9/// Handle to a keyspace of a transactional database
10#[derive(Clone)]
11pub struct SingleWriterTxKeyspace {
12    pub(crate) inner: Keyspace,
13    pub(crate) db: SingleWriterTxDatabase,
14}
15
16impl AsRef<Keyspace> for SingleWriterTxKeyspace {
17    fn as_ref(&self) -> &Keyspace {
18        self.inner()
19    }
20}
21
22impl SingleWriterTxKeyspace {
23    /// Returns the underlying LSM-tree's path.
24    #[must_use]
25    pub fn path(&self) -> PathBuf {
26        self.inner.path().into()
27    }
28
29    /// Approximates the amount of items in the keyspace.
30    ///
31    /// For update- or delete-heavy workloads, this value will
32    /// diverge from the real value, but is a O(1) operation.
33    ///
34    /// For insert-only workloads (e.g. logs, time series)
35    /// this value is reliable.
36    ///
37    /// # Examples
38    ///
39    /// ```
40    /// # use fjall::{SingleWriterTxDatabase, KeyspaceCreateOptions, Readable};
41    /// #
42    /// # let folder = tempfile::tempdir()?;
43    /// # let db = SingleWriterTxDatabase::builder(folder).open()?;
44    /// # let tree = db.keyspace("default", KeyspaceCreateOptions::default)?;
45    /// assert_eq!(tree.approximate_len(), 0);
46    ///
47    /// tree.insert("1", "abc")?;
48    /// assert_eq!(tree.approximate_len(), 1);
49    ///
50    /// tree.remove("1")?;
51    /// // Oops! approximate_len will not be reliable here
52    /// assert_eq!(tree.approximate_len(), 2);
53    /// #
54    /// # Ok::<(), fjall::Error>(())
55    /// ```
56    #[must_use]
57    pub fn approximate_len(&self) -> usize {
58        self.inner.approximate_len()
59    }
60
61    /// Removes an item and returns its value if it existed.
62    ///
63    /// The operation will run wrapped in a transaction.
64    ///
65    /// ```
66    /// # use fjall::{SingleWriterTxDatabase, KeyspaceCreateOptions, Readable};
67    /// # use std::sync::Arc;
68    /// #
69    /// # let folder = tempfile::tempdir()?;
70    /// # let db = SingleWriterTxDatabase::builder(folder).open()?;
71    /// # let tree = db.keyspace("default", KeyspaceCreateOptions::default)?;
72    /// tree.insert("a", "abc")?;
73    ///
74    /// let taken = tree.take("a")?.unwrap();
75    /// assert_eq!(b"abc", &*taken);
76    ///
77    /// let item = tree.get("a")?;
78    /// assert!(item.is_none());
79    /// #
80    /// # Ok::<(), fjall::Error>(())
81    /// ```
82    ///
83    /// # Errors
84    ///
85    /// Will return `Err` if an IO error occurs.
86    pub fn take<K: Into<UserKey>>(&self, key: K) -> crate::Result<Option<UserValue>> {
87        self.fetch_update(key, |_| None)
88    }
89
90    /// Atomically updates an item and returns the previous value.
91    ///
92    /// Returning `None` removes the item if it existed before.
93    ///
94    /// The operation will run wrapped in a transaction.
95    ///
96    /// # Examples
97    ///
98    /// ```
99    /// # use fjall::{SingleWriterTxDatabase, Slice, KeyspaceCreateOptions, Readable};
100    /// # use std::sync::Arc;
101    /// #
102    /// # let folder = tempfile::tempdir()?;
103    /// # let db = SingleWriterTxDatabase::builder(folder).open()?;
104    /// # let tree = db.keyspace("default", KeyspaceCreateOptions::default)?;
105    /// tree.insert("a", "abc")?;
106    ///
107    /// let prev = tree.fetch_update("a", |_| Some(Slice::from(*b"def")))?.unwrap();
108    /// assert_eq!(b"abc", &*prev);
109    ///
110    /// let item = tree.get("a")?;
111    /// assert_eq!(Some("def".as_bytes().into()), item);
112    /// #
113    /// # Ok::<(), fjall::Error>(())
114    /// ```
115    ///
116    /// ```
117    /// # use fjall::{SingleWriterTxDatabase, KeyspaceCreateOptions};
118    /// # use std::sync::Arc;
119    /// #
120    /// # let folder = tempfile::tempdir()?;
121    /// # let db = SingleWriterTxDatabase::builder(folder).open()?;
122    /// # let tree = db.keyspace("default", KeyspaceCreateOptions::default)?;
123    /// tree.insert("a", "abc")?;
124    ///
125    /// let prev = tree.fetch_update("a", |_| None)?.unwrap();
126    /// assert_eq!(b"abc", &*prev);
127    ///
128    /// let item = tree.get("a")?;
129    /// assert!(item.is_none());
130    /// #
131    /// # Ok::<(), fjall::Error>(())
132    /// ```
133    ///
134    /// # Errors
135    ///
136    /// Will return `Err` if an IO error occurs.
137    pub fn fetch_update<K: Into<UserKey>, F: FnOnce(Option<&UserValue>) -> Option<UserValue>>(
138        &self,
139        key: K,
140        f: F,
141    ) -> crate::Result<Option<UserValue>> {
142        let key: UserKey = key.into();
143
144        let mut tx = self.db.write_tx();
145
146        let prev = tx.fetch_update(self, key, f)?;
147        tx.commit()?;
148
149        Ok(prev)
150    }
151
152    /// Atomically updates an item and returns the new value.
153    ///
154    /// Returning `None` removes the item if it existed before.
155    ///
156    /// The operation will run wrapped in a transaction.
157    ///
158    /// # Examples
159    ///
160    /// ```
161    /// # use fjall::{SingleWriterTxDatabase, Slice, KeyspaceCreateOptions, Readable};
162    /// # use std::sync::Arc;
163    /// #
164    /// # let folder = tempfile::tempdir()?;
165    /// # let db = SingleWriterTxDatabase::builder(folder).open()?;
166    /// # let tree = db.keyspace("default", KeyspaceCreateOptions::default)?;
167    /// tree.insert("a", "abc")?;
168    ///
169    /// let updated = tree.update_fetch("a", |_| Some(Slice::from(*b"def")))?.unwrap();
170    /// assert_eq!(b"def", &*updated);
171    ///
172    /// let item = tree.get("a")?;
173    /// assert_eq!(Some("def".as_bytes().into()), item);
174    /// #
175    /// # Ok::<(), fjall::Error>(())
176    /// ```
177    ///
178    /// ```
179    /// # use fjall::{SingleWriterTxDatabase, KeyspaceCreateOptions, Readable};
180    /// # use std::sync::Arc;
181    /// #
182    /// # let folder = tempfile::tempdir()?;
183    /// # let db = SingleWriterTxDatabase::builder(folder).open()?;
184    /// # let tree = db.keyspace("default", KeyspaceCreateOptions::default)?;
185    /// tree.insert("a", "abc")?;
186    ///
187    /// let updated = tree.update_fetch("a", |_| None)?;
188    /// assert!(updated.is_none());
189    ///
190    /// let item = tree.get("a")?;
191    /// assert!(item.is_none());
192    /// #
193    /// # Ok::<(), fjall::Error>(())
194    /// ```
195    ///
196    /// # Errors
197    ///
198    /// Will return `Err` if an IO error occurs.
199    pub fn update_fetch<K: Into<UserKey>, F: FnOnce(Option<&UserValue>) -> Option<UserValue>>(
200        &self,
201        key: K,
202        f: F,
203    ) -> crate::Result<Option<UserValue>> {
204        let key = key.into();
205
206        let mut tx = self.db.write_tx();
207        let updated = tx.update_fetch(self, key, f)?;
208        tx.commit()?;
209
210        Ok(updated)
211    }
212
213    /// Inserts a key-value pair into the keyspace.
214    ///
215    /// Keys may be up to 65536 bytes long, values up to 2^32 bytes.
216    /// Shorter keys and values result in better performance.
217    ///
218    /// If the key already exists, the item will be overwritten.
219    ///
220    /// The operation will run wrapped in a transaction.
221    ///
222    /// # Examples
223    ///
224    /// ```
225    /// # use fjall::{SingleWriterTxDatabase, KeyspaceCreateOptions, Readable};
226    /// #
227    /// # let folder = tempfile::tempdir()?;
228    /// # let db = SingleWriterTxDatabase::builder(folder).open()?;
229    /// # let tree = db.keyspace("default", KeyspaceCreateOptions::default)?;
230    /// tree.insert("a", "abc")?;
231    ///
232    /// assert!(!db.read_tx().is_empty(&tree)?);
233    /// #
234    /// # Ok::<(), fjall::Error>(())
235    /// ```
236    ///
237    /// # Errors
238    ///
239    /// Will return `Err` if an IO error occurs.
240    pub fn insert<K: Into<UserKey>, V: Into<UserValue>>(
241        &self,
242        key: K,
243        value: V,
244    ) -> crate::Result<()> {
245        let mut tx = self.db.write_tx();
246        tx.insert(self, key, value);
247        tx.commit()?;
248        Ok(())
249    }
250
251    /// Removes an item from the keyspace.
252    ///
253    /// The key may be up to 65536 bytes long.
254    /// Shorter keys result in better performance.
255    ///
256    /// The operation will run wrapped in a transaction.
257    ///
258    /// # Examples
259    ///
260    /// ```
261    /// # use fjall::{SingleWriterTxDatabase, KeyspaceCreateOptions, Readable};
262    /// #
263    /// # let folder = tempfile::tempdir()?;
264    /// # let db = SingleWriterTxDatabase::builder(folder).open()?;
265    /// # let tree = db.keyspace("default", KeyspaceCreateOptions::default)?;
266    /// tree.insert("a", "abc")?;
267    /// assert!(!db.read_tx().is_empty(&tree)?);
268    ///
269    /// tree.remove("a")?;
270    /// assert!(db.read_tx().is_empty(&tree)?);
271    /// #
272    /// # Ok::<(), fjall::Error>(())
273    /// ```
274    ///
275    /// # Errors
276    ///
277    /// Will return `Err` if an IO error occurs.
278    pub fn remove<K: Into<UserKey>>(&self, key: K) -> crate::Result<()> {
279        let mut tx = self.db.write_tx();
280        tx.remove(self, key);
281        tx.commit()?;
282        Ok(())
283    }
284
285    /// Removes an item from the keyspace, leaving behind a weak tombstone.
286    ///
287    /// The tombstone marker of this delete operation will vanish when it
288    /// collides with its corresponding insertion.
289    /// This may cause older versions of the value to be resurrected, so it should
290    /// only be used and preferred in scenarios where a key is only ever written once.
291    ///
292    /// The key may be up to 65536 bytes long.
293    /// Shorter keys result in better performance.
294    ///
295    /// The operation will run wrapped in a transaction.
296    ///
297    /// # Experimental
298    ///
299    /// This function is currently experimental.
300    ///
301    /// # Examples
302    ///
303    /// ```
304    /// # use fjall::{SingleWriterTxDatabase, KeyspaceCreateOptions, Readable};
305    /// #
306    /// # let folder = tempfile::tempdir()?;
307    /// # let db = SingleWriterTxDatabase::builder(folder).open()?;
308    /// # let tree = db.keyspace("default", KeyspaceCreateOptions::default)?;
309    /// tree.insert("a", "abc")?;
310    /// assert!(!db.read_tx().is_empty(&tree)?);
311    ///
312    /// tree.remove_weak("a")?;
313    /// assert!(db.read_tx().is_empty(&tree)?);
314    /// #
315    /// # Ok::<(), fjall::Error>(())
316    /// ```
317    ///
318    /// # Errors
319    ///
320    /// Will return `Err` if an IO error occurs.
321    #[doc(hidden)]
322    pub fn remove_weak<K: Into<UserKey>>(&self, key: K) -> crate::Result<()> {
323        let mut tx = self.db.write_tx();
324        tx.remove_weak(self, key);
325        tx.commit()?;
326        Ok(())
327    }
328
329    /// Retrieves an item from the keyspace.
330    ///
331    /// The operation will run wrapped in a read snapshot.
332    ///
333    /// # Examples
334    ///
335    /// ```
336    /// # use fjall::{SingleWriterTxDatabase, KeyspaceCreateOptions, Readable};
337    /// #
338    /// # let folder = tempfile::tempdir()?;
339    /// # let db = SingleWriterTxDatabase::builder(folder).open()?;
340    /// # let tree = db.keyspace("default", KeyspaceCreateOptions::default)?;
341    /// tree.insert("a", "my_value")?;
342    ///
343    /// let item = tree.get("a")?;
344    /// assert_eq!(Some("my_value".as_bytes().into()), item);
345    /// #
346    /// # Ok::<(), fjall::Error>(())
347    /// ```
348    ///
349    /// # Errors
350    ///
351    /// Will return `Err` if an IO error occurs.
352    pub fn get<K: AsRef<[u8]>>(&self, key: K) -> crate::Result<Option<lsm_tree::UserValue>> {
353        self.inner.get(key)
354    }
355
356    /// Retrieves the size of an item from the keyspace.
357    ///
358    /// The operation will run wrapped in a read snapshot.
359    ///
360    /// # Examples
361    ///
362    /// ```
363    /// # use fjall::{SingleWriterTxDatabase, KeyspaceCreateOptions, Readable};
364    /// #
365    /// # let folder = tempfile::tempdir()?;
366    /// # let db = SingleWriterTxDatabase::builder(folder).open()?;
367    /// # let tree = db.keyspace("default", KeyspaceCreateOptions::default)?;
368    /// tree.insert("a", "my_value")?;
369    ///
370    /// let len = tree.size_of("a")?.unwrap_or_default();
371    /// assert_eq!("my_value".len() as u32, len);
372    /// #
373    /// # Ok::<(), fjall::Error>(())
374    /// ```
375    ///
376    /// # Errors
377    ///
378    /// Will return `Err` if an IO error occurs.
379    pub fn size_of<K: AsRef<[u8]>>(&self, key: K) -> crate::Result<Option<u32>> {
380        self.inner.size_of(key)
381    }
382
383    /// Returns the first key-value pair in the keyspace.
384    /// The key in this pair is the minimum key in the keyspace.
385    ///
386    /// The operation will run wrapped in a read snapshot.
387    ///
388    /// # Examples
389    ///
390    /// ```
391    /// # use fjall::{SingleWriterTxDatabase, KeyspaceCreateOptions, Readable};
392    /// #
393    /// # let folder = tempfile::tempdir()?;
394    /// # let db = SingleWriterTxDatabase::builder(folder).open()?;
395    /// # let tree = db.keyspace("default", KeyspaceCreateOptions::default)?;
396    /// tree.insert("a", "my_value")?;
397    /// tree.insert("b", "my_value")?;
398    ///
399    /// assert_eq!(b"a", &*tree.first_key_value().unwrap().key()?);
400    /// #
401    /// # Ok::<(), fjall::Error>(())
402    /// ```
403    #[must_use]
404    pub fn first_key_value(&self) -> Option<Guard> {
405        let read_tx = self.db.read_tx();
406        read_tx.first_key_value(self)
407    }
408
409    /// Returns the last key-value pair in the keyspace.
410    /// The key in this pair is the maximum key in the keyspace.
411    ///
412    /// The operation will run wrapped in a read snapshot.
413    ///
414    /// # Examples
415    ///
416    /// ```
417    /// # use fjall::{SingleWriterTxDatabase, KeyspaceCreateOptions, Readable};
418    /// #
419    /// # let folder = tempfile::tempdir()?;
420    /// # let db = SingleWriterTxDatabase::builder(folder).open()?;
421    /// # let tree = db.keyspace("default", KeyspaceCreateOptions::default)?;
422    /// tree.insert("a", "my_value")?;
423    /// tree.insert("b", "my_value")?;
424    ///
425    /// assert_eq!(b"b", &*tree.last_key_value().unwrap().key()?);
426    /// #
427    /// # Ok::<(), fjall::Error>(())
428    /// ```
429    #[must_use]
430    pub fn last_key_value(&self) -> Option<Guard> {
431        let read_tx = self.db.read_tx();
432        read_tx.last_key_value(self)
433    }
434
435    /// Returns `true` if the keyspace contains the specified key.
436    ///
437    /// The operation will run wrapped in a read snapshot.
438    ///
439    /// # Examples
440    ///
441    /// ```
442    /// # use fjall::{SingleWriterTxDatabase, KeyspaceCreateOptions, Readable};
443    /// #
444    /// # let folder = tempfile::tempdir()?;
445    /// # let db = SingleWriterTxDatabase::builder(folder).open()?;
446    /// # let tree = db.keyspace("default", KeyspaceCreateOptions::default)?;
447    /// tree.insert("a", "my_value")?;
448    ///
449    /// assert!(tree.contains_key("a")?);
450    /// assert!(!tree.contains_key("b")?);
451    /// #
452    /// # Ok::<(), fjall::Error>(())
453    /// ```
454    ///
455    /// # Errors
456    ///
457    /// Will return `Err` if an IO error occurs.
458    pub fn contains_key<K: AsRef<[u8]>>(&self, key: K) -> crate::Result<bool> {
459        self.inner.contains_key(key)
460    }
461
462    /// Allows access to the inner keyspace handle, allowing to
463    /// escape from the transactional context.
464    #[doc(hidden)]
465    #[must_use]
466    pub fn inner(&self) -> &Keyspace {
467        &self.inner
468    }
469}