1use crate::{
6 BlockStoreConfig, BloomBlockStore, BloomConfig, CachedBlockStore, MemoryBlockStore,
7 MetricsBlockStore, SledBlockStore,
8};
9use crate::{ChunkingConfig, DedupBlockStore};
10#[cfg(feature = "encryption")]
11use crate::{Cipher, EncryptedBlockStore, EncryptionConfig};
12use crate::{CoalesceConfig, CoalescingBlockStore};
13#[cfg(feature = "compression")]
14use crate::{CompressionAlgorithm, CompressionBlockStore, CompressionConfig};
15use crate::{TtlBlockStore, TtlConfig};
16use ipfrs_core::Result;
17use std::path::PathBuf;
18use std::time::Duration;
19
20type FullStack = BloomBlockStore<CachedBlockStore<SledBlockStore>>;
22type MonitoredFullStack = MetricsBlockStore<FullStack>;
23
24#[cfg(feature = "compression")]
25type CompressedStack = CompressionBlockStore<FullStack>;
26#[cfg(feature = "compression")]
27type MonitoredCompressedStack = MetricsBlockStore<CompressedStack>;
28
29#[cfg(feature = "encryption")]
30type EncryptedStack = EncryptedBlockStore<FullStack>;
31#[cfg(feature = "encryption")]
32type MonitoredEncryptedStack = MetricsBlockStore<EncryptedStack>;
33
34type DedupStack = DedupBlockStore<FullStack>;
35type MonitoredDedupStack = MetricsBlockStore<DedupStack>;
36
37#[cfg(feature = "compression")]
38type UltimateStack = DedupBlockStore<CompressedStack>;
39#[cfg(feature = "compression")]
40type MonitoredUltimateStack = MetricsBlockStore<UltimateStack>;
41
42pub struct StorageStackBuilder {
44 config: BlockStoreConfig,
45 enable_cache: bool,
46 cache_size_mb: usize,
47 enable_bloom: bool,
48 bloom_expected_items: usize,
49 enable_tiering: bool,
50}
51
52impl Default for StorageStackBuilder {
53 fn default() -> Self {
54 Self {
55 config: BlockStoreConfig::default(),
56 enable_cache: true,
57 cache_size_mb: 100,
58 enable_bloom: true,
59 bloom_expected_items: 100_000,
60 enable_tiering: false,
61 }
62 }
63}
64
65impl StorageStackBuilder {
66 pub fn new() -> Self {
68 Self::default()
69 }
70
71 pub fn with_config(mut self, config: BlockStoreConfig) -> Self {
73 self.config = config;
74 self
75 }
76
77 pub fn with_path(mut self, path: PathBuf) -> Self {
79 self.config.path = path;
80 self
81 }
82
83 pub fn with_cache(mut self, size_mb: usize) -> Self {
85 self.enable_cache = true;
86 self.cache_size_mb = size_mb;
87 self
88 }
89
90 pub fn without_cache(mut self) -> Self {
92 self.enable_cache = false;
93 self
94 }
95
96 pub fn with_bloom(mut self, expected_items: usize) -> Self {
98 self.enable_bloom = true;
99 self.bloom_expected_items = expected_items;
100 self
101 }
102
103 pub fn without_bloom(mut self) -> Self {
105 self.enable_bloom = false;
106 self
107 }
108
109 pub fn with_tiering(mut self) -> Self {
111 self.enable_tiering = true;
112 self
113 }
114
115 pub fn build_simple(self) -> Result<SledBlockStore> {
117 SledBlockStore::new(self.config)
118 }
119
120 pub fn build_cached(self) -> Result<CachedBlockStore<SledBlockStore>> {
122 use crate::CacheConfig;
123 use std::num::NonZeroUsize;
124 let base = SledBlockStore::new(self.config)?;
125 let block_capacity = std::cmp::max(1, self.cache_size_mb * 1024 * 1024 / 4096);
127 let config = CacheConfig {
128 l1_capacity: NonZeroUsize::new(block_capacity)
129 .unwrap_or_else(|| NonZeroUsize::new(1024).expect("1024>0")),
130 max_block_bytes: 256 * 1024,
131 };
132 Ok(CachedBlockStore::new(base, config))
133 }
134
135 pub fn build_full(self) -> Result<BloomBlockStore<CachedBlockStore<SledBlockStore>>> {
137 use crate::CacheConfig;
138 use std::num::NonZeroUsize;
139 let base = SledBlockStore::new(self.config)?;
140
141 let block_capacity = if self.enable_cache {
142 std::cmp::max(1, self.cache_size_mb * 1024 * 1024 / 4096)
143 } else {
144 1 };
146 let cache_config = CacheConfig {
147 l1_capacity: NonZeroUsize::new(block_capacity)
148 .unwrap_or_else(|| NonZeroUsize::new(1).expect("1>0")),
149 max_block_bytes: 256 * 1024,
150 };
151 let cached = CachedBlockStore::new(base, cache_config);
152
153 if self.enable_bloom {
154 let bloom_config = BloomConfig::new(self.bloom_expected_items, 0.01);
155 Ok(BloomBlockStore::with_config(cached, bloom_config))
156 } else {
157 let bloom_config = BloomConfig::new(100, 0.01);
159 Ok(BloomBlockStore::with_config(cached, bloom_config))
160 }
161 }
162}
163
164pub fn development_stack() -> Result<BloomBlockStore<CachedBlockStore<SledBlockStore>>> {
171 StorageStackBuilder::new()
172 .with_config(BlockStoreConfig::development())
173 .with_cache(50)
174 .with_bloom(10_000)
175 .build_full()
176}
177
178pub fn production_stack(path: PathBuf) -> Result<FullStack> {
184 StorageStackBuilder::new()
185 .with_config(BlockStoreConfig::production(path))
186 .with_cache(500)
187 .with_bloom(1_000_000)
188 .build_full()
189}
190
191pub fn embedded_stack(path: PathBuf) -> Result<BloomBlockStore<CachedBlockStore<SledBlockStore>>> {
197 StorageStackBuilder::new()
198 .with_config(BlockStoreConfig::embedded(path))
199 .with_cache(10)
200 .with_bloom(5_000)
201 .build_full()
202}
203
204pub fn testing_stack() -> Result<BloomBlockStore<CachedBlockStore<SledBlockStore>>> {
210 StorageStackBuilder::new()
211 .with_config(BlockStoreConfig::testing())
212 .with_cache(5)
213 .with_bloom(1_000)
214 .build_full()
215}
216
217pub fn monitored_production_stack(path: PathBuf) -> Result<MonitoredFullStack> {
222 let base = production_stack(path)?;
223 Ok(MetricsBlockStore::new(base))
224}
225
226pub fn memory_stack() -> MetricsBlockStore<BloomBlockStore<MemoryBlockStore>> {
233 let base = MemoryBlockStore::new();
234 let bloom_config = BloomConfig::new(100_000, 0.01);
235 let bloom = BloomBlockStore::with_config(base, bloom_config);
236 MetricsBlockStore::new(bloom)
237}
238
239#[cfg(feature = "compression")]
244pub fn compressed_production_stack(path: PathBuf) -> Result<MonitoredCompressedStack> {
245 let base = production_stack(path)?;
246 let compression_config = CompressionConfig {
247 algorithm: CompressionAlgorithm::Zstd,
248 level: 3, threshold: 1024, max_ratio: 0.9, };
252 let compressed = CompressionBlockStore::new(base, compression_config);
253 Ok(MetricsBlockStore::new(compressed))
254}
255
256#[cfg(feature = "encryption")]
261pub fn encrypted_production_stack(
262 path: PathBuf,
263 password: &str,
264) -> Result<MonitoredEncryptedStack> {
265 use crate::EncryptionKey;
266
267 let base = production_stack(path)?;
268 let (key, _salt) =
269 EncryptionKey::derive_from_password(Cipher::ChaCha20Poly1305, password.as_bytes(), None)?;
270 let config = EncryptionConfig {
271 cipher: Cipher::ChaCha20Poly1305,
272 };
273 let encrypted = EncryptedBlockStore::new(base, key, config);
274 Ok(MetricsBlockStore::new(encrypted))
275}
276
277pub fn deduplicated_production_stack(path: PathBuf) -> Result<MonitoredDedupStack> {
282 let base = production_stack(path)?;
283 let chunking_config = ChunkingConfig::default();
284 let dedup = DedupBlockStore::new(base, chunking_config);
285 Ok(MetricsBlockStore::new(dedup))
286}
287
288#[cfg(feature = "compression")]
299pub fn ultimate_production_stack(path: PathBuf) -> Result<MonitoredUltimateStack> {
300 let base = compressed_production_stack(path)?;
301 let inner = base.into_inner();
303 let chunking_config = ChunkingConfig::default();
304 let dedup = DedupBlockStore::new(inner, chunking_config);
305 Ok(MetricsBlockStore::new(dedup))
306}
307
308pub fn ttl_production_stack(
319 path: PathBuf,
320 default_ttl: Duration,
321) -> Result<MetricsBlockStore<TtlBlockStore<FullStack>>> {
322 let base = production_stack(path)?;
323 let ttl_config = TtlConfig {
324 default_ttl,
325 auto_cleanup: true,
326 cleanup_interval: Duration::from_secs(300), max_tracked_blocks: 1_000_000,
328 };
329 let ttl_store = TtlBlockStore::new(base, ttl_config);
330 Ok(MetricsBlockStore::new(ttl_store))
331}
332
333pub fn cache_stack(path: PathBuf) -> Result<MetricsBlockStore<TtlBlockStore<FullStack>>> {
343 ttl_production_stack(path, Duration::from_secs(3600)) }
345
346pub fn coalescing_memory_stack() -> MetricsBlockStore<CoalescingBlockStore<MemoryBlockStore>> {
356 let base = MemoryBlockStore::new();
357 let coalesce_config = CoalesceConfig::new(1000, Duration::from_millis(100));
358 let coalescing = CoalescingBlockStore::new(base, coalesce_config);
359 MetricsBlockStore::new(coalescing)
360}
361
362pub fn read_optimized_stack(path: PathBuf) -> Result<FullStack> {
371 StorageStackBuilder::new()
372 .with_config(BlockStoreConfig::production(path))
373 .with_cache(1024) .with_bloom(2_000_000) .build_full()
376}
377
378pub fn write_optimized_stack(path: PathBuf) -> Result<MonitoredDedupStack> {
388 let base = StorageStackBuilder::new()
389 .with_config(BlockStoreConfig::production(path))
390 .with_cache(100) .with_bloom(1_000_000)
392 .build_full()?;
393
394 let chunking_config = ChunkingConfig::default();
395 let dedup = DedupBlockStore::new(base, chunking_config);
396 Ok(MetricsBlockStore::new(dedup))
397}
398
399pub fn iot_stack(path: PathBuf) -> Result<FullStack> {
408 StorageStackBuilder::new()
409 .with_config(BlockStoreConfig::embedded(path))
410 .with_cache(5) .with_bloom(1_000) .build_full()
413}
414
415pub fn resilient_stack(path: PathBuf) -> Result<MetricsBlockStore<TtlBlockStore<FullStack>>> {
425 ttl_production_stack(path, Duration::from_secs(86400)) }
427
428pub fn ingestion_stack(path: PathBuf) -> Result<MonitoredDedupStack> {
438 let base = StorageStackBuilder::new()
439 .with_config(BlockStoreConfig::production(path))
440 .with_cache(200) .with_bloom(2_000_000)
442 .build_full()?;
443
444 let chunking_config = ChunkingConfig::default();
445 let dedup = DedupBlockStore::new(base, chunking_config);
446
447 Ok(MetricsBlockStore::new(dedup))
448}
449
450pub fn cdn_edge_stack(path: PathBuf) -> Result<MetricsBlockStore<TtlBlockStore<FullStack>>> {
460 let base = StorageStackBuilder::new()
461 .with_config(BlockStoreConfig::production(path))
462 .with_cache(2048) .with_bloom(5_000_000) .build_full()?;
465
466 let ttl_config = TtlConfig {
467 default_ttl: Duration::from_secs(3600), auto_cleanup: true,
469 cleanup_interval: Duration::from_secs(600), max_tracked_blocks: 5_000_000,
471 };
472 let ttl_store = TtlBlockStore::new(base, ttl_config);
473
474 Ok(MetricsBlockStore::new(ttl_store))
475}
476
477#[cfg(feature = "compression")]
487pub fn scientific_archive_stack(path: PathBuf) -> Result<MonitoredUltimateStack> {
488 let base = StorageStackBuilder::new()
489 .with_config(BlockStoreConfig::production(path))
490 .with_cache(256)
491 .with_bloom(1_000_000)
492 .build_full()?;
493
494 let compression_config = CompressionConfig {
495 algorithm: CompressionAlgorithm::Zstd,
496 level: 5, threshold: 512, max_ratio: 0.95, };
500 let compressed = CompressionBlockStore::new(base, compression_config);
501
502 let chunking_config = ChunkingConfig::default();
503 let dedup = DedupBlockStore::new(compressed, chunking_config);
504
505 Ok(MetricsBlockStore::new(dedup))
506}
507
508pub fn blockchain_stack(path: PathBuf) -> Result<MonitoredDedupStack> {
519 let base = StorageStackBuilder::new()
520 .with_config(BlockStoreConfig::production(path))
521 .with_cache(512) .with_bloom(10_000_000) .build_full()?;
524
525 let chunking_config = ChunkingConfig::default();
526 let dedup = DedupBlockStore::new(base, chunking_config);
527
528 Ok(MetricsBlockStore::new(dedup))
529}
530
531pub fn ml_model_stack(path: PathBuf) -> Result<MonitoredFullStack> {
540 let base = StorageStackBuilder::new()
541 .with_config(BlockStoreConfig::production(path))
542 .with_cache(1024) .with_bloom(100_000) .build_full()?;
545
546 Ok(MetricsBlockStore::new(base))
547}
548
549pub fn media_streaming_stack(path: PathBuf) -> Result<MetricsBlockStore<TtlBlockStore<FullStack>>> {
558 let base = StorageStackBuilder::new()
559 .with_config(BlockStoreConfig::production(path))
560 .with_cache(3072) .with_bloom(1_000_000)
562 .build_full()?;
563
564 let ttl_config = TtlConfig {
565 default_ttl: Duration::from_secs(7200), auto_cleanup: true,
567 cleanup_interval: Duration::from_secs(300), max_tracked_blocks: 1_000_000,
569 };
570 let ttl_store = TtlBlockStore::new(base, ttl_config);
571
572 Ok(MetricsBlockStore::new(ttl_store))
573}
574
575#[cfg(feature = "compression")]
585pub fn distributed_fs_stack(path: PathBuf) -> Result<MonitoredUltimateStack> {
586 let base = StorageStackBuilder::new()
587 .with_config(BlockStoreConfig::production(path))
588 .with_cache(1536) .with_bloom(5_000_000)
590 .build_full()?;
591
592 let compression_config = CompressionConfig {
593 algorithm: CompressionAlgorithm::Lz4, level: 1, threshold: 4096, max_ratio: 0.9,
597 };
598 let compressed = CompressionBlockStore::new(base, compression_config);
599
600 let chunking_config = ChunkingConfig::default();
601 let dedup = DedupBlockStore::new(compressed, chunking_config);
602
603 Ok(MetricsBlockStore::new(dedup))
604}
605
606#[cfg(test)]
607mod tests {
608 use super::*;
609
610 #[test]
611 fn test_builder_simple() {
612 let _stack = StorageStackBuilder::new()
613 .with_path(std::env::temp_dir().join("test-simple"))
614 .build_simple();
615 assert!(_stack.is_ok());
616 }
617
618 #[test]
619 fn test_builder_cached() {
620 let _stack = StorageStackBuilder::new()
621 .with_path(std::env::temp_dir().join("test-cached"))
622 .with_cache(10)
623 .build_cached();
624 assert!(_stack.is_ok());
625 }
626
627 #[test]
628 fn test_builder_full() {
629 let _stack = StorageStackBuilder::new()
630 .with_path(std::env::temp_dir().join("test-full"))
631 .with_cache(10)
632 .with_bloom(1000)
633 .build_full();
634 assert!(_stack.is_ok());
635 }
636
637 #[test]
638 fn test_development_stack() {
639 let _stack = development_stack();
640 assert!(_stack.is_ok());
641 }
642
643 #[test]
644 fn test_testing_stack() {
645 let _stack = testing_stack();
646 assert!(_stack.is_ok());
647 }
648}