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}