/home/gh-runner/action-runner3/_work/feoxdb/feoxdb/src/core/store/builder.rs
Line | Count | Source |
1 | | use std::time::Duration; |
2 | | |
3 | | use crate::constants::*; |
4 | | use crate::core::ttl_sweep::TtlConfig; |
5 | | use crate::error::Result; |
6 | | |
7 | | use super::FeoxStore; |
8 | | |
9 | | /// Configuration options for FeoxStore. |
10 | | /// |
11 | | /// Use `StoreBuilder` for a more ergonomic way to configure the store. |
12 | | pub struct StoreConfig { |
13 | | pub hash_bits: u32, |
14 | | pub memory_only: bool, |
15 | | pub enable_caching: bool, |
16 | | pub device_path: Option<String>, |
17 | | pub file_size: Option<u64>, |
18 | | pub max_memory: Option<usize>, |
19 | | pub enable_ttl: bool, |
20 | | pub ttl_config: Option<TtlConfig>, |
21 | | } |
22 | | |
23 | | /// Builder for creating FeoxStore with custom configuration. |
24 | | /// |
25 | | /// Provides a fluent interface for configuring store parameters. |
26 | | /// |
27 | | /// # Example |
28 | | /// |
29 | | /// ```rust |
30 | | /// use feoxdb::FeoxStore; |
31 | | /// |
32 | | /// # fn main() -> feoxdb::Result<()> { |
33 | | /// let store = FeoxStore::builder() |
34 | | /// .max_memory(1_000_000_000) |
35 | | /// .hash_bits(20) |
36 | | /// .enable_ttl(true) |
37 | | /// .build()?; |
38 | | /// # Ok(()) |
39 | | /// # } |
40 | | /// ``` |
41 | | pub struct StoreBuilder { |
42 | | hash_bits: u32, |
43 | | device_path: Option<String>, |
44 | | file_size: Option<u64>, |
45 | | max_memory: Option<usize>, |
46 | | enable_caching: Option<bool>, |
47 | | enable_ttl: bool, |
48 | | ttl_config: Option<TtlConfig>, |
49 | | } |
50 | | |
51 | | impl StoreBuilder { |
52 | 15 | pub fn new() -> Self { |
53 | 15 | Self { |
54 | 15 | hash_bits: DEFAULT_HASH_BITS, |
55 | 15 | device_path: None, |
56 | 15 | file_size: None, |
57 | 15 | max_memory: Some(DEFAULT_MAX_MEMORY), |
58 | 15 | enable_caching: None, // Disable caching for memory-only mode |
59 | 15 | enable_ttl: false, |
60 | 15 | ttl_config: None, |
61 | 15 | } |
62 | 15 | } |
63 | | |
64 | | /// Set the device path for persistent storage. |
65 | | /// |
66 | | /// When set, data will be persisted to disk asynchronously. |
67 | | /// If not set, the store operates in memory-only mode. |
68 | 0 | pub fn device_path(mut self, path: impl Into<String>) -> Self { |
69 | 0 | self.device_path = Some(path.into()); |
70 | 0 | self |
71 | 0 | } |
72 | | |
73 | | /// Set the initial file size for new persistent stores (in bytes). |
74 | | /// |
75 | | /// When creating a new persistent store file, it will be pre-allocated |
76 | | /// to this size for better performance. If not set, defaults to 1GB. |
77 | | /// This option is ignored for existing files. |
78 | | /// |
79 | | /// # Example |
80 | | /// |
81 | | /// ```no_run |
82 | | /// use feoxdb::FeoxStore; |
83 | | /// |
84 | | /// # fn main() -> feoxdb::Result<()> { |
85 | | /// let store = FeoxStore::builder() |
86 | | /// .device_path("/path/to/data.feox") |
87 | | /// .file_size(10 * 1024 * 1024 * 1024) // 10GB |
88 | | /// .build()?; |
89 | | /// # Ok(()) |
90 | | /// # } |
91 | | /// ``` |
92 | 0 | pub fn file_size(mut self, size: u64) -> Self { |
93 | 0 | self.file_size = Some(size); |
94 | 0 | self |
95 | 0 | } |
96 | | |
97 | | /// Set the maximum memory limit (in bytes). |
98 | | /// |
99 | | /// The store will start evicting entries when this limit is approached. |
100 | | /// Default: 1GB |
101 | 2 | pub fn max_memory(mut self, limit: usize) -> Self { |
102 | 2 | self.max_memory = Some(limit); |
103 | 2 | self |
104 | 2 | } |
105 | | |
106 | | /// Remove memory limit. |
107 | | /// |
108 | | /// Use with caution as the store can grow unbounded. |
109 | 0 | pub fn no_memory_limit(mut self) -> Self { |
110 | 0 | self.max_memory = None; |
111 | 0 | self |
112 | 0 | } |
113 | | |
114 | | /// Set number of hash bits (determines hash table size). |
115 | | /// |
116 | | /// More bits = larger hash table = better performance for large datasets. |
117 | | /// Default: 18 (256K buckets) |
118 | 1 | pub fn hash_bits(mut self, bits: u32) -> Self { |
119 | 1 | self.hash_bits = bits; |
120 | 1 | self |
121 | 1 | } |
122 | | |
123 | | /// Enable or disable caching. |
124 | | /// |
125 | | /// When enabled, frequently accessed values are kept in memory |
126 | | /// even after being written to disk. Uses CLOCK eviction algorithm. |
127 | 1 | pub fn enable_caching(mut self, enable: bool) -> Self { |
128 | 1 | self.enable_caching = Some(enable); |
129 | 1 | self |
130 | 1 | } |
131 | | |
132 | | /// Enable or disable TTL (Time-To-Live) functionality. |
133 | | /// |
134 | | /// When disabled (default), TTL operations will return errors and no background cleaner runs. |
135 | | /// When enabled, keys can have expiry times and a background cleaner removes expired keys. |
136 | | /// Default: false (disabled for optimal performance) |
137 | 12 | pub fn enable_ttl(mut self, enable: bool) -> Self { |
138 | 12 | self.enable_ttl = enable; |
139 | 12 | if enable { |
140 | 10 | let mut config = self.ttl_config.unwrap_or_default(); |
141 | 10 | config.enabled = true; |
142 | 10 | self.ttl_config = Some(config); |
143 | 10 | }2 |
144 | 12 | self |
145 | 12 | } |
146 | | |
147 | | /// Enable or disable TTL sweeper. |
148 | | /// |
149 | | /// When enabled, a background thread periodically removes expired keys. |
150 | | /// Note: This method is deprecated in favor of enable_ttl(). |
151 | 0 | pub fn enable_ttl_cleaner(mut self, enable: bool) -> Self { |
152 | 0 | let mut config = self.ttl_config.unwrap_or_default(); |
153 | 0 | config.enabled = enable; |
154 | 0 | self.ttl_config = Some(config); |
155 | 0 | self.enable_ttl = enable; // Also enable TTL when cleaner is enabled |
156 | 0 | self |
157 | 0 | } |
158 | | |
159 | | /// Configure TTL sweeper with custom parameters. |
160 | | /// |
161 | | /// # Arguments |
162 | | /// |
163 | | /// * `sample_size` - Keys to check per batch |
164 | | /// * `threshold` - Continue if >threshold expired (0.0-1.0) |
165 | | /// * `max_time_ms` - Max milliseconds per cleaning run |
166 | | /// * `interval_ms` - Sleep between runs |
167 | 0 | pub fn ttl_sweeper_config( |
168 | 0 | mut self, |
169 | 0 | sample_size: usize, |
170 | 0 | threshold: f32, |
171 | 0 | max_time_ms: u64, |
172 | 0 | interval_ms: u64, |
173 | 0 | ) -> Self { |
174 | 0 | self.ttl_config = Some(TtlConfig { |
175 | 0 | sample_size, |
176 | 0 | expiry_threshold: threshold, |
177 | 0 | max_iterations: 16, |
178 | 0 | max_time_per_run: Duration::from_millis(max_time_ms), |
179 | 0 | sleep_interval: Duration::from_millis(interval_ms), |
180 | 0 | enabled: true, |
181 | 0 | }); |
182 | 0 | self |
183 | 0 | } |
184 | | |
185 | | /// Build the FeoxStore |
186 | 15 | pub fn build(self) -> Result<FeoxStore> { |
187 | 15 | let memory_only = self.device_path.is_none(); |
188 | 15 | let enable_caching = self.enable_caching.unwrap_or(!memory_only); |
189 | | |
190 | 15 | let config = StoreConfig { |
191 | 15 | hash_bits: self.hash_bits, |
192 | 15 | memory_only, |
193 | 15 | enable_caching, |
194 | 15 | device_path: self.device_path, |
195 | 15 | file_size: self.file_size, |
196 | 15 | max_memory: self.max_memory, |
197 | 15 | enable_ttl: self.enable_ttl, |
198 | 15 | ttl_config: self.ttl_config, |
199 | 15 | }; |
200 | | |
201 | 15 | FeoxStore::with_config(config) |
202 | 15 | } |
203 | | } |
204 | | |
205 | | impl Default for StoreBuilder { |
206 | 0 | fn default() -> Self { |
207 | 0 | Self::new() |
208 | 0 | } |
209 | | } |