use crate::{
BlockStoreConfig, BloomBlockStore, BloomConfig, CachedBlockStore, MemoryBlockStore,
MetricsBlockStore, SledBlockStore,
};
use crate::{ChunkingConfig, DedupBlockStore};
#[cfg(feature = "encryption")]
use crate::{Cipher, EncryptedBlockStore, EncryptionConfig};
use crate::{CoalesceConfig, CoalescingBlockStore};
#[cfg(feature = "compression")]
use crate::{CompressionAlgorithm, CompressionBlockStore, CompressionConfig};
use crate::{TtlBlockStore, TtlConfig};
use ipfrs_core::Result;
use std::path::PathBuf;
use std::time::Duration;
type FullStack = BloomBlockStore<CachedBlockStore<SledBlockStore>>;
type MonitoredFullStack = MetricsBlockStore<FullStack>;
#[cfg(feature = "compression")]
type CompressedStack = CompressionBlockStore<FullStack>;
#[cfg(feature = "compression")]
type MonitoredCompressedStack = MetricsBlockStore<CompressedStack>;
#[cfg(feature = "encryption")]
type EncryptedStack = EncryptedBlockStore<FullStack>;
#[cfg(feature = "encryption")]
type MonitoredEncryptedStack = MetricsBlockStore<EncryptedStack>;
type DedupStack = DedupBlockStore<FullStack>;
type MonitoredDedupStack = MetricsBlockStore<DedupStack>;
#[cfg(feature = "compression")]
type UltimateStack = DedupBlockStore<CompressedStack>;
#[cfg(feature = "compression")]
type MonitoredUltimateStack = MetricsBlockStore<UltimateStack>;
pub struct StorageStackBuilder {
config: BlockStoreConfig,
enable_cache: bool,
cache_size_mb: usize,
enable_bloom: bool,
bloom_expected_items: usize,
enable_tiering: bool,
}
impl Default for StorageStackBuilder {
fn default() -> Self {
Self {
config: BlockStoreConfig::default(),
enable_cache: true,
cache_size_mb: 100,
enable_bloom: true,
bloom_expected_items: 100_000,
enable_tiering: false,
}
}
}
impl StorageStackBuilder {
pub fn new() -> Self {
Self::default()
}
pub fn with_config(mut self, config: BlockStoreConfig) -> Self {
self.config = config;
self
}
pub fn with_path(mut self, path: PathBuf) -> Self {
self.config.path = path;
self
}
pub fn with_cache(mut self, size_mb: usize) -> Self {
self.enable_cache = true;
self.cache_size_mb = size_mb;
self
}
pub fn without_cache(mut self) -> Self {
self.enable_cache = false;
self
}
pub fn with_bloom(mut self, expected_items: usize) -> Self {
self.enable_bloom = true;
self.bloom_expected_items = expected_items;
self
}
pub fn without_bloom(mut self) -> Self {
self.enable_bloom = false;
self
}
pub fn with_tiering(mut self) -> Self {
self.enable_tiering = true;
self
}
pub fn build_simple(self) -> Result<SledBlockStore> {
SledBlockStore::new(self.config)
}
pub fn build_cached(self) -> Result<CachedBlockStore<SledBlockStore>> {
let base = SledBlockStore::new(self.config)?;
let cache_size = self.cache_size_mb * 1024 * 1024;
Ok(CachedBlockStore::new(base, cache_size))
}
pub fn build_full(self) -> Result<BloomBlockStore<CachedBlockStore<SledBlockStore>>> {
let base = SledBlockStore::new(self.config)?;
let cache_size = if self.enable_cache {
self.cache_size_mb * 1024 * 1024
} else {
1024 };
let cached = CachedBlockStore::new(base, cache_size);
if self.enable_bloom {
let bloom_config = BloomConfig::new(self.bloom_expected_items, 0.01);
Ok(BloomBlockStore::with_config(cached, bloom_config))
} else {
let bloom_config = BloomConfig::new(100, 0.01);
Ok(BloomBlockStore::with_config(cached, bloom_config))
}
}
}
pub fn development_stack() -> Result<BloomBlockStore<CachedBlockStore<SledBlockStore>>> {
StorageStackBuilder::new()
.with_config(BlockStoreConfig::development())
.with_cache(50)
.with_bloom(10_000)
.build_full()
}
pub fn production_stack(path: PathBuf) -> Result<FullStack> {
StorageStackBuilder::new()
.with_config(BlockStoreConfig::production(path))
.with_cache(500)
.with_bloom(1_000_000)
.build_full()
}
pub fn embedded_stack(path: PathBuf) -> Result<BloomBlockStore<CachedBlockStore<SledBlockStore>>> {
StorageStackBuilder::new()
.with_config(BlockStoreConfig::embedded(path))
.with_cache(10)
.with_bloom(5_000)
.build_full()
}
pub fn testing_stack() -> Result<BloomBlockStore<CachedBlockStore<SledBlockStore>>> {
StorageStackBuilder::new()
.with_config(BlockStoreConfig::testing())
.with_cache(5)
.with_bloom(1_000)
.build_full()
}
pub fn monitored_production_stack(path: PathBuf) -> Result<MonitoredFullStack> {
let base = production_stack(path)?;
Ok(MetricsBlockStore::new(base))
}
pub fn memory_stack() -> MetricsBlockStore<BloomBlockStore<MemoryBlockStore>> {
let base = MemoryBlockStore::new();
let bloom_config = BloomConfig::new(100_000, 0.01);
let bloom = BloomBlockStore::with_config(base, bloom_config);
MetricsBlockStore::new(bloom)
}
#[cfg(feature = "compression")]
pub fn compressed_production_stack(path: PathBuf) -> Result<MonitoredCompressedStack> {
let base = production_stack(path)?;
let compression_config = CompressionConfig {
algorithm: CompressionAlgorithm::Zstd,
level: 3, threshold: 1024, max_ratio: 0.9, };
let compressed = CompressionBlockStore::new(base, compression_config);
Ok(MetricsBlockStore::new(compressed))
}
#[cfg(feature = "encryption")]
pub fn encrypted_production_stack(
path: PathBuf,
password: &str,
) -> Result<MonitoredEncryptedStack> {
use crate::EncryptionKey;
let base = production_stack(path)?;
let (key, _salt) =
EncryptionKey::derive_from_password(Cipher::ChaCha20Poly1305, password.as_bytes(), None)?;
let config = EncryptionConfig {
cipher: Cipher::ChaCha20Poly1305,
};
let encrypted = EncryptedBlockStore::new(base, key, config);
Ok(MetricsBlockStore::new(encrypted))
}
pub fn deduplicated_production_stack(path: PathBuf) -> Result<MonitoredDedupStack> {
let base = production_stack(path)?;
let chunking_config = ChunkingConfig::default();
let dedup = DedupBlockStore::new(base, chunking_config);
Ok(MetricsBlockStore::new(dedup))
}
#[cfg(feature = "compression")]
pub fn ultimate_production_stack(path: PathBuf) -> Result<MonitoredUltimateStack> {
let base = compressed_production_stack(path)?;
let inner = base.into_inner();
let chunking_config = ChunkingConfig::default();
let dedup = DedupBlockStore::new(inner, chunking_config);
Ok(MetricsBlockStore::new(dedup))
}
pub fn ttl_production_stack(
path: PathBuf,
default_ttl: Duration,
) -> Result<MetricsBlockStore<TtlBlockStore<FullStack>>> {
let base = production_stack(path)?;
let ttl_config = TtlConfig {
default_ttl,
auto_cleanup: true,
cleanup_interval: Duration::from_secs(300), max_tracked_blocks: 1_000_000,
};
let ttl_store = TtlBlockStore::new(base, ttl_config);
Ok(MetricsBlockStore::new(ttl_store))
}
pub fn cache_stack(path: PathBuf) -> Result<MetricsBlockStore<TtlBlockStore<FullStack>>> {
ttl_production_stack(path, Duration::from_secs(3600)) }
pub fn coalescing_memory_stack() -> MetricsBlockStore<CoalescingBlockStore<MemoryBlockStore>> {
let base = MemoryBlockStore::new();
let coalesce_config = CoalesceConfig::new(1000, Duration::from_millis(100));
let coalescing = CoalescingBlockStore::new(base, coalesce_config);
MetricsBlockStore::new(coalescing)
}
pub fn read_optimized_stack(path: PathBuf) -> Result<FullStack> {
StorageStackBuilder::new()
.with_config(BlockStoreConfig::production(path))
.with_cache(1024) .with_bloom(2_000_000) .build_full()
}
pub fn write_optimized_stack(path: PathBuf) -> Result<MonitoredDedupStack> {
let base = StorageStackBuilder::new()
.with_config(BlockStoreConfig::production(path))
.with_cache(100) .with_bloom(1_000_000)
.build_full()?;
let chunking_config = ChunkingConfig::default();
let dedup = DedupBlockStore::new(base, chunking_config);
Ok(MetricsBlockStore::new(dedup))
}
pub fn iot_stack(path: PathBuf) -> Result<FullStack> {
StorageStackBuilder::new()
.with_config(BlockStoreConfig::embedded(path))
.with_cache(5) .with_bloom(1_000) .build_full()
}
pub fn resilient_stack(path: PathBuf) -> Result<MetricsBlockStore<TtlBlockStore<FullStack>>> {
ttl_production_stack(path, Duration::from_secs(86400)) }
pub fn ingestion_stack(path: PathBuf) -> Result<MonitoredDedupStack> {
let base = StorageStackBuilder::new()
.with_config(BlockStoreConfig::production(path))
.with_cache(200) .with_bloom(2_000_000)
.build_full()?;
let chunking_config = ChunkingConfig::default();
let dedup = DedupBlockStore::new(base, chunking_config);
Ok(MetricsBlockStore::new(dedup))
}
pub fn cdn_edge_stack(path: PathBuf) -> Result<MetricsBlockStore<TtlBlockStore<FullStack>>> {
let base = StorageStackBuilder::new()
.with_config(BlockStoreConfig::production(path))
.with_cache(2048) .with_bloom(5_000_000) .build_full()?;
let ttl_config = TtlConfig {
default_ttl: Duration::from_secs(3600), auto_cleanup: true,
cleanup_interval: Duration::from_secs(600), max_tracked_blocks: 5_000_000,
};
let ttl_store = TtlBlockStore::new(base, ttl_config);
Ok(MetricsBlockStore::new(ttl_store))
}
#[cfg(feature = "compression")]
pub fn scientific_archive_stack(path: PathBuf) -> Result<MonitoredUltimateStack> {
let base = StorageStackBuilder::new()
.with_config(BlockStoreConfig::production(path))
.with_cache(256)
.with_bloom(1_000_000)
.build_full()?;
let compression_config = CompressionConfig {
algorithm: CompressionAlgorithm::Zstd,
level: 5, threshold: 512, max_ratio: 0.95, };
let compressed = CompressionBlockStore::new(base, compression_config);
let chunking_config = ChunkingConfig::default();
let dedup = DedupBlockStore::new(compressed, chunking_config);
Ok(MetricsBlockStore::new(dedup))
}
pub fn blockchain_stack(path: PathBuf) -> Result<MonitoredDedupStack> {
let base = StorageStackBuilder::new()
.with_config(BlockStoreConfig::production(path))
.with_cache(512) .with_bloom(10_000_000) .build_full()?;
let chunking_config = ChunkingConfig::default();
let dedup = DedupBlockStore::new(base, chunking_config);
Ok(MetricsBlockStore::new(dedup))
}
pub fn ml_model_stack(path: PathBuf) -> Result<MonitoredFullStack> {
let base = StorageStackBuilder::new()
.with_config(BlockStoreConfig::production(path))
.with_cache(1024) .with_bloom(100_000) .build_full()?;
Ok(MetricsBlockStore::new(base))
}
pub fn media_streaming_stack(path: PathBuf) -> Result<MetricsBlockStore<TtlBlockStore<FullStack>>> {
let base = StorageStackBuilder::new()
.with_config(BlockStoreConfig::production(path))
.with_cache(3072) .with_bloom(1_000_000)
.build_full()?;
let ttl_config = TtlConfig {
default_ttl: Duration::from_secs(7200), auto_cleanup: true,
cleanup_interval: Duration::from_secs(300), max_tracked_blocks: 1_000_000,
};
let ttl_store = TtlBlockStore::new(base, ttl_config);
Ok(MetricsBlockStore::new(ttl_store))
}
#[cfg(feature = "compression")]
pub fn distributed_fs_stack(path: PathBuf) -> Result<MonitoredUltimateStack> {
let base = StorageStackBuilder::new()
.with_config(BlockStoreConfig::production(path))
.with_cache(1536) .with_bloom(5_000_000)
.build_full()?;
let compression_config = CompressionConfig {
algorithm: CompressionAlgorithm::Lz4, level: 1, threshold: 4096, max_ratio: 0.9,
};
let compressed = CompressionBlockStore::new(base, compression_config);
let chunking_config = ChunkingConfig::default();
let dedup = DedupBlockStore::new(compressed, chunking_config);
Ok(MetricsBlockStore::new(dedup))
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_builder_simple() {
let _stack = StorageStackBuilder::new()
.with_path(PathBuf::from("/tmp/test-simple"))
.build_simple();
assert!(_stack.is_ok());
}
#[test]
fn test_builder_cached() {
let _stack = StorageStackBuilder::new()
.with_path(PathBuf::from("/tmp/test-cached"))
.with_cache(10)
.build_cached();
assert!(_stack.is_ok());
}
#[test]
fn test_builder_full() {
let _stack = StorageStackBuilder::new()
.with_path(PathBuf::from("/tmp/test-full"))
.with_cache(10)
.with_bloom(1000)
.build_full();
assert!(_stack.is_ok());
}
#[test]
fn test_development_stack() {
let _stack = development_stack();
assert!(_stack.is_ok());
}
#[test]
fn test_testing_stack() {
let _stack = testing_stack();
assert!(_stack.is_ok());
}
}