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
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
// Copyright (c) 2024-present, fjall-rs
// This source code is licensed under both the Apache 2.0 and MIT License
// (found in the LICENSE-* files in the repository)
use crate::{db_config::CompactionFilterAssigner, tx::single_writer::Openable, Config};
use lsm_tree::{Cache, CompressionType, DescriptorTable};
use std::{marker::PhantomData, path::Path, sync::Arc};
/// Database builder
pub struct Builder<O: Openable> {
inner: Config,
_phantom: PhantomData<O>,
}
impl<O: Openable> Builder<O> {
pub(crate) fn new(path: &Path) -> Self {
Self {
inner: Config::new(path),
_phantom: PhantomData,
}
}
#[doc(hidden)]
#[must_use]
pub fn into_config(self) -> Config {
self.inner
}
/// Opens the database, creating it if it does not exist.
///
/// # Errors
///
/// Errors if an I/O error occurred, or if the database can not be opened.
pub fn open(self) -> crate::Result<O> {
O::open(self.inner)
}
/// Sets the cache capacity in bytes.
///
/// It is recommended to configure the block cache capacity to be ~20-25% of the available memory - or more **if** the data set _fully_ fits into memory.
#[must_use]
pub fn cache_size(mut self, size_bytes: u64) -> Self {
self.inner.cache = Arc::new(Cache::with_capacity_bytes(size_bytes));
self
}
/// Sets the compression type to use for large values that are written into the journal file.
#[must_use]
pub fn journal_compression(mut self, comp: CompressionType) -> Self {
self.inner.journal_compression_type = comp;
self
}
/// If `false`, write batches or transactions automatically flush data to the operating system.
///
/// Default = false
///
/// Set to `true` to handle persistence manually, e.g. manually using `PersistMode::SyncData` for ACID transactions.
#[must_use]
pub fn manual_journal_persist(mut self, flag: bool) -> Self {
self.inner.manual_journal_persist = flag;
self
}
/// Sets the number of worker threads.
///
/// Default = min(# CPU cores, 4)
///
/// # Panics
///
/// Panics, if below 1.
#[must_use]
pub fn worker_threads(self, n: usize) -> Self {
#[cfg(not(test))]
assert!(n > 0, "worker count must be at least 1");
self.worker_threads_unchecked(n)
}
#[doc(hidden)]
#[must_use]
pub fn worker_threads_unchecked(mut self, n: usize) -> Self {
self.inner.worker_threads = n;
self
}
/// Sets the upper limit for cached file descriptors.
///
/// # Note
///
/// Setting to None is currently not supported.
///
/// # Panics
///
/// Panics if n < 10 or `None`.
#[must_use]
pub fn max_cached_files(mut self, n: Option<usize>) -> Self {
self.inner.descriptor_table = n.map(|n| Arc::new(DescriptorTable::new(n)));
self
}
/// Maximum size of all journals in bytes.
///
/// Default = 512 MiB
///
/// # Panics
///
/// Panics if < 64 MiB.
///
/// Same as `max_total_wal_size` in `RocksDB`.
#[must_use]
pub fn max_journaling_size(mut self, bytes: u64) -> Self {
assert!(bytes >= 64 * 1_024 * 1_024);
self.inner.max_journaling_size_in_bytes = bytes;
self
}
/// Maximum size of all memtables in bytes.
///
/// Similar to `db_write_buffer_size` in `RocksDB`, however it is disabled by default in `RocksDB`.
///
/// Set to `u64::MAX` or `0` to disable it.
///
/// Default = off
///
/// # Panics
///
/// Panics if bytes < 1 MiB.
#[doc(hidden)]
#[must_use]
#[deprecated = "todo"]
pub fn max_write_buffer_size(mut self, bytes: Option<u64>) -> Self {
if let Some(bytes) = bytes {
assert!(bytes >= 1_024 * 1_024);
}
self.inner.max_write_buffer_size_in_bytes = bytes;
self
}
/// Sets the `Database` to clean upon drop.
///
/// # Examples
///
/// ```
/// # use fjall::{PersistMode, Database, KeyspaceCreateOptions};
/// # let folder = tempfile::tempdir()?.into_path();
/// let db = Database::builder(&folder).temporary(true).open()?;
///
/// assert!(folder.try_exists()?);
/// drop(db);
/// assert!(!folder.try_exists()?);
/// #
/// # Ok::<_, fjall::Error>(())
/// ```
#[must_use]
pub fn temporary(mut self, flag: bool) -> Self {
self.inner.clean_path_on_drop = flag;
self
}
/// Installs a factory that assigns compaction filters to new or recovered keyspaces.
///
/// # Examples
///
/// ```
/// # use fjall::{PersistMode, Database, KeyspaceCreateOptions};
/// # use std::sync::Arc;
/// # let folder = tempfile::tempdir()?.keep();
/// use lsm_tree::compaction::filter::Factory;
///
/// let db = Database::builder(&folder)
/// .temporary(true)
/// .with_compaction_filter_factories(
/// Arc::new(|keyspace| {
/// // Match on the keyspace name to assign specific compaction filters
/// todo!()
/// })
/// )
/// .open()?;
///
/// #
/// # Ok::<_, fjall::Error>(())
/// ```
pub fn with_compaction_filter_factories(mut self, f: CompactionFilterAssigner) -> Self {
self.inner.compaction_filter_factory_assigner = Some(f);
self
}
}