Skip to main content

oxirs_stream/event_sourcing/
store.rs

1//! EventStore, EventIndexes, PersistenceManager implementations and the
2//! `EventMetadataAccessor` helper trait.
3
4use super::{
5    EventQuery, EventSnapshot, EventSourcingStats, EventStoreConfig, EventStoreTrait,
6    PersistenceBackend, PersistenceOperation, PersistenceStats, PersistenceStatus, QueryOrder,
7    SnapshotMetadata, StorageMetadata, StoredEvent, TimeRange,
8};
9use crate::{EventMetadata, StreamEvent};
10use anyhow::Result;
11use chrono::{DateTime, Utc};
12use serde::de::DeserializeOwned;
13use std::collections::VecDeque;
14use std::collections::{BTreeMap, HashMap, HashSet};
15use std::path::Path;
16use std::sync::atomic::{AtomicU64, Ordering};
17use std::sync::Arc;
18use std::time::Instant;
19use tokio::io::AsyncWriteExt;
20use tokio::sync::{Mutex, RwLock, Semaphore};
21use tracing::{debug, error, info, warn};
22use uuid::Uuid;
23
24/// Main event store implementation
25pub struct EventStore {
26    /// Configuration
27    config: EventStoreConfig,
28    /// In-memory event storage
29    memory_events: Arc<RwLock<BTreeMap<u64, StoredEvent>>>,
30    /// Stream version tracking
31    stream_versions: Arc<RwLock<HashMap<String, u64>>>,
32    /// Next sequence number
33    next_sequence: Arc<AtomicU64>,
34    /// Event indexes
35    indexes: Arc<EventIndexes>,
36    /// Snapshots
37    snapshots: Arc<RwLock<HashMap<String, Vec<EventSnapshot>>>>,
38    /// Persistence manager
39    persistence_manager: Arc<PersistenceManager>,
40    /// Statistics
41    stats: Arc<EventSourcingStats>,
42    /// Operation semaphore
43    operation_semaphore: Arc<Semaphore>,
44}
45
46/// Event indexes for efficient querying
47pub struct EventIndexes {
48    /// Index by event type
49    by_event_type: RwLock<HashMap<String, Vec<u64>>>,
50    /// Index by timestamp
51    by_timestamp: RwLock<BTreeMap<DateTime<Utc>, Vec<u64>>>,
52    /// Index by source
53    by_source: RwLock<HashMap<String, Vec<u64>>>,
54    /// Index by stream ID
55    by_stream: RwLock<HashMap<String, Vec<u64>>>,
56    /// Custom indexes
57    custom_indexes: RwLock<HashMap<String, HashMap<String, Vec<u64>>>>,
58}
59
60/// Persistence manager for durable storage
61pub struct PersistenceManager {
62    /// Backend configuration
63    backend: PersistenceBackend,
64    /// Pending operations queue
65    pending_operations: Arc<Mutex<VecDeque<PersistenceOperation>>>,
66    /// Persistence statistics
67    pub(crate) stats: Arc<PersistenceStats>,
68}
69
70impl EventStore {
71    /// Create a new event store.
72    ///
73    /// This is fallible because construction may need to create the on-disk
74    /// persistence directory (fail-fast if that isn't possible) and loads any
75    /// previously persisted events/snapshots back into memory ("load-on-open")
76    /// so a restart never silently drops durable history.
77    pub async fn new(config: EventStoreConfig) -> Result<Self> {
78        let persistence_manager =
79            Arc::new(PersistenceManager::new(config.persistence_backend.clone())?);
80
81        let memory_events = Arc::new(RwLock::new(BTreeMap::new()));
82        let stream_versions = Arc::new(RwLock::new(HashMap::new()));
83        let indexes = Arc::new(EventIndexes::new());
84        let snapshots = Arc::new(RwLock::new(HashMap::new()));
85        let next_sequence = Arc::new(AtomicU64::new(1));
86
87        // Load-on-open: recover previously persisted events so eviction never
88        // has to guess whether history already made it to durable storage.
89        let loaded_events = persistence_manager.load_persisted_events().await?;
90        let mut max_sequence = 0u64;
91        {
92            let mut mem = memory_events.write().await;
93            let mut versions = stream_versions.write().await;
94            for mut event in loaded_events {
95                // These records came from durable storage, so they are safe to evict again.
96                event.storage_metadata.persistence_status = PersistenceStatus::Persisted;
97                max_sequence = max_sequence.max(event.sequence_number);
98                let version_slot = versions.entry(event.stream_id.clone()).or_insert(0);
99                if event.stream_version > *version_slot {
100                    *version_slot = event.stream_version;
101                }
102                indexes.add_event(&event).await?;
103                mem.insert(event.sequence_number, event);
104            }
105        }
106        next_sequence.store(max_sequence + 1, Ordering::SeqCst);
107
108        let loaded_snapshots = persistence_manager.load_persisted_snapshots().await?;
109        {
110            let mut snapshot_map = snapshots.write().await;
111            for snapshot in loaded_snapshots {
112                snapshot_map
113                    .entry(snapshot.stream_id.clone())
114                    .or_insert_with(Vec::new)
115                    .push(snapshot);
116            }
117            for stream_snapshots in snapshot_map.values_mut() {
118                stream_snapshots.sort_by_key(|s| s.stream_version);
119            }
120        }
121
122        Ok(Self {
123            config,
124            memory_events,
125            stream_versions,
126            next_sequence,
127            indexes,
128            snapshots,
129            persistence_manager,
130            stats: Arc::new(EventSourcingStats::default()),
131            operation_semaphore: Arc::new(Semaphore::new(1000)), // Max 1000 concurrent operations
132        })
133    }
134
135    /// Store an event in the event store
136    pub async fn store_event(&self, stream_id: String, event: StreamEvent) -> Result<StoredEvent> {
137        let _permit = self.operation_semaphore.acquire().await?;
138        let start_time = Instant::now();
139
140        // Generate sequence number and stream version
141        let sequence_number = self.next_sequence.fetch_add(1, Ordering::SeqCst);
142        let stream_version = {
143            let mut versions = self.stream_versions.write().await;
144            let version = versions.get(&stream_id).unwrap_or(&0) + 1;
145            versions.insert(stream_id.clone(), version);
146            version
147        };
148
149        // Create stored event
150        let checksum = self.calculate_checksum(&event)?;
151        let original_size = self.estimate_size(&event);
152        let mut stored_event = StoredEvent {
153            event_id: Uuid::new_v4(),
154            sequence_number,
155            stream_id: stream_id.clone(),
156            stream_version,
157            event_data: event,
158            stored_at: Utc::now(),
159            storage_metadata: StorageMetadata {
160                checksum,
161                compressed_size: None,
162                original_size,
163                storage_location: format!("memory:{sequence_number}"),
164                persistence_status: PersistenceStatus::InMemory,
165            },
166        };
167
168        // Store in memory
169        {
170            let mut memory_events = self.memory_events.write().await;
171            memory_events.insert(sequence_number, stored_event.clone());
172        }
173
174        // Update indexes
175        self.indexes.add_event(&stored_event).await?;
176
177        // Persist synchronously (not just queued) so we know the true durability
178        // status of this event before it can ever be considered for eviction.
179        if self.config.enable_persistence && self.persistence_manager.is_durable() {
180            match self.persistence_manager.persist_event(&stored_event).await {
181                Ok(()) => {
182                    stored_event.storage_metadata.persistence_status = PersistenceStatus::Persisted;
183                    self.stats
184                        .persistence_operations
185                        .fetch_add(1, Ordering::Relaxed);
186                    let mut memory_events = self.memory_events.write().await;
187                    if let Some(entry) = memory_events.get_mut(&sequence_number) {
188                        entry.storage_metadata.persistence_status = PersistenceStatus::Persisted;
189                    }
190                }
191                Err(e) => {
192                    error!(
193                        "Failed to persist event {} for stream {}: {}",
194                        stored_event.event_id, stream_id, e
195                    );
196                    stored_event.storage_metadata.persistence_status = PersistenceStatus::Failed {
197                        error: e.to_string(),
198                    };
199                    self.stats.failed_operations.fetch_add(1, Ordering::Relaxed);
200                    let mut memory_events = self.memory_events.write().await;
201                    if let Some(entry) = memory_events.get_mut(&sequence_number) {
202                        entry.storage_metadata.persistence_status = PersistenceStatus::Failed {
203                            error: e.to_string(),
204                        };
205                    }
206                }
207            }
208        }
209
210        // Evict old events, but only ones that are safe to lose from memory:
211        // either persistence isn't expected for this store (Memory backend or
212        // persistence disabled), or the event has actually made it to durable
213        // storage. Events still pending/failed persistence are never evicted,
214        // so "persisted" eviction never silently discards undurable data.
215        {
216            let mut memory_events = self.memory_events.write().await;
217            if memory_events.len() > self.config.max_memory_events {
218                let overflow = memory_events.len() - self.config.max_memory_events;
219                let evictable: Vec<u64> = memory_events
220                    .iter()
221                    .filter(|(_, event)| self.can_evict(event))
222                    .map(|(seq, _)| *seq)
223                    .take(overflow)
224                    .collect();
225
226                for seq in evictable {
227                    memory_events.remove(&seq);
228                }
229            }
230        }
231
232        // Check if snapshot is needed
233        if self.config.snapshot_config.enable_snapshots
234            && stream_version % self.config.snapshot_config.snapshot_interval as u64 == 0
235        {
236            self.create_snapshot(&stream_id, stream_version).await?;
237        }
238
239        // Update statistics
240        self.stats
241            .total_events_stored
242            .fetch_add(1, Ordering::Relaxed);
243        let store_latency = start_time.elapsed();
244        self.stats
245            .average_store_latency_ms
246            .store(store_latency.as_millis() as u64, Ordering::Relaxed);
247
248        info!(
249            "Stored event {} for stream {} (seq: {}, version: {})",
250            stored_event.event_id, stream_id, sequence_number, stream_version
251        );
252
253        Ok(stored_event)
254    }
255
256    /// Retrieve events by query
257    pub async fn query_events(&self, query: EventQuery) -> Result<Vec<StoredEvent>> {
258        let _permit = self.operation_semaphore.acquire().await?;
259        let start_time = Instant::now();
260
261        let candidate_sequences = self.indexes.find_matching_sequences(&query).await?;
262        let mut results = Vec::new();
263
264        let memory_events = self.memory_events.read().await;
265        for &sequence in &candidate_sequences {
266            if let Some(stored_event) = memory_events.get(&sequence) {
267                if self.matches_query(stored_event, &query) {
268                    results.push(stored_event.clone());
269
270                    if let Some(limit) = query.limit {
271                        if results.len() >= limit {
272                            break;
273                        }
274                    }
275                }
276            }
277        }
278
279        // Sort results based on query order
280        self.sort_results(&mut results, &query.order);
281
282        // Update statistics
283        self.stats
284            .total_events_retrieved
285            .fetch_add(results.len() as u64, Ordering::Relaxed);
286        let retrieve_latency = start_time.elapsed();
287        self.stats
288            .average_retrieve_latency_ms
289            .store(retrieve_latency.as_millis() as u64, Ordering::Relaxed);
290
291        debug!(
292            "Query returned {} events in {:?}",
293            results.len(),
294            retrieve_latency
295        );
296
297        Ok(results)
298    }
299
300    /// Get events for a specific stream
301    pub async fn get_stream_events(
302        &self,
303        stream_id: &str,
304        from_version: Option<u64>,
305    ) -> Result<Vec<StoredEvent>> {
306        let query = EventQuery {
307            stream_id: Some(stream_id.to_string()),
308            event_types: None,
309            time_range: None,
310            sequence_range: None,
311            source: None,
312            custom_filters: HashMap::new(),
313            limit: None,
314            order: QueryOrder::SequenceAsc,
315        };
316
317        let mut events = self.query_events(query).await?;
318
319        if let Some(from_version) = from_version {
320            events.retain(|e| e.stream_version >= from_version);
321        }
322
323        Ok(events)
324    }
325
326    /// Replay events from a specific point in time
327    pub async fn replay_from_timestamp(
328        &self,
329        timestamp: DateTime<Utc>,
330    ) -> Result<Vec<StoredEvent>> {
331        let query = EventQuery {
332            stream_id: None,
333            event_types: None,
334            time_range: Some(TimeRange {
335                start: timestamp,
336                end: Utc::now(),
337            }),
338            sequence_range: None,
339            source: None,
340            custom_filters: HashMap::new(),
341            limit: None,
342            order: QueryOrder::SequenceAsc,
343        };
344
345        self.query_events(query).await
346    }
347
348    /// Create a snapshot for a stream
349    async fn create_snapshot(&self, stream_id: &str, stream_version: u64) -> Result<EventSnapshot> {
350        let events = self.get_stream_events(stream_id, None).await?;
351
352        // Aggregate state from events (simplified)
353        let state_data = self.aggregate_events(&events)?;
354        let compressed_data = self.compress_data(&state_data)?;
355
356        let snapshot = EventSnapshot {
357            snapshot_id: Uuid::new_v4(),
358            stream_id: stream_id.to_string(),
359            stream_version,
360            sequence_number: events.last().map(|e| e.sequence_number).unwrap_or(0),
361            created_at: Utc::now(),
362            state_data: compressed_data.clone(),
363            metadata: SnapshotMetadata {
364                compression: Some("gzip".to_string()),
365                original_size: state_data.len(),
366                compressed_size: compressed_data.len(),
367                checksum: self.calculate_data_checksum(&compressed_data)?,
368            },
369        };
370
371        // Store snapshot
372        {
373            let mut snapshots = self.snapshots.write().await;
374            let stream_snapshots = snapshots
375                .entry(stream_id.to_string())
376                .or_insert_with(Vec::new);
377            stream_snapshots.push(snapshot.clone());
378
379            // Keep only recent snapshots
380            if stream_snapshots.len() > self.config.snapshot_config.max_snapshots {
381                stream_snapshots.remove(0);
382            }
383        }
384
385        // Persist the snapshot synchronously; a snapshot write failure is a real
386        // error the caller needs to know about rather than a silently dropped write.
387        if self.config.enable_persistence && self.persistence_manager.is_durable() {
388            self.persistence_manager
389                .persist_snapshot(&snapshot)
390                .await
391                .map_err(|e| {
392                    anyhow::anyhow!("Failed to persist snapshot for stream {stream_id}: {e}")
393                })?;
394        }
395
396        self.stats.snapshots_created.fetch_add(1, Ordering::Relaxed);
397        info!(
398            "Created snapshot {} for stream {} at version {}",
399            snapshot.snapshot_id, stream_id, stream_version
400        );
401
402        Ok(snapshot)
403    }
404
405    /// Get the latest snapshot for a stream
406    pub async fn get_latest_snapshot(&self, stream_id: &str) -> Result<Option<EventSnapshot>> {
407        let snapshots = self.snapshots.read().await;
408        if let Some(stream_snapshots) = snapshots.get(stream_id) {
409            Ok(stream_snapshots.last().cloned())
410        } else {
411            Ok(None)
412        }
413    }
414
415    /// Rebuild stream state from events and snapshots
416    pub async fn rebuild_stream_state(&self, stream_id: &str) -> Result<Vec<u8>> {
417        // Get latest snapshot
418        if let Some(snapshot) = self.get_latest_snapshot(stream_id).await? {
419            // Get events after snapshot
420            let events = self
421                .get_stream_events(stream_id, Some(snapshot.stream_version + 1))
422                .await?;
423
424            // Start with snapshot state
425            let mut state = self.decompress_data(&snapshot.state_data)?;
426
427            // Apply subsequent events
428            for event in events {
429                state = self.apply_event_to_state(state, &event.event_data)?;
430            }
431
432            Ok(state)
433        } else {
434            // No snapshot, rebuild from all events
435            let events = self.get_stream_events(stream_id, None).await?;
436            let aggregated = self.aggregate_events(&events)?;
437            Ok(aggregated)
438        }
439    }
440
441    /// Whether an in-memory event is safe to evict: either this store never
442    /// promised durability for it (persistence disabled, or a non-durable
443    /// backend such as `Memory`), or it has actually been written to durable
444    /// storage. Events that are still pending or failed persistence are kept
445    /// in memory so eviction can never silently discard undurable data.
446    fn can_evict(&self, event: &StoredEvent) -> bool {
447        if !self.config.enable_persistence || !self.persistence_manager.is_durable() {
448            return true;
449        }
450        matches!(
451            event.storage_metadata.persistence_status,
452            PersistenceStatus::Persisted | PersistenceStatus::Archived
453        )
454    }
455
456    /// Check if an event matches the query criteria
457    fn matches_query(&self, event: &StoredEvent, query: &EventQuery) -> bool {
458        // Stream ID filter
459        if let Some(ref stream_id) = query.stream_id {
460            if &event.stream_id != stream_id {
461                return false;
462            }
463        }
464
465        // Event type filter
466        if let Some(ref event_types) = query.event_types {
467            let event_type = format!("{:?}", std::mem::discriminant(&event.event_data));
468            if !event_types.contains(&event_type) {
469                return false;
470            }
471        }
472
473        // Time range filter
474        if let Some(ref time_range) = query.time_range {
475            let event_time = event.event_data.metadata().timestamp;
476            if event_time < time_range.start || event_time > time_range.end {
477                return false;
478            }
479        }
480
481        // Sequence range filter
482        if let Some(ref seq_range) = query.sequence_range {
483            if event.sequence_number < seq_range.start || event.sequence_number > seq_range.end {
484                return false;
485            }
486        }
487
488        // Source filter
489        if let Some(ref source) = query.source {
490            if &event.event_data.metadata().source != source {
491                return false;
492            }
493        }
494
495        true
496    }
497
498    /// Sort results based on query order
499    fn sort_results(&self, results: &mut [StoredEvent], order: &QueryOrder) {
500        match order {
501            QueryOrder::SequenceAsc => {
502                results.sort_by_key(|e| e.sequence_number);
503            }
504            QueryOrder::SequenceDesc => {
505                results.sort_by_key(|e| std::cmp::Reverse(e.sequence_number));
506            }
507            QueryOrder::TimestampAsc => {
508                results.sort_by_key(|e| e.event_data.metadata().timestamp);
509            }
510            QueryOrder::TimestampDesc => {
511                results.sort_by_key(|e| std::cmp::Reverse(e.event_data.metadata().timestamp));
512            }
513        }
514    }
515
516    /// Calculate checksum for event
517    fn calculate_checksum(&self, event: &StreamEvent) -> Result<String> {
518        let serialized = serde_json::to_string(event)?;
519        Ok(format!("{:x}", crc32fast::hash(serialized.as_bytes())))
520    }
521
522    /// Calculate checksum for data
523    fn calculate_data_checksum(&self, data: &[u8]) -> Result<String> {
524        Ok(format!("{:x}", crc32fast::hash(data)))
525    }
526
527    /// Estimate size of an event
528    fn estimate_size(&self, event: &StreamEvent) -> usize {
529        serde_json::to_string(event)
530            .map(|s| s.len())
531            .unwrap_or(1024)
532    }
533
534    /// Aggregate events into state data
535    fn aggregate_events(&self, events: &[StoredEvent]) -> Result<Vec<u8>> {
536        // Simplified aggregation - in real implementation, this would be domain-specific
537        let aggregate = format!("Aggregated {} events", events.len());
538        Ok(aggregate.into_bytes())
539    }
540
541    /// Apply an event to existing state
542    fn apply_event_to_state(&self, mut state: Vec<u8>, _event: &StreamEvent) -> Result<Vec<u8>> {
543        // Simplified state application
544        state.extend_from_slice(b" +event");
545        Ok(state)
546    }
547
548    /// Compress data
549    fn compress_data(&self, data: &[u8]) -> Result<Vec<u8>> {
550        if self.config.enable_compression {
551            oxiarc_deflate::gzip_compress(data, 6)
552                .map_err(|e| anyhow::anyhow!("Gzip compression failed: {e}"))
553        } else {
554            Ok(data.to_vec())
555        }
556    }
557
558    /// Decompress data
559    fn decompress_data(&self, data: &[u8]) -> Result<Vec<u8>> {
560        if self.config.enable_compression {
561            oxiarc_deflate::gzip_decompress(data)
562                .map_err(|e| anyhow::anyhow!("Gzip decompression failed: {e}"))
563        } else {
564            Ok(data.to_vec())
565        }
566    }
567
568    /// Get event sourcing statistics
569    pub fn get_stats(&self) -> super::EventSourcingStats {
570        super::EventSourcingStats {
571            total_events_stored: AtomicU64::new(
572                self.stats.total_events_stored.load(Ordering::Relaxed),
573            ),
574            total_events_retrieved: AtomicU64::new(
575                self.stats.total_events_retrieved.load(Ordering::Relaxed),
576            ),
577            snapshots_created: AtomicU64::new(self.stats.snapshots_created.load(Ordering::Relaxed)),
578            events_archived: AtomicU64::new(self.stats.events_archived.load(Ordering::Relaxed)),
579            persistence_operations: AtomicU64::new(
580                self.stats.persistence_operations.load(Ordering::Relaxed),
581            ),
582            failed_operations: AtomicU64::new(self.stats.failed_operations.load(Ordering::Relaxed)),
583            memory_usage_bytes: AtomicU64::new(
584                self.stats.memory_usage_bytes.load(Ordering::Relaxed),
585            ),
586            disk_usage_bytes: AtomicU64::new(self.stats.disk_usage_bytes.load(Ordering::Relaxed)),
587            average_store_latency_ms: AtomicU64::new(
588                self.stats.average_store_latency_ms.load(Ordering::Relaxed),
589            ),
590            average_retrieve_latency_ms: AtomicU64::new(
591                self.stats
592                    .average_retrieve_latency_ms
593                    .load(Ordering::Relaxed),
594            ),
595        }
596    }
597}
598
599/// Implement the EventStoreTrait for the concrete EventStore
600#[async_trait::async_trait]
601impl EventStoreTrait for EventStore {
602    async fn store_event(&self, stream_id: String, event: StreamEvent) -> Result<StoredEvent> {
603        self.store_event(stream_id, event).await
604    }
605
606    async fn query_events(&self, query: EventQuery) -> Result<Vec<StoredEvent>> {
607        self.query_events(query).await
608    }
609
610    async fn get_stream_events(
611        &self,
612        stream_id: &str,
613        from_version: Option<u64>,
614    ) -> Result<Vec<StoredEvent>> {
615        self.get_stream_events(stream_id, from_version).await
616    }
617
618    async fn replay_from_timestamp(&self, timestamp: DateTime<Utc>) -> Result<Vec<StoredEvent>> {
619        self.replay_from_timestamp(timestamp).await
620    }
621
622    async fn get_latest_snapshot(&self, stream_id: &str) -> Result<Option<EventSnapshot>> {
623        self.get_latest_snapshot(stream_id).await
624    }
625
626    async fn rebuild_stream_state(&self, stream_id: &str) -> Result<Vec<u8>> {
627        self.rebuild_stream_state(stream_id).await
628    }
629
630    async fn append_events(
631        &self,
632        aggregate_id: &str,
633        events: &[StreamEvent],
634        _expected_version: Option<u64>,
635    ) -> Result<u64> {
636        let mut last_version = 0u64;
637        for event in events {
638            let stored_event = self
639                .store_event(aggregate_id.to_string(), event.clone())
640                .await?;
641            last_version = stored_event.stream_version;
642        }
643        Ok(last_version)
644    }
645}
646
647impl Default for EventIndexes {
648    fn default() -> Self {
649        Self::new()
650    }
651}
652
653impl EventIndexes {
654    /// Create new event indexes
655    pub fn new() -> Self {
656        Self {
657            by_event_type: RwLock::new(HashMap::new()),
658            by_timestamp: RwLock::new(BTreeMap::new()),
659            by_source: RwLock::new(HashMap::new()),
660            by_stream: RwLock::new(HashMap::new()),
661            custom_indexes: RwLock::new(HashMap::new()),
662        }
663    }
664
665    /// Add an event to indexes
666    pub async fn add_event(&self, event: &StoredEvent) -> Result<()> {
667        let sequence = event.sequence_number;
668
669        // Index by event type
670        {
671            let mut by_type = self.by_event_type.write().await;
672            let event_type = format!("{:?}", std::mem::discriminant(&event.event_data));
673            by_type
674                .entry(event_type)
675                .or_insert_with(Vec::new)
676                .push(sequence);
677        }
678
679        // Index by timestamp
680        {
681            let mut by_timestamp = self.by_timestamp.write().await;
682            let timestamp = event.event_data.metadata().timestamp;
683            by_timestamp
684                .entry(timestamp)
685                .or_insert_with(Vec::new)
686                .push(sequence);
687        }
688
689        // Index by source
690        {
691            let mut by_source = self.by_source.write().await;
692            let source = &event.event_data.metadata().source;
693            by_source
694                .entry(source.clone())
695                .or_insert_with(Vec::new)
696                .push(sequence);
697        }
698
699        // Index by stream
700        {
701            let mut by_stream = self.by_stream.write().await;
702            by_stream
703                .entry(event.stream_id.clone())
704                .or_insert_with(Vec::new)
705                .push(sequence);
706        }
707
708        Ok(())
709    }
710
711    /// Find sequences matching query criteria
712    pub async fn find_matching_sequences(&self, query: &EventQuery) -> Result<Vec<u64>> {
713        let mut candidate_sequences = Vec::new();
714
715        // Start with stream filter if specified
716        if let Some(ref stream_id) = query.stream_id {
717            let by_stream = self.by_stream.read().await;
718            if let Some(sequences) = by_stream.get(stream_id) {
719                candidate_sequences = sequences.clone();
720            } else {
721                return Ok(Vec::new()); // Stream not found
722            }
723        } else {
724            // Get all sequences (this could be optimized)
725            let by_stream = self.by_stream.read().await;
726            for sequences in by_stream.values() {
727                candidate_sequences.extend(sequences);
728            }
729        }
730
731        // Apply other filters
732        if let Some(ref event_types) = query.event_types {
733            let by_type = self.by_event_type.read().await;
734            let mut type_sequences: HashSet<u64> = HashSet::new();
735
736            for event_type in event_types {
737                if let Some(sequences) = by_type.get(event_type) {
738                    type_sequences.extend(sequences);
739                }
740            }
741
742            candidate_sequences.retain(|seq| type_sequences.contains(seq));
743        }
744
745        // Apply sequence range filter
746        if let Some(ref seq_range) = query.sequence_range {
747            candidate_sequences.retain(|&seq| seq >= seq_range.start && seq <= seq_range.end);
748        }
749
750        candidate_sequences.sort_unstable();
751        Ok(candidate_sequences)
752    }
753}
754
755impl PersistenceManager {
756    /// Create new persistence manager.
757    ///
758    /// For the `FileSystem` backend this eagerly creates the base directory so
759    /// construction fails fast (rather than failing later, silently, on the
760    /// first write) if the path is unusable.
761    pub fn new(backend: PersistenceBackend) -> Result<Self> {
762        if let PersistenceBackend::FileSystem { base_path } = &backend {
763            std::fs::create_dir_all(base_path).map_err(|e| {
764                anyhow::anyhow!(
765                    "Failed to create event store persistence directory '{base_path}': {e}"
766                )
767            })?;
768        }
769
770        Ok(Self {
771            backend,
772            pending_operations: Arc::new(Mutex::new(VecDeque::new())),
773            stats: Arc::new(PersistenceStats::default()),
774        })
775    }
776
777    /// Whether this backend actually durably persists data. `Memory` is an
778    /// explicit "no persistence" backend, so events stored under it were never
779    /// expected to survive a restart or be excluded from eviction.
780    pub fn is_durable(&self) -> bool {
781        matches!(self.backend, PersistenceBackend::FileSystem { .. })
782    }
783
784    /// Persist a single event immediately (append-only JSON-lines write with
785    /// an fsync before returning), so the caller knows for certain whether the
786    /// event actually reached durable storage.
787    pub async fn persist_event(&self, event: &StoredEvent) -> Result<()> {
788        match &self.backend {
789            PersistenceBackend::Memory => Ok(()),
790            PersistenceBackend::FileSystem { base_path } => {
791                let line = serde_json::to_string(event)?;
792                self.append_jsonl(base_path, "events.jsonl", line).await
793            }
794            PersistenceBackend::Database { .. } | PersistenceBackend::ObjectStorage { .. } => {
795                Err(anyhow::anyhow!(
796                    "Persistence backend {:?} is not implemented; use FileSystem or Memory",
797                    self.backend
798                ))
799            }
800        }
801    }
802
803    /// Persist a snapshot immediately (append-only JSON-lines write with an
804    /// fsync before returning).
805    pub async fn persist_snapshot(&self, snapshot: &EventSnapshot) -> Result<()> {
806        match &self.backend {
807            PersistenceBackend::Memory => Ok(()),
808            PersistenceBackend::FileSystem { base_path } => {
809                let line = serde_json::to_string(snapshot)?;
810                self.append_jsonl(base_path, "snapshots.jsonl", line).await
811            }
812            PersistenceBackend::Database { .. } | PersistenceBackend::ObjectStorage { .. } => {
813                Err(anyhow::anyhow!(
814                    "Persistence backend {:?} is not implemented; use FileSystem or Memory",
815                    self.backend
816                ))
817            }
818        }
819    }
820
821    /// Load all previously persisted events (load-on-open recovery).
822    pub async fn load_persisted_events(&self) -> Result<Vec<StoredEvent>> {
823        match &self.backend {
824            PersistenceBackend::FileSystem { base_path } => {
825                let path = Path::new(base_path).join("events.jsonl");
826                Self::read_jsonl(&path).await
827            }
828            _ => Ok(Vec::new()),
829        }
830    }
831
832    /// Load all previously persisted snapshots (load-on-open recovery).
833    pub async fn load_persisted_snapshots(&self) -> Result<Vec<EventSnapshot>> {
834        match &self.backend {
835            PersistenceBackend::FileSystem { base_path } => {
836                let path = Path::new(base_path).join("snapshots.jsonl");
837                Self::read_jsonl(&path).await
838            }
839            _ => Ok(Vec::new()),
840        }
841    }
842
843    /// Append one JSON-line record to `base_path/file_name`, fsync-ing before
844    /// returning so a successful result is a durability guarantee.
845    async fn append_jsonl(&self, base_path: &str, file_name: &str, line: String) -> Result<()> {
846        let path = Path::new(base_path).join(file_name);
847        let mut file = tokio::fs::OpenOptions::new()
848            .create(true)
849            .append(true)
850            .open(&path)
851            .await
852            .map_err(|e| {
853                anyhow::anyhow!("Failed to open persistence file '{}': {e}", path.display())
854            })?;
855        file.write_all(line.as_bytes()).await.map_err(|e| {
856            anyhow::anyhow!(
857                "Failed to write to persistence file '{}': {e}",
858                path.display()
859            )
860        })?;
861        file.write_all(b"\n").await.map_err(|e| {
862            anyhow::anyhow!(
863                "Failed to write to persistence file '{}': {e}",
864                path.display()
865            )
866        })?;
867        file.flush()
868            .await
869            .map_err(|e| anyhow::anyhow!("Failed to flush '{}': {e}", path.display()))?;
870        file.sync_data()
871            .await
872            .map_err(|e| anyhow::anyhow!("fsync failed for '{}': {e}", path.display()))?;
873
874        self.stats
875            .bytes_written
876            .fetch_add(line.len() as u64 + 1, Ordering::Relaxed);
877        Ok(())
878    }
879
880    /// Read and parse a JSON-lines file, skipping (and logging) any corrupt
881    /// records instead of failing the whole load.
882    async fn read_jsonl<T: DeserializeOwned>(path: &Path) -> Result<Vec<T>> {
883        if !tokio::fs::try_exists(path).await.unwrap_or(false) {
884            return Ok(Vec::new());
885        }
886
887        let content = tokio::fs::read_to_string(path)
888            .await
889            .map_err(|e| anyhow::anyhow!("Failed to read '{}': {e}", path.display()))?;
890
891        let mut items = Vec::new();
892        for (line_no, line) in content.lines().enumerate() {
893            let line = line.trim();
894            if line.is_empty() {
895                continue;
896            }
897            match serde_json::from_str::<T>(line) {
898                Ok(item) => items.push(item),
899                Err(e) => warn!(
900                    "Skipping corrupt persisted record at {}:{}: {e}",
901                    path.display(),
902                    line_no + 1
903                ),
904            }
905        }
906        Ok(items)
907    }
908
909    /// Queue a persistence operation for later batch processing (used for
910    /// archival/deletion bookkeeping; regular event/snapshot writes go through
911    /// [`Self::persist_event`]/[`Self::persist_snapshot`] synchronously).
912    pub async fn queue_operation(&self, operation: PersistenceOperation) -> Result<()> {
913        let mut queue = self.pending_operations.lock().await;
914        queue.push_back(operation);
915        self.stats.operations_queued.fetch_add(1, Ordering::Relaxed);
916        Ok(())
917    }
918
919    /// Process pending persistence operations
920    pub async fn process_pending_operations(&self) -> Result<()> {
921        let operations: Vec<PersistenceOperation> = {
922            let mut queue = self.pending_operations.lock().await;
923            queue.drain(..).collect()
924        };
925
926        for operation in operations {
927            match self.execute_operation(operation).await {
928                Ok(_) => {
929                    self.stats
930                        .operations_completed
931                        .fetch_add(1, Ordering::Relaxed);
932                }
933                Err(e) => {
934                    self.stats.operations_failed.fetch_add(1, Ordering::Relaxed);
935                    error!("Persistence operation failed: {}", e);
936                }
937            }
938        }
939
940        Ok(())
941    }
942
943    /// Execute a single queued persistence operation
944    async fn execute_operation(&self, operation: PersistenceOperation) -> Result<()> {
945        match &self.backend {
946            PersistenceBackend::Memory => Ok(()),
947            PersistenceBackend::FileSystem { base_path } => {
948                self.execute_filesystem_operation(operation, base_path)
949                    .await
950            }
951            PersistenceBackend::Database { .. } | PersistenceBackend::ObjectStorage { .. } => {
952                Err(anyhow::anyhow!(
953                    "Persistence backend {:?} is not implemented; use FileSystem or Memory",
954                    self.backend
955                ))
956            }
957        }
958    }
959
960    /// Execute filesystem persistence operation
961    async fn execute_filesystem_operation(
962        &self,
963        operation: PersistenceOperation,
964        base_path: &str,
965    ) -> Result<()> {
966        match operation {
967            PersistenceOperation::StoreEvent(event) => {
968                let line = serde_json::to_string(&*event)?;
969                self.append_jsonl(base_path, "events.jsonl", line).await?;
970            }
971            PersistenceOperation::StoreSnapshot(snapshot) => {
972                let line = serde_json::to_string(&snapshot)?;
973                self.append_jsonl(base_path, "snapshots.jsonl", line)
974                    .await?;
975            }
976            PersistenceOperation::ArchiveEvents(events) => {
977                for event in &events {
978                    let line = serde_json::to_string(event)?;
979                    self.append_jsonl(base_path, "archive.jsonl", line).await?;
980                }
981            }
982            PersistenceOperation::DeleteEvents(sequence_numbers) => {
983                let line = serde_json::to_string(&sequence_numbers)?;
984                self.append_jsonl(base_path, "deletions.jsonl", line)
985                    .await?;
986            }
987        }
988        Ok(())
989    }
990}
991
992// Helper trait for accessing metadata
993pub trait EventMetadataAccessor {
994    fn metadata(&self) -> &EventMetadata;
995}
996
997impl EventMetadataAccessor for StreamEvent {
998    fn metadata(&self) -> &EventMetadata {
999        match self {
1000            StreamEvent::TripleAdded { metadata, .. } => metadata,
1001            StreamEvent::TripleRemoved { metadata, .. } => metadata,
1002            StreamEvent::QuadAdded { metadata, .. } => metadata,
1003            StreamEvent::QuadRemoved { metadata, .. } => metadata,
1004            StreamEvent::GraphCreated { metadata, .. } => metadata,
1005            StreamEvent::GraphCleared { metadata, .. } => metadata,
1006            StreamEvent::GraphDeleted { metadata, .. } => metadata,
1007            StreamEvent::SparqlUpdate { metadata, .. } => metadata,
1008            StreamEvent::TransactionBegin { metadata, .. } => metadata,
1009            StreamEvent::TransactionCommit { metadata, .. } => metadata,
1010            StreamEvent::TransactionAbort { metadata, .. } => metadata,
1011            StreamEvent::SchemaChanged { metadata, .. } => metadata,
1012            StreamEvent::Heartbeat { metadata, .. } => metadata,
1013            StreamEvent::QueryResultAdded { metadata, .. } => metadata,
1014            StreamEvent::QueryResultRemoved { metadata, .. } => metadata,
1015            StreamEvent::QueryCompleted { metadata, .. } => metadata,
1016            StreamEvent::ErrorOccurred { metadata, .. } => metadata,
1017            _ => {
1018                // For unmatched event types, return a static reference
1019                use once_cell::sync::Lazy;
1020                static DEFAULT_METADATA: Lazy<EventMetadata> = Lazy::new(EventMetadata::default);
1021                &DEFAULT_METADATA
1022            }
1023        }
1024    }
1025}