reddb_server/storage/cache/blob/
config.rs1use std::path::{Path, PathBuf};
2
3pub const DEFAULT_BLOB_L1_BYTES_MAX: usize = 256 * 1024 * 1024;
4pub const DEFAULT_BLOB_L2_BYTES_MAX: u64 = 4 * 1024 * 1024 * 1024;
5pub const DEFAULT_BLOB_MAX_NAMESPACES: usize = 256;
6pub const DEFAULT_BLOB_SHARDS: usize = 64;
7pub const DEFAULT_CONTENT_METADATA_KEYS_MAX: usize = 32;
8pub const DEFAULT_CONTENT_METADATA_BYTES_MAX: usize = 4 * 1024;
9pub const METRIC_CACHE_BLOB_L1_BYTES_IN_USE: &str = "cache_blob_l1_bytes_in_use";
10pub const METRIC_CACHE_VERSION_MISMATCH_TOTAL: &str = "cache_version_mismatch_total";
11pub const METRIC_CACHE_BLOB_L2_BYTES_IN_USE: &str = "reddb_cache_blob_l2_bytes_in_use";
12pub const METRIC_CACHE_BLOB_L2_FULL_REJECTIONS_TOTAL: &str =
13 "reddb_cache_blob_l2_full_rejections_total";
14pub const METRIC_CACHE_BLOB_SYNOPSIS_METADATA_READS_TOTAL: &str =
15 "cache_blob_synopsis_metadata_reads_total";
16pub const METRIC_CACHE_BLOB_SYNOPSIS_BYTES: &str = "cache_blob_synopsis_bytes";
17
18pub const DEFAULT_BLOB_SYNOPSIS_CAPACITY: usize = 10_000;
22
23#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
25pub enum L2PromotionPolicy {
26 Immediate,
27 #[default]
28 OnSecondHit,
29}
30
31#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
39pub enum L2Compression {
40 Off,
41 #[default]
42 On,
43}
44
45#[derive(Debug, Clone, PartialEq, Eq)]
46pub struct BlobCacheConfig {
47 pub(super) l1_bytes_max: usize,
48 pub(super) l2_bytes_max: u64,
49 pub(super) l2_path: Option<PathBuf>,
50 pub(super) max_namespaces: usize,
51 pub(super) shard_count: usize,
52 pub(super) content_metadata_keys_max: usize,
53 pub(super) content_metadata_bytes_max: usize,
54 pub(super) l2_compression: L2Compression,
55 pub(super) l2_promotion_policy: L2PromotionPolicy,
56}
57
58impl Default for BlobCacheConfig {
59 fn default() -> Self {
60 Self {
61 l1_bytes_max: DEFAULT_BLOB_L1_BYTES_MAX,
62 l2_bytes_max: DEFAULT_BLOB_L2_BYTES_MAX,
63 l2_path: None,
64 max_namespaces: DEFAULT_BLOB_MAX_NAMESPACES,
65 shard_count: DEFAULT_BLOB_SHARDS,
66 content_metadata_keys_max: DEFAULT_CONTENT_METADATA_KEYS_MAX,
67 content_metadata_bytes_max: DEFAULT_CONTENT_METADATA_BYTES_MAX,
68 l2_compression: L2Compression::default(),
69 l2_promotion_policy: L2PromotionPolicy::default(),
70 }
71 }
72}
73
74impl BlobCacheConfig {
75 pub fn builder() -> BlobCacheConfigBuilder {
80 BlobCacheConfigBuilder::new()
81 }
82
83 pub fn with_l1_bytes_max(mut self, l1_bytes_max: usize) -> Self {
84 self.l1_bytes_max = l1_bytes_max;
85 self
86 }
87
88 pub fn with_l2_bytes_max(mut self, l2_bytes_max: u64) -> Self {
89 self.l2_bytes_max = l2_bytes_max;
90 self
91 }
92
93 pub fn with_l2_path(mut self, path: impl Into<PathBuf>) -> Self {
94 self.l2_path = Some(path.into());
95 self
96 }
97
98 pub fn with_max_namespaces(mut self, max_namespaces: usize) -> Self {
99 self.max_namespaces = max_namespaces;
100 self
101 }
102
103 pub fn with_shard_count(mut self, shard_count: usize) -> Self {
104 self.shard_count = shard_count.max(1);
105 self
106 }
107
108 pub fn with_content_metadata_limits(mut self, keys_max: usize, bytes_max: usize) -> Self {
109 self.content_metadata_keys_max = keys_max;
110 self.content_metadata_bytes_max = bytes_max;
111 self
112 }
113
114 pub fn with_l2_compression(mut self, compression: L2Compression) -> Self {
115 self.l2_compression = compression;
116 self
117 }
118
119 pub fn with_l2_promotion_policy(mut self, policy: L2PromotionPolicy) -> Self {
120 self.l2_promotion_policy = policy;
121 self
122 }
123
124 pub fn l1_bytes_max(&self) -> usize {
125 self.l1_bytes_max
126 }
127
128 pub fn l2_bytes_max(&self) -> u64 {
129 self.l2_bytes_max
130 }
131
132 pub fn l2_path(&self) -> Option<&Path> {
133 self.l2_path.as_deref()
134 }
135
136 pub fn max_namespaces(&self) -> usize {
137 self.max_namespaces
138 }
139
140 pub fn shard_count(&self) -> usize {
141 self.shard_count
142 }
143
144 pub fn content_metadata_keys_max(&self) -> usize {
145 self.content_metadata_keys_max
146 }
147
148 pub fn content_metadata_bytes_max(&self) -> usize {
149 self.content_metadata_bytes_max
150 }
151
152 pub fn l2_compression(&self) -> L2Compression {
153 self.l2_compression
154 }
155
156 pub fn l2_promotion_policy(&self) -> L2PromotionPolicy {
157 self.l2_promotion_policy
158 }
159}
160
161#[derive(Debug, Clone)]
166pub struct BlobCacheConfigBuilder {
167 inner: BlobCacheConfig,
168}
169
170#[derive(Debug, Clone, PartialEq, Eq)]
171pub enum BlobCacheConfigError {
172 ZeroShardCount,
174 ZeroMaxNamespaces,
176}
177
178impl BlobCacheConfigBuilder {
179 fn new() -> Self {
180 Self {
181 inner: BlobCacheConfig::default(),
182 }
183 }
184
185 pub fn l1_bytes_max(mut self, value: usize) -> Self {
186 self.inner.l1_bytes_max = value;
187 self
188 }
189
190 pub fn l2_bytes_max(mut self, value: u64) -> Self {
191 self.inner.l2_bytes_max = value;
192 self
193 }
194
195 pub fn l2_path(mut self, path: impl Into<PathBuf>) -> Self {
196 self.inner.l2_path = Some(path.into());
197 self
198 }
199
200 pub fn max_namespaces(mut self, value: usize) -> Self {
201 self.inner.max_namespaces = value;
202 self
203 }
204
205 pub fn shard_count(mut self, value: usize) -> Self {
206 self.inner.shard_count = value;
207 self
208 }
209
210 pub fn content_metadata_keys_max(mut self, value: usize) -> Self {
211 self.inner.content_metadata_keys_max = value;
212 self
213 }
214
215 pub fn content_metadata_bytes_max(mut self, value: usize) -> Self {
216 self.inner.content_metadata_bytes_max = value;
217 self
218 }
219
220 pub fn l2_compression(mut self, value: L2Compression) -> Self {
221 self.inner.l2_compression = value;
222 self
223 }
224
225 pub fn l2_promotion_policy(mut self, value: L2PromotionPolicy) -> Self {
226 self.inner.l2_promotion_policy = value;
227 self
228 }
229
230 pub fn try_build(self) -> Result<BlobCacheConfig, BlobCacheConfigError> {
231 if self.inner.shard_count == 0 {
232 return Err(BlobCacheConfigError::ZeroShardCount);
233 }
234 if self.inner.max_namespaces == 0 {
235 return Err(BlobCacheConfigError::ZeroMaxNamespaces);
236 }
237 Ok(self.inner)
238 }
239
240 pub fn build(self) -> BlobCacheConfig {
243 self.try_build().expect("blob cache config")
244 }
245}
246
247#[cfg(test)]
248mod tests {
249 use super::*;
250
251 #[test]
252 fn blob_cache_config_builder_rejects_zero_shard_count() {
253 let err = BlobCacheConfig::builder()
254 .shard_count(0)
255 .try_build()
256 .expect_err("zero shard count must be rejected");
257 assert_eq!(err, BlobCacheConfigError::ZeroShardCount);
258 }
259
260 #[test]
261 fn blob_cache_config_builder_rejects_zero_max_namespaces() {
262 let err = BlobCacheConfig::builder()
263 .max_namespaces(0)
264 .try_build()
265 .expect_err("zero max_namespaces must be rejected");
266 assert_eq!(err, BlobCacheConfigError::ZeroMaxNamespaces);
267 }
268}