cuprate_blockchain/config.rs
1//! Database configuration.
2//!
3//! This module contains the main [`Config`]uration struct
4//! for the database [`Env`](cuprate_database::Env)ironment,
5//! and blockchain-specific configuration.
6//!
7//! It also contains types related to configuration settings.
8//!
9//! The main constructor is the [`ConfigBuilder`].
10//!
11//! These configurations are processed at runtime, meaning
12//! the `Env` can/will dynamically adjust its behavior based
13//! on these values.
14//!
15//! # Example
16//! ```rust
17//! use cuprate_blockchain::{
18//! cuprate_database::{Env, config::SyncMode},
19//! config::{ConfigBuilder, ReaderThreads},
20//! };
21//!
22//! # fn main() -> Result<(), Box<dyn std::error::Error>> {
23//! let tmp_dir = tempfile::tempdir()?;
24//! let db_dir = tmp_dir.path().to_owned();
25//!
26//! let config = ConfigBuilder::new()
27//! // Use a custom database directory.
28//! .data_directory(db_dir.into())
29//! // Use as many reader threads as possible (when using `service`).
30//! .reader_threads(ReaderThreads::OnePerThread)
31//! // Use the fastest sync mode.
32//! .sync_mode(SyncMode::Fast)
33//! // Build into `Config`
34//! .build();
35//!
36//! // Start a database `service` using this configuration.
37//! let (_, _, env) = cuprate_blockchain::service::init(config.clone())?;
38//! // It's using the config we provided.
39//! assert_eq!(env.config(), &config.db_config);
40//! # Ok(()) }
41//! ```
42
43//---------------------------------------------------------------------------------------------------- Import
44use std::{borrow::Cow, path::PathBuf};
45
46#[cfg(feature = "serde")]
47use serde::{Deserialize, Serialize};
48
49use cuprate_database::{config::SyncMode, resize::ResizeAlgorithm};
50use cuprate_helper::{
51 fs::{blockchain_path, CUPRATE_DATA_DIR},
52 network::Network,
53};
54
55// re-exports
56pub use cuprate_database_service::ReaderThreads;
57
58//---------------------------------------------------------------------------------------------------- ConfigBuilder
59/// Builder for [`Config`].
60///
61// SOMEDAY: there's are many more options to add in the future.
62#[derive(Debug, Clone, PartialEq, PartialOrd)]
63#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
64pub struct ConfigBuilder {
65 network: Network,
66
67 data_dir: Option<PathBuf>,
68
69 /// [`Config::cuprate_database_config`].
70 db_config: cuprate_database::config::ConfigBuilder,
71
72 /// [`Config::reader_threads`].
73 reader_threads: Option<ReaderThreads>,
74}
75
76impl ConfigBuilder {
77 /// Create a new [`ConfigBuilder`].
78 ///
79 /// [`ConfigBuilder::build`] can be called immediately
80 /// after this function to use default values.
81 pub fn new() -> Self {
82 Self {
83 network: Network::default(),
84 data_dir: None,
85 db_config: cuprate_database::config::ConfigBuilder::new(Cow::Owned(blockchain_path(
86 &CUPRATE_DATA_DIR,
87 Network::Mainnet,
88 ))),
89 reader_threads: None,
90 }
91 }
92
93 /// Build into a [`Config`].
94 ///
95 /// # Default values
96 /// If [`ConfigBuilder::data_directory`] was not called,
97 /// [`blockchain_path`] with [`CUPRATE_DATA_DIR`] [`Network::Mainnet`] will be used.
98 ///
99 /// For all other values, [`Default::default`] is used.
100 pub fn build(self) -> Config {
101 // INVARIANT: all PATH safety checks are done
102 // in `helper::fs`. No need to do them here.
103 let data_dir = self
104 .data_dir
105 .unwrap_or_else(|| CUPRATE_DATA_DIR.to_path_buf());
106
107 let reader_threads = self.reader_threads.unwrap_or_default();
108 let db_config = self
109 .db_config
110 .db_directory(Cow::Owned(blockchain_path(&data_dir, self.network)))
111 .reader_threads(reader_threads.as_threads())
112 .build();
113
114 Config {
115 db_config,
116 reader_threads,
117 }
118 }
119
120 /// Change the network this blockchain database is for.
121 #[must_use]
122 pub const fn network(mut self, network: Network) -> Self {
123 self.network = network;
124 self
125 }
126
127 /// Set a custom database directory (and file) [`PathBuf`].
128 #[must_use]
129 pub fn data_directory(mut self, db_directory: PathBuf) -> Self {
130 self.data_dir = Some(db_directory);
131 self
132 }
133
134 /// Calls [`cuprate_database::config::ConfigBuilder::sync_mode`].
135 #[must_use]
136 pub fn sync_mode(mut self, sync_mode: SyncMode) -> Self {
137 self.db_config = self.db_config.sync_mode(sync_mode);
138 self
139 }
140
141 /// Calls [`cuprate_database::config::ConfigBuilder::resize_algorithm`].
142 #[must_use]
143 pub fn resize_algorithm(mut self, resize_algorithm: ResizeAlgorithm) -> Self {
144 self.db_config = self.db_config.resize_algorithm(resize_algorithm);
145 self
146 }
147
148 /// Set a custom [`ReaderThreads`].
149 #[must_use]
150 pub const fn reader_threads(mut self, reader_threads: ReaderThreads) -> Self {
151 self.reader_threads = Some(reader_threads);
152 self
153 }
154
155 /// Tune the [`ConfigBuilder`] for the highest performing,
156 /// but also most resource-intensive & maybe risky settings.
157 ///
158 /// Good default for testing, and resource-available machines.
159 #[must_use]
160 pub fn fast(mut self) -> Self {
161 self.db_config = self.db_config.fast();
162
163 self.reader_threads = Some(ReaderThreads::OnePerThread);
164 self
165 }
166
167 /// Tune the [`ConfigBuilder`] for the lowest performing,
168 /// but also least resource-intensive settings.
169 ///
170 /// Good default for resource-limited machines, e.g. a cheap VPS.
171 #[must_use]
172 pub fn low_power(mut self) -> Self {
173 self.db_config = self.db_config.low_power();
174
175 self.reader_threads = Some(ReaderThreads::One);
176 self
177 }
178}
179
180impl Default for ConfigBuilder {
181 fn default() -> Self {
182 Self {
183 network: Network::default(),
184 data_dir: Some(CUPRATE_DATA_DIR.to_path_buf()),
185 db_config: cuprate_database::config::ConfigBuilder::new(Cow::Owned(blockchain_path(
186 &CUPRATE_DATA_DIR,
187 Network::default(),
188 ))),
189 reader_threads: Some(ReaderThreads::default()),
190 }
191 }
192}
193
194//---------------------------------------------------------------------------------------------------- Config
195/// `cuprate_blockchain` configuration.
196///
197/// This is a configuration built on-top of [`cuprate_database::config::Config`].
198///
199/// It contains configuration specific to this crate, plus the database config.
200///
201/// For construction, either use [`ConfigBuilder`] or [`Config::default`].
202#[derive(Debug, Clone, PartialEq, PartialOrd)]
203#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
204pub struct Config {
205 /// The database configuration.
206 pub db_config: cuprate_database::config::Config,
207
208 /// Database reader thread count.
209 pub reader_threads: ReaderThreads,
210}
211
212impl Config {
213 /// Create a new [`Config`] with sane default settings.
214 ///
215 /// The [`cuprate_database::config::Config::db_directory`]
216 /// will be set to [`blockchain_path`] with [`CUPRATE_DATA_DIR`] [`Network::Mainnet`].
217 ///
218 /// All other values will be [`Default::default`].
219 ///
220 /// Same as [`Config::default`].
221 ///
222 /// ```rust
223 /// use cuprate_database::{
224 /// config::SyncMode,
225 /// resize::ResizeAlgorithm,
226 /// DATABASE_DATA_FILENAME,
227 /// };
228 /// use cuprate_helper::{fs::*, network::Network};
229 ///
230 /// use cuprate_blockchain::config::*;
231 ///
232 /// let config = Config::new();
233 ///
234 /// assert_eq!(config.db_config.db_directory().as_ref(), blockchain_path(&CUPRATE_DATA_DIR, Network::Mainnet).as_path());
235 /// assert!(config.db_config.db_file().starts_with(&*CUPRATE_DATA_DIR));
236 /// assert!(config.db_config.db_file().ends_with(DATABASE_DATA_FILENAME));
237 /// assert_eq!(config.db_config.sync_mode, SyncMode::default());
238 /// assert_eq!(config.db_config.resize_algorithm, ResizeAlgorithm::default());
239 /// assert_eq!(config.reader_threads, ReaderThreads::default());
240 /// ```
241 pub fn new() -> Self {
242 ConfigBuilder::default().build()
243 }
244}
245
246impl Default for Config {
247 /// Same as [`Config::new`].
248 ///
249 /// ```rust
250 /// # use cuprate_blockchain::config::*;
251 /// assert_eq!(Config::default(), Config::new());
252 /// ```
253 fn default() -> Self {
254 Self::new()
255 }
256}