reth_libmdbx/flags.rs
1use bitflags::bitflags;
2use ffi::*;
3
4/// MDBX sync mode
5#[derive(Clone, Copy, Debug, Default)]
6pub enum SyncMode {
7 /// Default robust and durable sync mode.
8 /// Metadata is written and flushed to disk after a data is written and flushed, which
9 /// guarantees the integrity of the database in the event of a crash at any time.
10 #[default]
11 Durable,
12
13 /// Don't sync the meta-page after commit.
14 ///
15 /// Flush system buffers to disk only once per transaction commit, omit the metadata flush.
16 /// Defer that until the system flushes files to disk, or next non-read-only commit or
17 /// [`Environment::sync()`](crate::Environment::sync). Depending on the platform and
18 /// hardware, with [`SyncMode::NoMetaSync`] you may get a doubling of write performance.
19 ///
20 /// This trade-off maintains database integrity, but a system crash may undo the last committed
21 /// transaction. I.e. it preserves the ACPI (atomicity, consistency, isolation) but not D
22 /// (durability) database property.
23 NoMetaSync,
24
25 /// Don't sync anything but keep previous steady commits.
26 ///
27 /// [`SyncMode::UtterlyNoSync`] the [`SyncMode::SafeNoSync`] flag disable similarly flush
28 /// system buffers to disk when committing a transaction. But there is a huge difference in
29 /// how are recycled the MVCC snapshots corresponding to previous "steady" transactions
30 /// (see below).
31 ///
32 /// With [`crate::EnvironmentKind::WriteMap`] the [`SyncMode::SafeNoSync`] instructs MDBX to
33 /// use asynchronous mmap-flushes to disk. Asynchronous mmap-flushes means that actually
34 /// all writes will scheduled and performed by operation system on it own manner, i.e.
35 /// unordered. MDBX itself just notify operating system that it would be nice to write data
36 /// to disk, but no more.
37 ///
38 /// Depending on the platform and hardware, with [`SyncMode::SafeNoSync`] you may get a
39 /// multiple increase of write performance, even 10 times or more.
40 ///
41 /// In contrast to [`SyncMode::UtterlyNoSync`] mode, with [`SyncMode::SafeNoSync`] flag MDBX
42 /// will keeps untouched pages within B-tree of the last transaction "steady" which was
43 /// synced to disk completely. This has big implications for both data durability and
44 /// (unfortunately) performance:
45 /// - A system crash can't corrupt the database, but you will lose the last transactions;
46 /// because MDBX will rollback to last steady commit since it kept explicitly.
47 /// - The last steady transaction makes an effect similar to "long-lived" read transaction
48 /// since prevents reuse of pages freed by newer write transactions, thus the any data
49 /// changes will be placed in newly allocated pages.
50 /// - To avoid rapid database growth, the system will sync data and issue a steady commit-point
51 /// to resume reuse pages, each time there is insufficient space and before increasing the
52 /// size of the file on disk.
53 ///
54 /// In other words, with
55 /// [`SyncMode::SafeNoSync`] flag MDBX protects you from the whole database corruption, at the
56 /// cost increasing database size and/or number of disk IOPs. So, [`SyncMode::SafeNoSync`]
57 /// flag could be used with [`Environment::sync()`](crate::Environment::sync) as alternatively
58 /// for batch committing or nested transaction (in some cases).
59 ///
60 /// The number and volume of disk IOPs with [`SyncMode::SafeNoSync`] flag will exactly the
61 /// as without any no-sync flags. However, you should expect a larger process's work set
62 /// and significantly worse a locality of reference, due to the more intensive allocation
63 /// of previously unused pages and increase the size of the database.
64 SafeNoSync,
65
66 /// Don't sync anything and wipe previous steady commits.
67 ///
68 /// Don't flush system buffers to disk when committing a transaction.
69 /// This optimization means a system crash can corrupt the database, if buffers are not yet
70 /// flushed to disk. Depending on the platform and hardware, with [`SyncMode::UtterlyNoSync`]
71 /// you may get a multiple increase of write performance, even 100 times or more.
72 ///
73 /// If the filesystem preserves write order (which is rare and never provided unless explicitly
74 /// noted) and the [`WriteMap`](crate::EnvironmentKind::WriteMap) and
75 /// [`EnvironmentFlags::liforeclaim`] flags are not used, then a system crash can't corrupt
76 /// the database, but you can lose the last transactions, if at least one buffer is not yet
77 /// flushed to disk. The risk is governed by how often the system flushes dirty buffers to
78 /// disk and how often [`Environment::sync()`](crate::Environment::sync) is called. So,
79 /// transactions exhibit ACPI (atomicity, consistency, isolation) properties and only lose D
80 /// (durability). I.e. database integrity is maintained, but a system crash may undo the
81 /// final transactions.
82 ///
83 /// Otherwise, if the filesystem not preserves write order (which is typically) or
84 /// [`WriteMap`](crate::EnvironmentKind::WriteMap) or [`EnvironmentFlags::liforeclaim`] flags
85 /// are used, you should expect the corrupted database after a system crash.
86 ///
87 /// So, most important thing about [`SyncMode::UtterlyNoSync`]:
88 /// - A system crash immediately after commit the write transaction high likely lead to
89 /// database corruption.
90 /// - Successful completion of [`Environment::sync(force=true`)](crate::Environment::sync)
91 /// after one or more committed transactions guarantees consistency and durability.
92 /// - BUT by committing two or more transactions you back database into a weak state, in which
93 /// a system crash may lead to database corruption! In case single transaction after
94 /// [`Environment::sync()`](crate::Environment::sync), you may lose transaction itself, but
95 /// not a whole database.
96 ///
97 /// Nevertheless, [`SyncMode::UtterlyNoSync`] provides "weak" durability in
98 /// case of an application crash (but no durability on system failure), and therefore may
99 /// be very useful in scenarios where data durability is not required over a system failure
100 /// (e.g for short-lived data), or if you can take such risk.
101 UtterlyNoSync,
102}
103
104#[derive(Clone, Copy, Debug)]
105pub enum Mode {
106 ReadOnly,
107 ReadWrite { sync_mode: SyncMode },
108}
109
110impl Default for Mode {
111 fn default() -> Self {
112 Self::ReadWrite { sync_mode: SyncMode::default() }
113 }
114}
115
116impl From<Mode> for EnvironmentFlags {
117 fn from(mode: Mode) -> Self {
118 Self { mode, ..Default::default() }
119 }
120}
121
122#[derive(Clone, Copy, Debug, Default)]
123pub struct EnvironmentFlags {
124 pub no_sub_dir: bool,
125 pub exclusive: bool,
126 /// Flag is intended to open an existing sub-database which was created with unknown flags
127 /// In such cases, instead of returning the `MDBX_INCOMPATIBLE` error, the sub-database will be
128 /// opened with flags which it was created, and then an application could determine the actual
129 /// flags.
130 pub accede: bool,
131 pub mode: Mode,
132 pub no_rdahead: bool,
133 pub no_meminit: bool,
134 pub coalesce: bool,
135 pub liforeclaim: bool,
136}
137
138impl EnvironmentFlags {
139 /// Configures the mdbx flags to use when opening the environment.
140 pub(crate) const fn make_flags(&self) -> ffi::MDBX_env_flags_t {
141 let mut flags = 0;
142
143 if self.no_sub_dir {
144 flags |= ffi::MDBX_NOSUBDIR;
145 }
146
147 if self.exclusive {
148 flags |= ffi::MDBX_EXCLUSIVE;
149 }
150
151 if self.accede {
152 flags |= ffi::MDBX_ACCEDE;
153 }
154
155 match self.mode {
156 Mode::ReadOnly => {
157 flags |= ffi::MDBX_RDONLY;
158 }
159 Mode::ReadWrite { sync_mode } => {
160 flags |= match sync_mode {
161 SyncMode::Durable => ffi::MDBX_SYNC_DURABLE,
162 SyncMode::NoMetaSync => ffi::MDBX_NOMETASYNC,
163 SyncMode::SafeNoSync => ffi::MDBX_SAFE_NOSYNC,
164 SyncMode::UtterlyNoSync => ffi::MDBX_UTTERLY_NOSYNC,
165 };
166 }
167 }
168
169 if self.no_rdahead {
170 flags |= ffi::MDBX_NORDAHEAD;
171 }
172
173 if self.no_meminit {
174 flags |= ffi::MDBX_NOMEMINIT;
175 }
176
177 if self.coalesce {
178 flags |= ffi::MDBX_COALESCE;
179 }
180
181 if self.liforeclaim {
182 flags |= ffi::MDBX_LIFORECLAIM;
183 }
184
185 flags |= ffi::MDBX_NOTLS;
186
187 flags
188 }
189}
190
191bitflags! {
192 #[doc="Database options."]
193 #[derive(Default)]
194 pub struct DatabaseFlags: MDBX_env_flags_t {
195 const REVERSE_KEY = MDBX_REVERSEKEY;
196 const DUP_SORT = MDBX_DUPSORT;
197 const INTEGER_KEY = MDBX_INTEGERKEY;
198 const DUP_FIXED = MDBX_DUPFIXED;
199 const INTEGER_DUP = MDBX_INTEGERDUP;
200 const REVERSE_DUP = MDBX_REVERSEDUP;
201 const CREATE = MDBX_CREATE;
202 const ACCEDE = MDBX_DB_ACCEDE;
203 }
204}
205
206bitflags! {
207 #[doc="Write options."]
208 #[derive(Default)]
209 pub struct WriteFlags: MDBX_env_flags_t {
210 const UPSERT = MDBX_UPSERT;
211 const NO_OVERWRITE = MDBX_NOOVERWRITE;
212 const NO_DUP_DATA = MDBX_NODUPDATA;
213 const CURRENT = MDBX_CURRENT;
214 const ALLDUPS = MDBX_ALLDUPS;
215 const RESERVE = MDBX_RESERVE;
216 const APPEND = MDBX_APPEND;
217 const APPEND_DUP = MDBX_APPENDDUP;
218 const MULTIPLE = MDBX_MULTIPLE;
219 }
220}