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}