1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
//! Fjall is an LSM-based embedded key-value storage engine written in Rust. It features:
//!
//! - Thread-safe BTreeMap-like API
//! - 100% safe & stable Rust
//! - Range & prefix searching with forward and reverse iteration
//! - Cross-partition snapshots (MVCC)
//! - Automatic background maintenance
//!
//! 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.
//! 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
//! single database-level journal, which will be recovered after a crash.
//!
//! It is not:
//!
//! - a standalone server
//! - a relational database
//! - a wide-column database: it has no notion of columns
//!
//! 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.
//!
//! For the underlying LSM-tree implementation, see: <https://crates.io/crates/lsm-tree>.
//!
//! ```
//! use fjall::{Config, Keyspace, PartitionCreateOptions};
//!
//! # let folder = tempfile::tempdir()?;
//! #
//! let keyspace = Config::new(folder).open()?;
//!
//! // Each partition is its own physical LSM-tree
//! let items = keyspace.open_partition("my_items", PartitionCreateOptions::default())?;
//!
//! // Write some data
//! items.insert("a", "hello")?;
//!
//! // And retrieve it
//! let bytes = items.get("a")?;
//!
//! // Or remove it again
//! items.remove("a")?;
//!
//! // Search by prefix
//! for item in &items.prefix("prefix") {
//!   // ...
//! }
//!
//! // Search by range
//! for item in &items.range("a"..="z") {
//!   // ...
//! }
//!
//! // Iterators implement DoubleEndedIterator, so you can search backwards, too!
//! for item in items.prefix("prefix").into_iter().rev() {
//!   // ...
//! }
//!
//! // Sync the journal to disk to make sure data is definitely durable
//! // When the keyspace is dropped, it will try to persist
//! // Also, by default every second the keyspace will be persisted asynchronously
//! keyspace.persist()?;
//!
//! // Destroy the partition, removing all data in it.
//! // This may be useful when using temporary tables or indexes,
//! // as it is essentially an O(1) operation.
//! keyspace.delete_partition(items)?;
//! #
//! # Ok::<_, fjall::Error>(())
//! ```
#![doc(html_logo_url = "https://raw.githubusercontent.com/marvin-j97/fjall/main/fjall/logo.png")]
#![doc(html_favicon_url = "https://raw.githubusercontent.com/marvin-j97/fjall/main/fjall/logo.png")]
#![forbid(unsafe_code)]
#![deny(clippy::all, missing_docs)]
#![deny(clippy::unwrap_used, clippy::indexing_slicing)]
#![warn(clippy::pedantic, clippy::nursery, clippy::cargo)]
#![warn(clippy::expect_used)]
#![allow(clippy::missing_const_for_fn)]

mod batch;

/// Contains compaction strategies
pub mod compaction;

mod config;
mod error;
mod file;
mod flush;
mod journal;
mod keyspace;
mod monitor;
mod partition;
mod recovery;
mod sharded;
mod version;
mod write_buffer_manager;

pub use lsm_tree::{BlockCache, Snapshot};

pub use {
    batch::Batch,
    config::Config,
    error::{Error, Result},
    keyspace::Keyspace,
    partition::{config::CreateOptions as PartitionCreateOptions, PartitionHandle},
};

/// A snapshot moment
///
/// See [`Keyspace::instant`].
pub type Instant = lsm_tree::SeqNo;

/// Re-export of [`lsm_tree::Error`]
pub type LsmError = lsm_tree::Error;