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#![deny(clippy::all, missing_docs, clippy::cargo)]
76#![deny(clippy::unwrap_used)]
77#![deny(clippy::indexing_slicing)]
78#![warn(clippy::pedantic, clippy::nursery)]
79#![warn(clippy::expect_used)]
80#![allow(clippy::missing_const_for_fn)]
81#![warn(clippy::multiple_crate_versions)]
82
83mod batch;
84
85/// Contains compaction strategies
86pub mod compaction;
87
88mod config;
89
90#[cfg(feature = "__internal_whitebox")]
91#[doc(hidden)]
92pub mod drop;
93
94mod background_worker;
95mod error;
96mod file;
97mod flush;
98mod gc;
99mod iter;
100mod journal;
101mod keyspace;
102mod monitor;
103mod partition;
104mod path;
105mod poison_dart;
106mod recovery;
107mod snapshot_nonce;
108mod snapshot_tracker;
109mod stats;
110mod tracked_snapshot;
111
112#[cfg(any(feature = "single_writer_tx", feature = "ssi_tx"))]
113mod tx;
114
115mod version;
116mod write_buffer_manager;
117
118pub(crate) type HashMap<K, V> = std::collections::HashMap<K, V, xxhash_rust::xxh3::Xxh3Builder>;
119pub(crate) type HashSet<K> = std::collections::HashSet<K, xxhash_rust::xxh3::Xxh3Builder>;
120
121pub use {
122 batch::{Batch, InnerItem, Item},
123 config::Config,
124 error::{Error, Result},
125 gc::GarbageCollection,
126 journal::{error::RecoveryError, writer::PersistMode},
127 keyspace::Keyspace,
128 partition::{
129 options::CreateOptions as PartitionCreateOptions, options::KvSeparationOptions,
130 PartitionHandle,
131 },
132 tracked_snapshot::TrackedSnapshot as Snapshot,
133 version::Version,
134};
135
136#[cfg(any(feature = "single_writer_tx", feature = "ssi_tx"))]
137pub use tx::{
138 keyspace::{TransactionalKeyspace, TxKeyspace},
139 partition::TransactionalPartitionHandle,
140 read_tx::ReadTransaction,
141 write_tx::WriteTransaction,
142};
143
144/// Alias for [`Batch`]
145pub type WriteBatch = Batch;
146
147/// Alias for [`PartitionHandle`]
148pub type Partition = PartitionHandle;
149
150/// Alias for [`TransactionalPartitionHandle`]
151#[cfg(any(feature = "single_writer_tx", feature = "ssi_tx"))]
152pub type TxPartition = TransactionalPartitionHandle;
153
154/// Alias for [`TransactionalPartitionHandle`]
155#[cfg(any(feature = "single_writer_tx", feature = "ssi_tx"))]
156pub type TxPartitionHandle = TransactionalPartitionHandle;
157
158/// Alias for [`TransactionalPartitionHandle`]
159#[cfg(any(feature = "single_writer_tx", feature = "ssi_tx"))]
160pub type TransactionalPartition = TransactionalPartitionHandle;
161
162/// A snapshot moment
163///
164/// See [`Keyspace::instant`].
165pub type Instant = lsm_tree::SeqNo;
166
167/// Re-export of [`lsm_tree::Error`]
168pub type LsmError = lsm_tree::Error;
169
170#[doc(hidden)]
171pub use lsm_tree::AbstractTree;
172
173pub use lsm_tree::{
174 AnyTree, CompressionType, KvPair, Slice, TreeType, UserKey, UserValue, ValueType,
175};
176
177// TODO: remove in V3
178
179/// Block cache that caches frequently read disk blocks
180#[deprecated = "Use Config::cache_size instead"]
181pub struct BlockCache(u64);
182
183#[allow(deprecated)]
184impl BlockCache {
185 /// Creates a new cache with given capacity in bytes.
186 #[must_use]
187 pub fn with_capacity_bytes(bytes: u64) -> Self {
188 Self(bytes)
189 }
190
191 /// Returns the cache capacity in bytes.
192 #[must_use]
193 pub fn capacity(&self) -> u64 {
194 self.0
195 }
196}
197
198/// Blob cache that caches frequently read blobs
199#[deprecated = "Use Config::cache_size instead"]
200#[allow(deprecated)]
201pub struct BlobCache(BlockCache);
202
203#[allow(deprecated)]
204impl BlobCache {
205 /// Creates a new cache with given capacity in bytes.
206 #[must_use]
207 pub fn with_capacity_bytes(bytes: u64) -> Self {
208 #[allow(deprecated)]
209 Self(BlockCache::with_capacity_bytes(bytes))
210 }
211}
212
213#[allow(deprecated)]
214impl std::ops::Deref for BlobCache {
215 type Target = BlockCache;
216
217 fn deref(&self) -> &Self::Target {
218 &self.0
219 }
220}