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}