Skip to main content

prolly/prolly/
config.rs

1//! Configuration for Prolly Trees
2
3use serde::{Deserialize, Serialize};
4
5use super::encoding::Encoding;
6use super::format::{BoundaryRule, ChunkingSpec, NodeLayoutSpec, TreeFormat};
7
8/// Runtime-only tuning that never participates in persisted tree identity.
9#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
10pub struct RuntimeConfig {
11    /// Optional maximum number of decoded nodes retained in each manager cache.
12    pub node_cache_max_nodes: Option<usize>,
13    /// Optional maximum serialized-node bytes retained in each manager cache.
14    pub node_cache_max_bytes: Option<usize>,
15    /// Preferred maximum number of concurrent ordered reads.
16    pub read_parallelism: usize,
17}
18
19impl Default for RuntimeConfig {
20    fn default() -> Self {
21        Self {
22            node_cache_max_nodes: None,
23            node_cache_max_bytes: None,
24            read_parallelism: 1,
25        }
26    }
27}
28
29/// Tree configuration
30#[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
31pub struct Config {
32    /// Persisted settings that determine shape and content IDs.
33    pub format: TreeFormat,
34    /// Cache and I/O tuning local to this manager.
35    pub runtime: RuntimeConfig,
36}
37
38impl Config {
39    /// Create a new ConfigBuilder
40    pub fn builder() -> ConfigBuilder {
41        ConfigBuilder::default()
42    }
43
44    /// Minimum configured chunk measure before probabilistic cuts.
45    pub fn min_chunk_size(&self) -> usize {
46        self.format.chunking.min as usize
47    }
48
49    /// Maximum configured chunk measure.
50    pub fn max_chunk_size(&self) -> usize {
51        self.format.chunking.max as usize
52    }
53
54    /// Hash-threshold factor, or the target measure for non-threshold policies.
55    pub fn chunking_factor(&self) -> u32 {
56        match self.format.chunking.rule {
57            BoundaryRule::HashThreshold { factor } => factor,
58            _ => self.format.chunking.target.min(u64::from(u32::MAX)) as u32,
59        }
60    }
61
62    /// Boundary hash seed.
63    pub fn hash_seed(&self) -> u64 {
64        self.format.chunking.hash_seed
65    }
66
67    /// Value encoding descriptor.
68    pub fn encoding(&self) -> &Encoding {
69        &self.format.value_encoding
70    }
71}
72
73/// Builder for Config
74#[derive(Default)]
75pub struct ConfigBuilder {
76    config: Config,
77}
78
79impl ConfigBuilder {
80    /// Set the minimum chunk size
81    pub fn min_chunk_size(mut self, size: usize) -> Self {
82        self.config.format.chunking.min = size as u64;
83        self.config.format.chunking.target = self.config.format.chunking.target.max(size as u64);
84        self.config.format.chunking.max = self.config.format.chunking.max.max(size as u64);
85        self
86    }
87
88    /// Set the maximum chunk size
89    pub fn max_chunk_size(mut self, size: usize) -> Self {
90        self.config.format.chunking.max = size as u64;
91        self.config.format.chunking.target = self.config.format.chunking.target.min(size as u64);
92        self.config.format.chunking.min = self.config.format.chunking.min.min(size as u64);
93        self
94    }
95
96    /// Set the chunking factor
97    pub fn chunking_factor(mut self, factor: u32) -> Self {
98        self.config.format.chunking.rule = BoundaryRule::HashThreshold { factor };
99        self
100    }
101
102    /// Set the hash seed
103    pub fn hash_seed(mut self, seed: u64) -> Self {
104        self.config.format.chunking.hash_seed = seed;
105        self
106    }
107
108    /// Set the encoding type
109    pub fn encoding(mut self, encoding: Encoding) -> Self {
110        self.config.format.value_encoding = encoding;
111        self
112    }
113
114    /// Select the complete persisted chunking policy.
115    pub fn chunking(mut self, chunking: ChunkingSpec) -> Self {
116        self.config.format.chunking = chunking;
117        self
118    }
119
120    /// Select the physical node layout.
121    pub fn node_layout(mut self, layout: NodeLayoutSpec) -> Self {
122        self.config.format.node_layout = layout;
123        self
124    }
125
126    /// Replace the complete persisted tree format.
127    pub fn format(mut self, format: TreeFormat) -> Self {
128        self.config.format = format;
129        self
130    }
131
132    /// Set the maximum number of decoded nodes retained in each manager cache.
133    ///
134    /// Use `0` to disable node caching. Omit this setting to keep the cache
135    /// unbounded.
136    pub fn node_cache_max_nodes(mut self, max_nodes: usize) -> Self {
137        self.config.runtime.node_cache_max_nodes = Some(max_nodes);
138        self
139    }
140
141    /// Set the maximum serialized-node bytes retained in each manager cache.
142    ///
143    /// Use `0` to disable node caching. Omit this setting to keep the cache
144    /// unbounded by bytes.
145    pub fn node_cache_max_bytes(mut self, max_bytes: usize) -> Self {
146        self.config.runtime.node_cache_max_bytes = Some(max_bytes);
147        self
148    }
149
150    /// Keep each manager's decoded-node cache unbounded.
151    pub fn unbounded_node_cache(mut self) -> Self {
152        self.config.runtime.node_cache_max_nodes = None;
153        self.config.runtime.node_cache_max_bytes = None;
154        self
155    }
156
157    /// Set preferred ordered-read parallelism.
158    pub fn read_parallelism(mut self, parallelism: usize) -> Self {
159        self.config.runtime.read_parallelism = parallelism.max(1);
160        self
161    }
162
163    /// Build the Config
164    pub fn build(self) -> Config {
165        self.config
166    }
167}
168
169#[cfg(test)]
170mod tests {
171    use super::*;
172
173    #[test]
174    fn test_config_default() {
175        let config = Config::default();
176        assert_eq!(config, Config::default());
177        assert_eq!(config.runtime, RuntimeConfig::default());
178    }
179
180    #[test]
181    fn test_config_builder() {
182        let config = Config::builder()
183            .min_chunk_size(2)
184            .max_chunk_size(100)
185            .chunking_factor(64)
186            .hash_seed(42)
187            .encoding(Encoding::Cbor)
188            .node_cache_max_nodes(128)
189            .node_cache_max_bytes(1_048_576)
190            .build();
191
192        assert_eq!(config.min_chunk_size(), 2);
193        assert_eq!(config.max_chunk_size(), 100);
194        assert_eq!(config.chunking_factor(), 64);
195        assert_eq!(config.hash_seed(), 42);
196        assert_eq!(config.encoding(), &Encoding::Cbor);
197        assert_eq!(config.runtime.node_cache_max_nodes, Some(128));
198        assert_eq!(config.runtime.node_cache_max_bytes, Some(1_048_576));
199
200        let config = Config::builder()
201            .node_cache_max_nodes(128)
202            .node_cache_max_bytes(1_048_576)
203            .unbounded_node_cache()
204            .build();
205        assert_eq!(config.runtime.node_cache_max_nodes, None);
206        assert_eq!(config.runtime.node_cache_max_bytes, None);
207    }
208}