1pub mod compression;
15pub mod immutable;
16pub mod mmap_storage;
17pub mod mvcc;
18pub mod temporal;
19pub mod tiered;
20pub mod virtualization;
21
22pub use mvcc::{IsolationLevel, MvccConfig, MvccStore, TransactionId as MvccTransactionId};
23use parking_lot::RwLock;
24
25use crate::OxirsError;
26use std::path::Path;
27use std::sync::Arc;
28
29#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
31pub struct StorageConfig {
32 pub enable_tiering: bool,
34 pub enable_columnar: bool,
36 pub enable_temporal: bool,
38 pub compression: CompressionType,
40 pub tiers: TierConfig,
42 pub cache_size_mb: usize,
44}
45
46impl Default for StorageConfig {
47 fn default() -> Self {
48 StorageConfig {
49 enable_tiering: true,
50 enable_columnar: true,
51 enable_temporal: true,
52 compression: CompressionType::Zstd { level: 3 },
53 tiers: TierConfig::default(),
54 cache_size_mb: 1024,
55 }
56 }
57}
58
59#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
61pub enum CompressionType {
62 None,
63 Lz4 { level: u32 },
64 Zstd { level: i32 },
65 RdfCustom { dictionary_size: usize },
66}
67
68#[derive(Debug, Clone, Default, serde::Serialize, serde::Deserialize)]
70pub struct TierConfig {
71 pub hot_tier: HotTierConfig,
73 pub warm_tier: WarmTierConfig,
75 pub cold_tier: ColdTierConfig,
77 pub archive_tier: ArchiveTierConfig,
79}
80
81#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
83pub struct HotTierConfig {
84 pub max_size_mb: usize,
86 pub eviction_policy: EvictionPolicy,
88 pub ttl_seconds: Option<u64>,
90}
91
92impl Default for HotTierConfig {
93 fn default() -> Self {
94 HotTierConfig {
95 max_size_mb: 4096,
96 eviction_policy: EvictionPolicy::Lru,
97 ttl_seconds: Some(3600),
98 }
99 }
100}
101
102#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
104pub struct WarmTierConfig {
105 pub path: String,
107 pub max_size_gb: usize,
109 pub promotion_threshold: u32,
111 pub demotion_threshold_days: u32,
113}
114
115impl Default for WarmTierConfig {
116 fn default() -> Self {
117 WarmTierConfig {
118 path: "/var/oxirs/warm".to_string(),
119 max_size_gb: 100,
120 promotion_threshold: 10,
121 demotion_threshold_days: 7,
122 }
123 }
124}
125
126#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
128pub struct ColdTierConfig {
129 pub path: String,
131 pub max_size_tb: usize,
133 pub compression_level: i32,
135 pub archive_threshold_days: u32,
137}
138
139impl Default for ColdTierConfig {
140 fn default() -> Self {
141 ColdTierConfig {
142 path: "/var/oxirs/cold".to_string(),
143 max_size_tb: 10,
144 compression_level: 9,
145 archive_threshold_days: 90,
146 }
147 }
148}
149
150#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
152pub struct ArchiveTierConfig {
153 pub backend: ArchiveBackend,
155 pub retention_years: Option<u32>,
157 pub immutable: bool,
159}
160
161impl Default for ArchiveTierConfig {
162 fn default() -> Self {
163 ArchiveTierConfig {
164 backend: ArchiveBackend::Local("/var/oxirs/archive".to_string()),
165 retention_years: Some(7),
166 immutable: true,
167 }
168 }
169}
170
171#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
173pub enum ArchiveBackend {
174 Local(String),
175 S3 { bucket: String, prefix: String },
176 GCS { bucket: String, prefix: String },
177 Azure { container: String, prefix: String },
178}
179
180#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
182pub enum EvictionPolicy {
183 Lru,
184 Lfu,
185 Fifo,
186 Adaptive,
187}
188
189#[async_trait::async_trait]
191pub trait StorageEngine: Send + Sync {
192 async fn init(&mut self, config: StorageConfig) -> Result<(), OxirsError>;
194
195 async fn store_triple(&self, triple: &crate::model::Triple) -> Result<(), OxirsError>;
197
198 async fn store_triples(&self, triples: &[crate::model::Triple]) -> Result<(), OxirsError>;
200
201 async fn query_triples(
203 &self,
204 pattern: &crate::model::TriplePattern,
205 ) -> Result<Vec<crate::model::Triple>, OxirsError>;
206
207 async fn delete_triples(
209 &self,
210 pattern: &crate::model::TriplePattern,
211 ) -> Result<usize, OxirsError>;
212
213 async fn stats(&self) -> Result<StorageStats, OxirsError>;
215
216 async fn optimize(&self) -> Result<(), OxirsError>;
218
219 async fn backup(&self, path: &Path) -> Result<(), OxirsError>;
221
222 async fn restore(&self, path: &Path) -> Result<(), OxirsError>;
224}
225
226#[derive(Debug, Clone)]
228pub struct StorageStats {
229 pub total_triples: u64,
231 pub total_size_bytes: u64,
233 pub tier_stats: TierStats,
235 pub compression_ratio: f64,
237 pub query_metrics: QueryMetrics,
239}
240
241#[derive(Debug, Clone)]
243pub struct TierStats {
244 pub hot: TierStat,
246 pub warm: TierStat,
248 pub cold: TierStat,
250 pub archive: TierStat,
252}
253
254#[derive(Debug, Clone)]
256pub struct TierStat {
257 pub triple_count: u64,
259 pub size_bytes: u64,
261 pub hit_rate: f64,
263 pub avg_access_time_us: u64,
265}
266
267#[derive(Debug, Clone)]
269pub struct QueryMetrics {
270 pub avg_query_time_ms: f64,
272 pub p99_query_time_ms: f64,
274 pub qps: f64,
276 pub cache_hit_rate: f64,
278}
279
280pub async fn create_engine(config: StorageConfig) -> Result<Arc<dyn StorageEngine>, OxirsError> {
282 let engine = SimpleStorageEngine::new(config).await?;
283 Ok(Arc::new(engine))
284}
285
286pub struct SimpleStorageEngine {
288 #[allow(dead_code)]
289 config: StorageConfig,
290 mvcc_store: MvccStore,
291 stats: Arc<RwLock<StorageStats>>,
292 #[allow(dead_code)]
293 base_path: std::path::PathBuf,
294}
295
296impl SimpleStorageEngine {
297 pub async fn new(config: StorageConfig) -> Result<Self, OxirsError> {
299 let base_path = std::path::PathBuf::from("/tmp/oxirs_storage");
300 std::fs::create_dir_all(&base_path)
301 .map_err(|e| OxirsError::Store(format!("Failed to create storage directory: {e}")))?;
302
303 let mvcc_config = MvccConfig {
304 max_versions_per_triple: 100,
305 gc_interval: std::time::Duration::from_secs(60),
306 min_version_age: std::time::Duration::from_secs(30),
307 enable_snapshot_isolation: true,
308 enable_read_your_writes: true,
309 conflict_detection: mvcc::ConflictDetection::OptimisticTwoPhase,
310 };
311
312 let mvcc_store = MvccStore::new(mvcc_config);
313
314 let initial_stats = StorageStats {
315 total_triples: 0,
316 total_size_bytes: 0,
317 tier_stats: TierStats {
318 hot: TierStat {
319 triple_count: 0,
320 size_bytes: 0,
321 hit_rate: 0.0,
322 avg_access_time_us: 0,
323 },
324 warm: TierStat {
325 triple_count: 0,
326 size_bytes: 0,
327 hit_rate: 0.0,
328 avg_access_time_us: 0,
329 },
330 cold: TierStat {
331 triple_count: 0,
332 size_bytes: 0,
333 hit_rate: 0.0,
334 avg_access_time_us: 0,
335 },
336 archive: TierStat {
337 triple_count: 0,
338 size_bytes: 0,
339 hit_rate: 0.0,
340 avg_access_time_us: 0,
341 },
342 },
343 compression_ratio: 1.0,
344 query_metrics: QueryMetrics {
345 avg_query_time_ms: 0.0,
346 p99_query_time_ms: 0.0,
347 qps: 0.0,
348 cache_hit_rate: 0.0,
349 },
350 };
351
352 Ok(Self {
353 config,
354 mvcc_store,
355 stats: Arc::new(RwLock::new(initial_stats)),
356 base_path,
357 })
358 }
359}
360
361#[async_trait::async_trait]
362impl StorageEngine for SimpleStorageEngine {
363 async fn init(&mut self, _config: StorageConfig) -> Result<(), OxirsError> {
364 Ok(())
366 }
367
368 async fn store_triple(&self, triple: &crate::model::Triple) -> Result<(), OxirsError> {
369 let tx_id = self
370 .mvcc_store
371 .begin_transaction(IsolationLevel::Snapshot)
372 .map_err(|e| OxirsError::Store(format!("Failed to begin transaction: {e}")))?;
373
374 self.mvcc_store
375 .insert(tx_id, triple.clone())
376 .map_err(|e| OxirsError::Store(format!("Failed to insert triple: {e}")))?;
377
378 self.mvcc_store
379 .commit_transaction(tx_id)
380 .map_err(|e| OxirsError::Store(format!("Failed to commit transaction: {e}")))?;
381
382 let mut stats = self.stats.write();
384 stats.total_triples += 1;
385 stats.tier_stats.hot.triple_count += 1;
386
387 Ok(())
388 }
389
390 async fn store_triples(&self, triples: &[crate::model::Triple]) -> Result<(), OxirsError> {
391 let tx_id = self
392 .mvcc_store
393 .begin_transaction(IsolationLevel::Snapshot)
394 .map_err(|e| OxirsError::Store(format!("Failed to begin transaction: {e}")))?;
395
396 for triple in triples {
397 self.mvcc_store
398 .insert(tx_id, triple.clone())
399 .map_err(|e| OxirsError::Store(format!("Failed to insert triple: {e}")))?;
400 }
401
402 self.mvcc_store
403 .commit_transaction(tx_id)
404 .map_err(|e| OxirsError::Store(format!("Failed to commit transaction: {e}")))?;
405
406 let mut stats = self.stats.write();
408 stats.total_triples += triples.len() as u64;
409 stats.tier_stats.hot.triple_count += triples.len() as u64;
410
411 Ok(())
412 }
413
414 async fn query_triples(
415 &self,
416 pattern: &crate::model::TriplePattern,
417 ) -> Result<Vec<crate::model::Triple>, OxirsError> {
418 let tx_id = self
419 .mvcc_store
420 .begin_transaction(IsolationLevel::Snapshot)
421 .map_err(|e| OxirsError::Store(format!("Failed to begin transaction: {e}")))?;
422
423 let subject = Self::pattern_to_subject(pattern.subject());
425 let predicate = Self::pattern_to_predicate(pattern.predicate());
426 let object = Self::pattern_to_object(pattern.object());
427
428 let results = self
429 .mvcc_store
430 .query(tx_id, subject.as_ref(), predicate.as_ref(), object.as_ref())
431 .map_err(|e| OxirsError::Store(format!("Failed to query triples: {e}")))?;
432
433 let filtered: Vec<_> = results
435 .into_iter()
436 .filter(|triple| pattern.matches(triple))
437 .collect();
438
439 Ok(filtered)
440 }
441
442 async fn delete_triples(
443 &self,
444 pattern: &crate::model::TriplePattern,
445 ) -> Result<usize, OxirsError> {
446 let tx_id = self
447 .mvcc_store
448 .begin_transaction(IsolationLevel::Snapshot)
449 .map_err(|e| OxirsError::Store(format!("Failed to begin transaction: {e}")))?;
450
451 let subject = Self::pattern_to_subject(pattern.subject());
453 let predicate = Self::pattern_to_predicate(pattern.predicate());
454 let object = Self::pattern_to_object(pattern.object());
455
456 let matching_triples = self
458 .mvcc_store
459 .query(tx_id, subject.as_ref(), predicate.as_ref(), object.as_ref())
460 .map_err(|e| OxirsError::Store(format!("Failed to query triples for deletion: {e}")))?;
461
462 let filtered: Vec<_> = matching_triples
464 .into_iter()
465 .filter(|triple| pattern.matches(triple))
466 .collect();
467
468 let deleted_count = filtered.len();
469
470 for triple in &filtered {
472 self.mvcc_store
473 .delete(tx_id, triple)
474 .map_err(|e| OxirsError::Store(format!("Failed to delete triple: {e}")))?;
475 }
476
477 self.mvcc_store
478 .commit_transaction(tx_id)
479 .map_err(|e| OxirsError::Store(format!("Failed to commit transaction: {e}")))?;
480
481 let mut stats = self.stats.write();
483 stats.total_triples = stats.total_triples.saturating_sub(deleted_count as u64);
484 stats.tier_stats.hot.triple_count = stats
485 .tier_stats
486 .hot
487 .triple_count
488 .saturating_sub(deleted_count as u64);
489
490 Ok(deleted_count)
491 }
492
493 async fn stats(&self) -> Result<StorageStats, OxirsError> {
494 let stats = self.stats.read();
495 Ok(stats.clone())
496 }
497
498 async fn optimize(&self) -> Result<(), OxirsError> {
499 self.mvcc_store
501 .garbage_collect()
502 .map_err(|e| OxirsError::Store(format!("Failed to optimize storage: {e}")))?;
503 Ok(())
504 }
505
506 async fn backup(&self, path: &Path) -> Result<(), OxirsError> {
507 let backup_path = path.join("oxirs_backup.json");
509
510 let all_pattern = crate::model::TriplePattern::new(None, None, None);
512 let triples = self.query_triples(&all_pattern).await?;
513
514 let serialized = serde_json::to_string_pretty(&triples)
515 .map_err(|e| OxirsError::Store(format!("Failed to serialize backup: {e}")))?;
516
517 std::fs::write(&backup_path, serialized)
518 .map_err(|e| OxirsError::Store(format!("Failed to write backup: {e}")))?;
519
520 Ok(())
521 }
522
523 async fn restore(&self, path: &Path) -> Result<(), OxirsError> {
524 let backup_path = path.join("oxirs_backup.json");
526
527 let serialized = std::fs::read_to_string(&backup_path)
528 .map_err(|e| OxirsError::Store(format!("Failed to read backup: {e}")))?;
529
530 let triples: Vec<crate::model::Triple> = serde_json::from_str(&serialized)
531 .map_err(|e| OxirsError::Store(format!("Failed to deserialize backup: {e}")))?;
532
533 self.store_triples(&triples).await?;
534
535 Ok(())
536 }
537}
538
539impl SimpleStorageEngine {
540 fn pattern_to_subject(
542 pattern: Option<&crate::model::pattern::SubjectPattern>,
543 ) -> Option<crate::model::Subject> {
544 pattern?.try_into().ok()
545 }
546
547 fn pattern_to_predicate(
549 pattern: Option<&crate::model::pattern::PredicatePattern>,
550 ) -> Option<crate::model::Predicate> {
551 pattern?.try_into().ok()
552 }
553
554 fn pattern_to_object(
556 pattern: Option<&crate::model::pattern::ObjectPattern>,
557 ) -> Option<crate::model::Object> {
558 pattern?.try_into().ok()
559 }
560}
561
562#[cfg(test)]
563mod tests {
564 use super::*;
565
566 #[test]
567 fn test_default_config() {
568 let config = StorageConfig::default();
569 assert!(config.enable_tiering);
570 assert!(config.enable_columnar);
571 assert!(config.enable_temporal);
572 assert_eq!(config.cache_size_mb, 1024);
573 }
574}