signet_libmdbx/flags.rs
1//! Environment and database flags.
2
3use bitflags::bitflags;
4use ffi::*;
5use std::str::FromStr;
6
7/// MDBX sync mode
8#[derive(PartialEq, Eq, Clone, Copy, Debug, Default)]
9pub enum SyncMode {
10 /// Default robust and durable sync mode.
11 /// Metadata is written and flushed to disk after a data is written and flushed, which
12 /// guarantees the integrity of the database in the event of a crash at any time.
13 #[default]
14 Durable,
15
16 /// Don't sync the meta-page after commit.
17 ///
18 /// Flush system buffers to disk only once per transaction commit, omit the metadata flush.
19 /// Defer that until the system flushes files to disk, or next non-read-only commit or
20 /// [`Environment::sync()`](crate::Environment::sync). Depending on the platform and
21 /// hardware, with [`SyncMode::NoMetaSync`] you may get a doubling of write performance.
22 ///
23 /// This trade-off maintains database integrity, but a system crash may undo the last committed
24 /// transaction. I.e. it preserves the ACPI (atomicity, consistency, isolation) but not D
25 /// (durability) database property.
26 NoMetaSync,
27
28 /// Don't sync anything but keep previous steady commits.
29 ///
30 /// [`SyncMode::UtterlyNoSync`] the [`SyncMode::SafeNoSync`] flag disable similarly flush
31 /// system buffers to disk when committing a transaction. But there is a huge difference in
32 /// how are recycled the MVCC snapshots corresponding to previous "steady" transactions
33 /// (see below).
34 ///
35 /// With [`crate::sys::EnvironmentKind::WriteMap`] the [`SyncMode::SafeNoSync`] instructs MDBX to
36 /// use asynchronous mmap-flushes to disk. Asynchronous mmap-flushes means that actually
37 /// all writes will scheduled and performed by operation system on it own manner, i.e.
38 /// unordered. MDBX itself just notify operating system that it would be nice to write data
39 /// to disk, but no more.
40 ///
41 /// Depending on the platform and hardware, with [`SyncMode::SafeNoSync`] you may get a
42 /// multiple increase of write performance, even 10 times or more.
43 ///
44 /// In contrast to [`SyncMode::UtterlyNoSync`] mode, with [`SyncMode::SafeNoSync`] flag MDBX
45 /// will keeps untouched pages within B-tree of the last transaction "steady" which was
46 /// synced to disk completely. This has big implications for both data durability and
47 /// (unfortunately) performance:
48 /// - A system crash can't corrupt the database, but you will lose the last transactions;
49 /// because MDBX will rollback to last steady commit since it kept explicitly.
50 /// - The last steady transaction makes an effect similar to "long-lived" read transaction
51 /// since prevents reuse of pages freed by newer write transactions, thus the any data
52 /// changes will be placed in newly allocated pages.
53 /// - To avoid rapid database growth, the system will sync data and issue a steady commit-point
54 /// to resume reuse pages, each time there is insufficient space and before increasing the
55 /// size of the file on disk.
56 ///
57 /// In other words, with
58 /// [`SyncMode::SafeNoSync`] flag MDBX protects you from the whole database corruption, at the
59 /// cost increasing database size and/or number of disk IOPs. So, [`SyncMode::SafeNoSync`]
60 /// flag could be used with [`Environment::sync()`](crate::Environment::sync) as alternatively
61 /// for batch committing or nested transaction (in some cases).
62 ///
63 /// The number and volume of disk IOPs with [`SyncMode::SafeNoSync`] flag will exactly the
64 /// as without any no-sync flags. However, you should expect a larger process's work set
65 /// and significantly worse a locality of reference, due to the more intensive allocation
66 /// of previously unused pages and increase the size of the database.
67 SafeNoSync,
68
69 /// Don't sync anything and wipe previous steady commits.
70 ///
71 /// Don't flush system buffers to disk when committing a transaction.
72 /// This optimization means a system crash can corrupt the database, if buffers are not yet
73 /// flushed to disk. Depending on the platform and hardware, with [`SyncMode::UtterlyNoSync`]
74 /// you may get a multiple increase of write performance, even 100 times or more.
75 ///
76 /// If the filesystem preserves write order (which is rare and never provided unless explicitly
77 /// noted) and the [`WriteMap`](crate::sys::EnvironmentKind::WriteMap) and
78 /// [`EnvironmentFlags::liforeclaim`] flags are not used, then a system crash can't corrupt
79 /// the database, but you can lose the last transactions, if at least one buffer is not yet
80 /// flushed to disk. The risk is governed by how often the system flushes dirty buffers to
81 /// disk and how often [`Environment::sync()`](crate::Environment::sync) is called. So,
82 /// transactions exhibit ACPI (atomicity, consistency, isolation) properties and only lose D
83 /// (durability). I.e. database integrity is maintained, but a system crash may undo the
84 /// final transactions.
85 ///
86 /// Otherwise, if the filesystem not preserves write order (which is typically) or
87 /// [`WriteMap`](crate::sys::EnvironmentKind::WriteMap) or [`EnvironmentFlags::liforeclaim`] flags
88 /// are used, you should expect the corrupted database after a system crash.
89 ///
90 /// So, most important thing about [`SyncMode::UtterlyNoSync`]:
91 /// - A system crash immediately after commit the write transaction high likely lead to
92 /// database corruption.
93 /// - Successful completion of [`Environment::sync(force=true`)](crate::Environment::sync)
94 /// after one or more committed transactions guarantees consistency and durability.
95 /// - BUT by committing two or more transactions you back database into a weak state, in which
96 /// a system crash may lead to database corruption! In case single transaction after
97 /// [`Environment::sync()`](crate::Environment::sync), you may lose transaction itself, but
98 /// not a whole database.
99 ///
100 /// Nevertheless, [`SyncMode::UtterlyNoSync`] provides "weak" durability in
101 /// case of an application crash (but no durability on system failure), and therefore may
102 /// be very useful in scenarios where data durability is not required over a system failure
103 /// (e.g for short-lived data), or if you can take such risk.
104 UtterlyNoSync,
105}
106
107/// Environment mode (read-only or read-write).
108#[derive(Clone, Copy, Debug)]
109pub enum Mode {
110 /// Read-only mode.
111 ReadOnly,
112 /// Read-write mode with specified sync mode.
113 ReadWrite {
114 /// Sync mode for write transactions.
115 sync_mode: SyncMode,
116 },
117}
118
119impl Default for Mode {
120 fn default() -> Self {
121 Self::ReadWrite { sync_mode: SyncMode::default() }
122 }
123}
124
125impl From<Mode> for EnvironmentFlags {
126 fn from(mode: Mode) -> Self {
127 Self { mode, ..Default::default() }
128 }
129}
130
131impl FromStr for SyncMode {
132 type Err = String;
133
134 fn from_str(s: &str) -> Result<Self, Self::Err> {
135 let val = s.trim().to_ascii_lowercase();
136 match val.as_str() {
137 "durable" => Ok(Self::Durable),
138 "safe-no-sync" | "safenosync" | "safe_no_sync" => Ok(Self::SafeNoSync),
139 _ => Err(format!(
140 "invalid value '{s}' for sync mode. valid values: durable, safe-no-sync"
141 )),
142 }
143 }
144}
145
146/// Environment opening flags.
147#[derive(Clone, Copy, Debug, Default)]
148pub struct EnvironmentFlags {
149 /// Do not create a subdirectory for the environment.
150 pub no_sub_dir: bool,
151 /// Open the environment in exclusive mode.
152 pub exclusive: bool,
153 /// Flag is intended to open an existing sub-database which was created with unknown flags
154 /// In such cases, instead of returning the `MDBX_INCOMPATIBLE` error, the sub-database will be
155 /// opened with flags which it was created, and then an application could determine the actual
156 /// flags.
157 pub accede: bool,
158 /// Environment mode (RO/RW).
159 pub mode: Mode,
160 /// Disable read-ahead.
161 pub no_rdahead: bool,
162 /// Do not initialize memory before writing to it.
163 pub no_meminit: bool,
164 /// Enable page coalescing.
165 pub coalesce: bool,
166 /// Enable LIFO reclamation of pages.
167 pub liforeclaim: bool,
168}
169
170impl EnvironmentFlags {
171 /// Configures the mdbx flags to use when opening the environment.
172 pub(crate) const fn make_flags(&self) -> ffi::MDBX_env_flags_t {
173 let mut flags = 0;
174
175 if self.no_sub_dir {
176 flags |= ffi::MDBX_NOSUBDIR;
177 }
178
179 if self.exclusive {
180 flags |= ffi::MDBX_EXCLUSIVE;
181 }
182
183 if self.accede {
184 flags |= ffi::MDBX_ACCEDE;
185 }
186
187 match self.mode {
188 Mode::ReadOnly => {
189 flags |= ffi::MDBX_RDONLY;
190 }
191 Mode::ReadWrite { sync_mode } => {
192 flags |= match sync_mode {
193 SyncMode::Durable => ffi::MDBX_SYNC_DURABLE,
194 SyncMode::NoMetaSync => ffi::MDBX_NOMETASYNC,
195 SyncMode::SafeNoSync => ffi::MDBX_SAFE_NOSYNC,
196 SyncMode::UtterlyNoSync => ffi::MDBX_UTTERLY_NOSYNC,
197 };
198 }
199 }
200
201 if self.no_rdahead {
202 flags |= ffi::MDBX_NORDAHEAD;
203 }
204
205 if self.no_meminit {
206 flags |= ffi::MDBX_NOMEMINIT;
207 }
208
209 if self.coalesce {
210 flags |= ffi::MDBX_COALESCE;
211 }
212
213 if self.liforeclaim {
214 flags |= ffi::MDBX_LIFORECLAIM;
215 }
216
217 flags |= ffi::MDBX_NOTLS;
218
219 flags
220 }
221}
222
223bitflags! {
224 #[doc="Database options."]
225 #[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
226 #[repr(transparent)]
227 pub struct DatabaseFlags: MDBX_env_flags_t {
228 /// Use reverse string comparison for keys.
229 const REVERSE_KEY = MDBX_REVERSEKEY;
230 /// Use sorted duplicates, i.e. allow multi-values for a keys.
231 const DUP_SORT = MDBX_DUPSORT;
232 /// Numeric keys in native byte order either uint32_t or uint64_t (must
233 /// be one of uint32_t or uint64_t, other integer types, for example,
234 /// signed integer or uint16_t will not work). The keys must all be of
235 /// the same size and must be aligned while passing as arguments.
236 const INTEGER_KEY = MDBX_INTEGERKEY;
237 /// With MDBX_DUPSORT; sorted dup items have fixed size. The data
238 /// values must all be of the same size.
239 const DUP_FIXED = MDBX_DUPFIXED;
240 /// With MDBX_DUPSORT and with MDBX_DUPFIXED; dups are fixed size like
241 /// MDBX_INTEGERKEY -style integers. The data values must all be of the
242 /// same size and must be aligned while passing as arguments.
243 const INTEGER_DUP = MDBX_INTEGERDUP;
244 /// With MDBX_DUPSORT; use reverse string comparison for data values.
245 const REVERSE_DUP = MDBX_REVERSEDUP;
246 /// Create DB if not already existing.
247 const CREATE = MDBX_CREATE;
248 /// Open an existing table which may be open by another process
249 /// with unknown mode/flags. In such cases, instead of returning the
250 /// `MDBX_INCOMPATIBLE` error, the database will be opened with flags
251 /// which it was created, and then an application could determine the
252 /// actual flags.
253 const ACCEDE = MDBX_DB_ACCEDE;
254 }
255}
256
257bitflags! {
258 #[doc="Write options."]
259 #[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
260 #[repr(transparent)]
261 pub struct WriteFlags: MDBX_env_flags_t {
262 /// Upsertion by default (without any other flags)
263 const UPSERT = MDBX_UPSERT;
264 /// For insertion: Don't write if the key already exists.
265 const NO_OVERWRITE = MDBX_NOOVERWRITE;
266 /// Has effect only for MDBX_DUPSORT tables. For upsertion: don't write
267 /// if the key-value pair already exist.
268 const NO_DUP_DATA = MDBX_NODUPDATA;
269 /// For upsertion: overwrite the current key/data pair. MDBX allows
270 /// this flag for mdbx_put() for explicit overwrite/update without
271 /// insertion. For deletion: remove only single entry at the current
272 /// cursor position.
273 const CURRENT = MDBX_CURRENT;
274 /// Has effect only for MDBX_DUPSORT tables. For deletion: remove all
275 /// multi-values (aka duplicates) for given key. For upsertion: replace
276 /// all multi-values for given key with a new one.
277 const ALLDUPS = MDBX_ALLDUPS;
278 /// For upsertion: Just reserve space for data, don't copy it. Return a
279 /// pointer to the reserved space.
280 const RESERVE = MDBX_RESERVE;
281 /// Data is being appended. Don't split full pages, continue on a new
282 /// instead.
283 const APPEND = MDBX_APPEND;
284 /// Has effect only for MDBX_DUPSORT tables. Duplicate data is being
285 /// appended. Don't split full pages, continue on a new instead.
286 const APPEND_DUP = MDBX_APPENDDUP;
287 /// Only for MDBX_DUPFIXED. Store multiple data items in one call.
288 const MULTIPLE = MDBX_MULTIPLE;
289 }
290}