fjall/
lib.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
5//! Fjall is a log-structured embeddable key-value storage engine written in Rust. It features:
6//!
7//! - Thread-safe BTreeMap-like API
8//! - 100% safe & stable Rust
9//! - LSM-tree-based storage similar to `RocksDB`
10//! - Range & prefix searching with forward and reverse iteration
11//! - Cross-partition snapshots (MVCC)
12//! - Automatic background maintenance
13//! - Single-writer transactions (optional)
14//! - Key-value separation for large blob use cases (optional)
15//!
16//! Each `Keyspace` is a single logical database and is split into `partitions` (a.k.a. column families) - you should probably only use a single keyspace for your application.
17//! Each partition is physically a single LSM-tree and its own logical collection; however, write operations across partitions are atomic as they are persisted in a
18//! single database-level journal, which will be recovered after a crash.
19//!
20//! It is not:
21//!
22//! - a standalone server
23//! - a relational or wide-column database: it has no notion of columns
24//!
25//! Keys are limited to 65536 bytes, values are limited to 2^32 bytes. As is normal with any kind of storage engine, larger keys and values have a bigger performance impact.
26//!
27//! For the underlying LSM-tree implementation, see: <https://crates.io/crates/lsm-tree>.
28//!
29//! ```
30//! use fjall::{Config, PersistMode, Keyspace, PartitionCreateOptions};
31//!
32//! # let folder = tempfile::tempdir()?;
33//! #
34//! // A keyspace is a database, which may contain multiple collections ("partitions")
35//! // You should probably only use a single keyspace for your application
36//! //
37//! let keyspace = Config::new(folder).open()?; // or open_transactional for transactional semantics
38//!
39//! // Each partition is its own physical LSM-tree
40//! let items = keyspace.open_partition("my_items", PartitionCreateOptions::default())?;
41//!
42//! // Write some data
43//! items.insert("a", "hello")?;
44//!
45//! // And retrieve it
46//! let bytes = items.get("a")?;
47//!
48//! // Or remove it again
49//! items.remove("a")?;
50//!
51//! // Search by prefix
52//! for kv in items.prefix("prefix") {
53//!   // ...
54//! }
55//!
56//! // Search by range
57//! for kv in items.range("a"..="z") {
58//!   // ...
59//! }
60//!
61//! // Iterators implement DoubleEndedIterator, so you can search backwards, too!
62//! for kv in items.prefix("prefix").rev() {
63//!   // ...
64//! }
65//!
66//! // Sync the journal to disk to make sure data is definitely durable
67//! // When the keyspace is dropped, it will try to persist with `PersistMode::SyncAll` as well
68//! keyspace.persist(PersistMode::SyncAll)?;
69//! #
70//! # Ok::<_, fjall::Error>(())
71//! ```
72
73#![doc(html_logo_url = "https://raw.githubusercontent.com/fjall-rs/fjall/main/logo.png")]
74#![doc(html_favicon_url = "https://raw.githubusercontent.com/fjall-rs/fjall/main/logo.png")]
75#![forbid(unsafe_code)]
76#![deny(clippy::all, missing_docs, clippy::cargo)]
77#![deny(clippy::unwrap_used)]
78#![deny(clippy::indexing_slicing)]
79#![warn(clippy::pedantic, clippy::nursery)]
80#![warn(clippy::expect_used)]
81#![allow(clippy::missing_const_for_fn)]
82#![warn(clippy::multiple_crate_versions)]
83
84mod batch;
85
86/// Contains compaction strategies
87pub mod compaction;
88
89mod config;
90
91#[cfg(feature = "__internal_whitebox")]
92#[doc(hidden)]
93pub mod drop;
94
95mod background_worker;
96mod error;
97mod file;
98mod flush;
99mod gc;
100mod iter;
101mod journal;
102mod keyspace;
103mod monitor;
104mod partition;
105mod path;
106mod poison_dart;
107mod recovery;
108mod snapshot_nonce;
109mod snapshot_tracker;
110mod stats;
111mod tracked_snapshot;
112
113#[cfg(any(feature = "single_writer_tx", feature = "ssi_tx"))]
114mod tx;
115
116mod version;
117mod write_buffer_manager;
118
119pub(crate) type HashMap<K, V> = std::collections::HashMap<K, V, xxhash_rust::xxh3::Xxh3Builder>;
120pub(crate) type HashSet<K> = std::collections::HashSet<K, xxhash_rust::xxh3::Xxh3Builder>;
121
122pub use {
123    batch::{Batch, CompactItem},
124    config::Config,
125    error::{Error, Result},
126    gc::GarbageCollection,
127    journal::{error::RecoveryError, writer::PersistMode},
128    keyspace::Keyspace,
129    partition::{
130        options::CreateOptions as PartitionCreateOptions, options::KvSeparationOptions,
131        PartitionHandle,
132    },
133    tracked_snapshot::TrackedSnapshot as Snapshot,
134    version::Version,
135};
136
137#[cfg(any(feature = "single_writer_tx", feature = "ssi_tx"))]
138pub use tx::{
139    keyspace::{TransactionalKeyspace, TxKeyspace},
140    partition::TransactionalPartitionHandle,
141    read_tx::ReadTransaction,
142    write_tx::WriteTransaction,
143};
144
145/// Alias for [`Batch`]
146pub type WriteBatch = Batch;
147
148/// Alias for [`PartitionHandle`]
149pub type Partition = PartitionHandle;
150
151/// Alias for [`TransactionalPartitionHandle`]
152#[cfg(any(feature = "single_writer_tx", feature = "ssi_tx"))]
153pub type TxPartition = TransactionalPartitionHandle;
154
155/// Alias for [`TransactionalPartitionHandle`]
156#[cfg(any(feature = "single_writer_tx", feature = "ssi_tx"))]
157pub type TxPartitionHandle = TransactionalPartitionHandle;
158
159/// Alias for [`TransactionalPartitionHandle`]
160#[cfg(any(feature = "single_writer_tx", feature = "ssi_tx"))]
161pub type TransactionalPartition = TransactionalPartitionHandle;
162
163/// A snapshot moment
164///
165/// See [`Keyspace::instant`].
166pub type Instant = lsm_tree::SeqNo;
167
168/// Re-export of [`lsm_tree::Error`]
169pub type LsmError = lsm_tree::Error;
170
171#[doc(hidden)]
172pub use lsm_tree::AbstractTree;
173
174pub use lsm_tree::{AnyTree, CompressionType, KvPair, Slice, TreeType, UserKey, UserValue};
175
176// TODO: remove in V3
177
178/// Block cache that caches frequently read disk blocks
179#[deprecated = "Use Config::cache_size instead"]
180pub struct BlockCache(u64);
181
182#[allow(deprecated)]
183impl BlockCache {
184    /// Creates a new cache with given capacity in bytes.
185    #[must_use]
186    pub fn with_capacity_bytes(bytes: u64) -> Self {
187        Self(bytes)
188    }
189
190    /// Returns the cache capacity in bytes.
191    #[must_use]
192    pub fn capacity(&self) -> u64 {
193        self.0
194    }
195}
196
197/// Blob cache that caches frequently read blobs
198#[deprecated = "Use Config::cache_size instead"]
199#[allow(deprecated)]
200pub struct BlobCache(BlockCache);
201
202#[allow(deprecated)]
203impl BlobCache {
204    /// Creates a new cache with given capacity in bytes.
205    #[must_use]
206    pub fn with_capacity_bytes(bytes: u64) -> Self {
207        #[allow(deprecated)]
208        Self(BlockCache::with_capacity_bytes(bytes))
209    }
210}
211
212#[allow(deprecated)]
213impl std::ops::Deref for BlobCache {
214    type Target = BlockCache;
215
216    fn deref(&self) -> &Self::Target {
217        &self.0
218    }
219}