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}