lsm_tree/
snapshot.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::{
6    value::{SeqNo, UserKey, UserValue},
7    AbstractTree, AnyTree, KvPair,
8};
9use std::ops::RangeBounds;
10
11/// A snapshot captures a read-only point-in-time view of the tree at the time the snapshot was created
12///
13/// As long as the snapshot is open, old versions of objects will not be evicted as to
14/// keep the snapshot consistent. Thus, snapshots should only be kept around for as little as possible.
15///
16/// Snapshots do not persist across restarts.
17#[derive(Clone)]
18pub struct Snapshot {
19    tree: AnyTree,
20
21    #[doc(hidden)]
22    pub seqno: SeqNo,
23}
24
25impl Snapshot {
26    /// Creates a snapshot
27    pub(crate) fn new(tree: AnyTree, seqno: SeqNo) -> Self {
28        log::trace!("Opening snapshot with seqno: {seqno}");
29        Self { tree, seqno }
30    }
31
32    /// Retrieves an item from the snapshot.
33    ///
34    /// # Examples
35    ///
36    /// ```
37    /// # let folder = tempfile::tempdir()?;
38    /// use lsm_tree::{AbstractTree, Config, Tree};
39    ///
40    /// let tree = Config::new(folder).open()?;
41    /// let snapshot = tree.snapshot(0);
42    ///
43    /// tree.insert("a", "my_value", 0);
44    ///
45    /// let len = snapshot.size_of("a")?;
46    /// assert_eq!(None, len);
47    ///
48    /// let snapshot = tree.snapshot(1);
49    ///
50    /// let len = snapshot.size_of("a")?.unwrap_or_default();
51    /// assert_eq!("my_value".len() as u32, len);
52    ///
53    /// let len = snapshot.size_of("b")?.unwrap_or_default();
54    /// assert_eq!(0, len);
55    /// #
56    /// # Ok::<(), lsm_tree::Error>(())
57    /// ```
58    ///
59    /// # Errors
60    ///
61    /// Will return `Err` if an IO error occurs.
62    pub fn size_of<K: AsRef<[u8]>>(&self, key: K) -> crate::Result<Option<u32>> {
63        self.tree.size_of(key, Some(self.seqno))
64    }
65
66    /// Retrieves an item from the snapshot.
67    ///
68    /// # Examples
69    ///
70    /// ```
71    /// # let folder = tempfile::tempdir()?;
72    /// use lsm_tree::{AbstractTree, Config, Tree};
73    ///
74    /// let tree = Config::new(folder).open()?;
75    /// let snapshot = tree.snapshot(0);
76    ///
77    /// tree.insert("a", "my_value", 0);
78    ///
79    /// let item = snapshot.get("a")?;
80    /// assert_eq!(None, item);
81    ///
82    /// let snapshot = tree.snapshot(1);
83    ///
84    /// let item = snapshot.get("a")?.unwrap();
85    /// assert_eq!(b"my_value", &*item);
86    /// #
87    /// # Ok::<(), lsm_tree::Error>(())
88    /// ```
89    ///
90    /// # Errors
91    ///
92    /// Will return `Err` if an IO error occurs.
93    pub fn get<K: AsRef<[u8]>>(&self, key: K) -> crate::Result<Option<UserValue>> {
94        self.tree.get(key, Some(self.seqno))
95    }
96
97    /// Returns an iterator that scans through the entire snapshot.
98    ///
99    /// Avoid using this function, or limit it as otherwise it may scan a lot of items.
100    ///
101    /// # Examples
102    ///
103    /// ```
104    /// # let folder = tempfile::tempdir()?;
105    /// use lsm_tree::{AbstractTree, Config, Tree};
106    ///
107    /// let tree = Config::new(folder).open()?;
108    ///
109    /// tree.insert("a", "abc", 0);
110    /// tree.insert("f", "abc", 1);
111    /// let snapshot = tree.snapshot(2);
112    ///
113    /// tree.insert("g", "abc", 2);
114    ///
115    /// assert_eq!(2, snapshot.iter().count());
116    /// #
117    /// # Ok::<(), lsm_tree::Error>(())
118    /// ```
119    #[must_use]
120    pub fn iter(&self) -> impl DoubleEndedIterator<Item = crate::Result<KvPair>> + 'static {
121        self.tree.iter(Some(self.seqno), None)
122    }
123
124    /// Returns an iterator that scans through the entire snapshot, returning keys only.
125    ///
126    /// Avoid using this function, or limit it as otherwise it may scan a lot of items.
127    ///
128    /// # Examples
129    ///
130    /// ```
131    /// # let folder = tempfile::tempdir()?;
132    /// use lsm_tree::{AbstractTree, Config, Tree};
133    ///
134    /// let tree = Config::new(folder).open()?;
135    ///
136    /// tree.insert("a", "abc", 0);
137    /// tree.insert("f", "abc", 1);
138    /// let snapshot = tree.snapshot(2);
139    ///
140    /// tree.insert("g", "abc", 2);
141    ///
142    /// assert_eq!(2, snapshot.keys().count());
143    /// #
144    /// # Ok::<(), lsm_tree::Error>(())
145    /// ```
146    #[must_use]
147    pub fn keys(&self) -> impl DoubleEndedIterator<Item = crate::Result<UserKey>> + 'static {
148        self.tree.keys(Some(self.seqno), None)
149    }
150
151    /// Returns an iterator that scans through the entire snapshot, returning values only.
152    ///
153    /// Avoid using this function, or limit it as otherwise it may scan a lot of items.
154    ///
155    /// # Examples
156    ///
157    /// ```
158    /// # let folder = tempfile::tempdir()?;
159    /// use lsm_tree::{AbstractTree, Config, Tree};
160    ///
161    /// let tree = Config::new(folder).open()?;
162    ///
163    /// tree.insert("a", "abc", 0);
164    /// tree.insert("f", "abc", 1);
165    /// let snapshot = tree.snapshot(2);
166    ///
167    /// tree.insert("g", "abc", 2);
168    ///
169    /// assert_eq!(2, snapshot.values().count());
170    /// #
171    /// # Ok::<(), lsm_tree::Error>(())
172    /// ```
173    #[must_use]
174    pub fn values(&self) -> impl DoubleEndedIterator<Item = crate::Result<UserValue>> + 'static {
175        self.tree.values(Some(self.seqno), None)
176    }
177
178    /// Returns an iterator over a range of items in the snapshot.
179    ///
180    /// Avoid using full or unbounded ranges as they may scan a lot of items (unless limited).
181    ///
182    /// # Examples
183    ///
184    /// ```
185    /// # let folder = tempfile::tempdir()?;
186    /// use lsm_tree::{AbstractTree, Config, Tree};
187    ///
188    /// let tree = Config::new(folder).open()?;
189    ///
190    /// tree.insert("a", "abc", 0);
191    /// let snapshot = tree.snapshot(1);
192    ///
193    /// tree.insert("f", "abc", 1);
194    /// tree.insert("g", "abc", 2);
195    ///
196    /// assert_eq!(1, snapshot.range("a"..="f").count());
197    /// #
198    /// # Ok::<(), lsm_tree::Error>(())
199    /// ```
200    pub fn range<K: AsRef<[u8]>, R: RangeBounds<K>>(
201        &self,
202        range: R,
203    ) -> impl DoubleEndedIterator<Item = crate::Result<KvPair>> + 'static {
204        self.tree.range(range, Some(self.seqno), None)
205    }
206
207    /// Returns an iterator over a prefixed set of items in the snapshot.
208    ///
209    /// Avoid using an empty prefix as it may scan a lot of items (unless limited).
210    ///
211    /// # Examples
212    ///
213    /// ```
214    /// # let folder = tempfile::tempdir()?;
215    /// use lsm_tree::{AbstractTree, Config, Tree};
216    ///
217    /// let tree = Config::new(folder).open()?;
218    ///
219    /// tree.insert("a", "abc", 0);
220    /// tree.insert("ab", "abc", 1);
221    /// let snapshot = tree.snapshot(2);
222    ///
223    /// tree.insert("abc", "abc", 2);
224    ///
225    /// assert_eq!(2, snapshot.prefix("a").count());
226    /// #
227    /// # Ok::<(), lsm_tree::Error>(())
228    /// ```
229    pub fn prefix<K: AsRef<[u8]>>(
230        &self,
231        prefix: K,
232    ) -> impl DoubleEndedIterator<Item = crate::Result<KvPair>> + 'static {
233        self.tree.prefix(prefix, Some(self.seqno), None)
234    }
235
236    /// Returns the first key-value pair in the snapshot.
237    /// The key in this pair is the minimum key in the snapshot.
238    ///
239    /// # Examples
240    ///
241    /// ```
242    /// # use lsm_tree::Error as TreeError;
243    /// use lsm_tree::{AbstractTree, Config, Tree};
244    ///
245    /// # let folder = tempfile::tempdir()?;
246    /// let tree = Config::new(folder).open()?;
247    ///
248    /// tree.insert("5", "abc", 0);
249    /// tree.insert("3", "abc", 1);
250    /// let snapshot = tree.snapshot(2);
251    ///
252    /// tree.insert("1", "abc", 2);
253    ///
254    /// let (key, _) = snapshot.first_key_value()?.expect("item should exist");
255    /// assert_eq!(&*key, "3".as_bytes());
256    /// #
257    /// # Ok::<(), TreeError>(())
258    /// ```
259    ///
260    /// # Errors
261    ///
262    /// Will return `Err` if an IO error occurs.
263    pub fn first_key_value(&self) -> crate::Result<Option<(UserKey, UserValue)>> {
264        self.iter().next().transpose()
265    }
266
267    /// Returns the las key-value pair in the snapshot.
268    /// The key in this pair is the maximum key in the snapshot.
269    ///
270    /// # Examples
271    ///
272    /// ```
273    /// # use lsm_tree::Error as TreeError;
274    /// use lsm_tree::{AbstractTree, Config, Tree};
275    ///
276    /// # let folder = tempfile::tempdir()?;
277    /// let tree = Config::new(folder).open()?;
278    ///
279    /// tree.insert("1", "abc", 0);
280    /// tree.insert("3", "abc", 1);
281    /// let snapshot = tree.snapshot(2);
282    ///
283    /// tree.insert("5", "abc", 2);
284    ///
285    /// let (key, _) = snapshot.last_key_value()?.expect("item should exist");
286    /// assert_eq!(&*key, "3".as_bytes());
287    /// #
288    /// # Ok::<(), TreeError>(())
289    /// ```
290    ///
291    /// # Errors
292    ///
293    /// Will return `Err` if an IO error occurs.
294    pub fn last_key_value(&self) -> crate::Result<Option<(UserKey, UserValue)>> {
295        self.iter().next_back().transpose()
296    }
297
298    /// Returns `true` if the snapshot contains the specified key.
299    ///
300    /// # Examples
301    ///
302    /// ```
303    /// # let folder = tempfile::tempdir()?;
304    /// use lsm_tree::{AbstractTree, Config, Tree};
305    ///
306    /// let tree = Config::new(folder).open()?;
307    /// let snapshot = tree.snapshot(0);
308    ///
309    /// assert!(!snapshot.contains_key("a")?);
310    ///
311    /// tree.insert("a", "abc", 0);
312    /// assert!(!snapshot.contains_key("a")?);
313    /// #
314    /// # Ok::<(), lsm_tree::Error>(())
315    /// ```
316    ///
317    /// # Errors
318    ///
319    /// Will return `Err` if an IO error occurs.
320    pub fn contains_key<K: AsRef<[u8]>>(&self, key: K) -> crate::Result<bool> {
321        self.tree.contains_key(key, Some(self.seqno))
322    }
323
324    /// Returns `true` if the snapshot is empty.
325    ///
326    /// This operation has O(1) complexity.
327    ///
328    /// # Examples
329    ///
330    /// ```
331    /// # let folder = tempfile::tempdir()?;
332    /// use lsm_tree::{AbstractTree, Config, Tree};
333    ///
334    /// let tree = Config::new(folder).open()?;
335    /// let snapshot = tree.snapshot(0);
336    ///
337    /// assert!(snapshot.is_empty()?);
338    ///
339    /// tree.insert("a", "abc", 0);
340    /// assert!(snapshot.is_empty()?);
341    /// #
342    /// # Ok::<(), lsm_tree::Error>(())
343    /// ```
344    ///
345    /// # Errors
346    ///
347    /// Will return `Err` if an IO error occurs.
348    pub fn is_empty(&self) -> crate::Result<bool> {
349        self.first_key_value().map(|x| x.is_none())
350    }
351
352    /// Scans the entire snapshot, returning the amount of items.
353    ///
354    /// ###### Caution
355    ///
356    /// This operation scans the entire tree: O(n) complexity!
357    ///
358    /// Never, under any circumstances, use .`len()` == 0 to check
359    /// if the snapshot is empty, use [`Snapshot::is_empty`] instead.
360    ///
361    /// # Examples
362    ///
363    /// ```
364    /// # use lsm_tree::Error as TreeError;
365    /// use lsm_tree::{AbstractTree, Config, Tree};
366    ///
367    /// # let folder = tempfile::tempdir()?;
368    /// let tree = Config::new(folder).open()?;
369    /// let snapshot = tree.snapshot(0);
370    ///
371    /// assert_eq!(snapshot.len()?, 0);
372    /// tree.insert("1", "abc", 0);
373    /// tree.insert("3", "abc", 1);
374    /// tree.insert("5", "abc", 2);
375    /// assert_eq!(snapshot.len()?, 0);
376    /// #
377    /// # Ok::<(), TreeError>(())
378    /// ```
379    ///
380    /// # Errors
381    ///
382    /// Will return `Err` if an IO error occurs.
383    pub fn len(&self) -> crate::Result<usize> {
384        let mut count = 0;
385
386        for item in self.iter() {
387            let _ = item?;
388            count += 1;
389        }
390
391        Ok(count)
392    }
393}