cuprate_database/config/config.rs
1//! The main [`Config`] struct, holding all configurable values.
2
3//---------------------------------------------------------------------------------------------------- Import
4use std::{borrow::Cow, num::NonZeroUsize, path::Path};
5
6#[cfg(feature = "serde")]
7use serde::{Deserialize, Serialize};
8
9use crate::{config::SyncMode, constants::DATABASE_DATA_FILENAME, resize::ResizeAlgorithm};
10
11//---------------------------------------------------------------------------------------------------- Constants
12/// Default value for [`Config::reader_threads`].
13///
14/// ```rust
15/// use cuprate_database::config::*;
16/// assert_eq!(READER_THREADS_DEFAULT.get(), 126);
17/// ```
18pub const READER_THREADS_DEFAULT: NonZeroUsize = NonZeroUsize::new(126).unwrap();
19
20//---------------------------------------------------------------------------------------------------- ConfigBuilder
21/// Builder for [`Config`].
22///
23// SOMEDAY: there's are many more options to add in the future.
24#[derive(Debug, Clone, PartialEq, PartialOrd)]
25#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
26pub struct ConfigBuilder {
27 /// [`Config::db_directory`].
28 db_directory: Cow<'static, Path>,
29
30 /// [`Config::sync_mode`].
31 sync_mode: Option<SyncMode>,
32
33 /// [`Config::reader_threads`].
34 reader_threads: Option<NonZeroUsize>,
35
36 /// [`Config::resize_algorithm`].
37 resize_algorithm: Option<ResizeAlgorithm>,
38}
39
40impl ConfigBuilder {
41 /// Create a new [`ConfigBuilder`].
42 ///
43 /// [`ConfigBuilder::build`] can be called immediately
44 /// after this function to use default values.
45 pub const fn new(db_directory: Cow<'static, Path>) -> Self {
46 Self {
47 db_directory,
48 sync_mode: None,
49 reader_threads: Some(READER_THREADS_DEFAULT),
50 resize_algorithm: None,
51 }
52 }
53
54 /// Build into a [`Config`].
55 ///
56 /// # Default values
57 /// - [`READER_THREADS_DEFAULT`] is used for [`Config::reader_threads`]
58 /// - [`Default::default`] is used for all other values (except the `db_directory`)
59 pub fn build(self) -> Config {
60 // Add the database filename to the directory.
61 let db_file = {
62 let mut db_file = self.db_directory.to_path_buf();
63 db_file.push(DATABASE_DATA_FILENAME);
64 Cow::Owned(db_file)
65 };
66
67 Config {
68 db_directory: self.db_directory,
69 db_file,
70 sync_mode: self.sync_mode.unwrap_or_default(),
71 reader_threads: self.reader_threads.unwrap_or(READER_THREADS_DEFAULT),
72 resize_algorithm: self.resize_algorithm.unwrap_or_default(),
73 }
74 }
75
76 /// Set a custom database directory (and file) [`Path`].
77 #[must_use]
78 pub fn db_directory(mut self, db_directory: Cow<'static, Path>) -> Self {
79 self.db_directory = db_directory;
80 self
81 }
82
83 /// Tune the [`ConfigBuilder`] for the highest performing,
84 /// but also most resource-intensive & maybe risky settings.
85 ///
86 /// Good default for testing, and resource-available machines.
87 #[must_use]
88 pub fn fast(mut self) -> Self {
89 self.sync_mode = Some(SyncMode::Fast);
90 self.resize_algorithm = Some(ResizeAlgorithm::default());
91 self
92 }
93
94 /// Tune the [`ConfigBuilder`] for the lowest performing,
95 /// but also least resource-intensive settings.
96 ///
97 /// Good default for resource-limited machines, e.g. a cheap VPS.
98 #[must_use]
99 pub fn low_power(mut self) -> Self {
100 self.sync_mode = Some(SyncMode::default());
101 self.resize_algorithm = Some(ResizeAlgorithm::default());
102 self
103 }
104
105 /// Set a custom [`SyncMode`].
106 #[must_use]
107 pub const fn sync_mode(mut self, sync_mode: SyncMode) -> Self {
108 self.sync_mode = Some(sync_mode);
109 self
110 }
111
112 /// Set a custom [`Config::reader_threads`].
113 #[must_use]
114 pub const fn reader_threads(mut self, reader_threads: NonZeroUsize) -> Self {
115 self.reader_threads = Some(reader_threads);
116 self
117 }
118
119 /// Set a custom [`ResizeAlgorithm`].
120 #[must_use]
121 pub const fn resize_algorithm(mut self, resize_algorithm: ResizeAlgorithm) -> Self {
122 self.resize_algorithm = Some(resize_algorithm);
123 self
124 }
125}
126
127//---------------------------------------------------------------------------------------------------- Config
128/// Database [`Env`](crate::Env) configuration.
129///
130/// This is the struct passed to [`Env::open`](crate::Env::open) that
131/// allows the database to be configured in various ways.
132///
133/// For construction, use [`ConfigBuilder`].
134///
135// SOMEDAY: there's are many more options to add in the future.
136#[derive(Debug, Clone, PartialEq, PartialOrd)]
137#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
138pub struct Config {
139 //------------------------ Database PATHs
140 // These are private since we don't want
141 // users messing with them after construction.
142 /// The directory used to store all database files.
143 ///
144 // SOMEDAY: we should also support `/etc/cuprated.conf`.
145 // This could be represented with an `enum DbPath { Default, Custom, Etc, }`
146 pub(crate) db_directory: Cow<'static, Path>,
147 /// The actual database data file.
148 ///
149 /// This is private, and created from the above `db_directory`.
150 pub(crate) db_file: Cow<'static, Path>,
151
152 /// Disk synchronization mode.
153 pub sync_mode: SyncMode,
154
155 /// Database reader thread count.
156 ///
157 /// Set the number of slots in the reader table.
158 ///
159 /// This is only used in LMDB, see
160 /// [here](https://github.com/LMDB/lmdb/blob/b8e54b4c31378932b69f1298972de54a565185b1/libraries/liblmdb/mdb.c#L794-L799).
161 ///
162 /// By default, this value is [`READER_THREADS_DEFAULT`].
163 pub reader_threads: NonZeroUsize,
164
165 /// Database memory map resizing algorithm.
166 ///
167 /// This is used as the default fallback, but
168 /// custom algorithms can be used as well with
169 /// [`Env::resize_map`](crate::Env::resize_map).
170 pub resize_algorithm: ResizeAlgorithm,
171}
172
173impl Config {
174 /// Create a new [`Config`] with sane default settings.
175 ///
176 /// The [`Config::db_directory`] must be passed.
177 ///
178 /// All other values will be [`Default::default`].
179 ///
180 /// ```rust
181 /// use cuprate_database::{config::*, resize::*, DATABASE_DATA_FILENAME};
182 ///
183 /// let tmp_dir = tempfile::tempdir().unwrap();
184 /// let db_directory = tmp_dir.path().to_owned();
185 /// let config = Config::new(db_directory.clone().into());
186 ///
187 /// assert_eq!(*config.db_directory(), db_directory);
188 /// assert!(config.db_file().starts_with(db_directory));
189 /// assert!(config.db_file().ends_with(DATABASE_DATA_FILENAME));
190 /// assert_eq!(config.sync_mode, SyncMode::default());
191 /// assert_eq!(config.reader_threads, READER_THREADS_DEFAULT);
192 /// assert_eq!(config.resize_algorithm, ResizeAlgorithm::default());
193 /// ```
194 pub fn new(db_directory: Cow<'static, Path>) -> Self {
195 ConfigBuilder::new(db_directory).build()
196 }
197
198 /// Return the absolute [`Path`] to the database directory.
199 pub const fn db_directory(&self) -> &Cow<'_, Path> {
200 &self.db_directory
201 }
202
203 /// Return the absolute [`Path`] to the database data file.
204 pub const fn db_file(&self) -> &Cow<'_, Path> {
205 &self.db_file
206 }
207}