Skip to main content

sim_lib_journal/
lib.rs

1//! Domain-free atomic journal behavior over content-addressed objects.
2//!
3//! A journal publishes immutable objects before making entries visible and
4//! advances its head through one fenced compare-and-swap transition. Backends
5//! implement [`JournalBackend::admit`]; callers use [`Journal`] so the gapless,
6//! closure, redelivery, and replay laws are enforced once.
7
8mod backend;
9mod datum_codec;
10mod entry;
11mod head;
12mod host;
13mod lease;
14mod memory;
15mod native_codec;
16mod object;
17mod persistent;
18mod projection;
19mod replay;
20mod verify;
21
22pub use backend::{Admission, JournalBackend, StoredState};
23pub use entry::JournalEntry;
24pub use head::JournalHead;
25pub use host::HostDirJournalBackend;
26pub use lease::Lease;
27pub use memory::MemoryBackend;
28pub use native_codec::{
29    BackendCapabilities, EntryLocation, EntryNamespace, Failpoint, NativeFormatId,
30    NativeStateEnvelope, VerifiedNativePrefixRef,
31};
32pub use object::JournalObject;
33pub use persistent::{
34    PersistentObjectStore, PersistentSemanticObjects, StoreError, StoredDatumRef,
35};
36pub use projection::{DirProjection, ProjectionRow, TableProjection};
37pub use replay::{Replay, replay};
38pub use verify::{JournalError, Verification};
39
40use sim_kernel::{ContentId, Symbol};
41
42/// The single state-machine implementation shared by every backend.
43pub struct Journal<B> {
44    backend: B,
45}
46
47impl<B: JournalBackend> Journal<B> {
48    /// Wraps a backend. Durable production backends must report write safety.
49    pub fn new(backend: B) -> Self {
50        Self { backend }
51    }
52
53    /// Obtains a new fencing generation, invalidating every older lease.
54    pub fn acquire_lease(&self) -> Result<Lease, JournalError> {
55        self.backend.acquire_lease()
56    }
57
58    /// Returns the current verified head.
59    pub fn head(&self) -> Result<Option<JournalHead>, JournalError> {
60        let state = self.backend.read_state()?;
61        verify::verify_state(&state).map(|v| v.head)
62    }
63
64    /// Atomically publishes objects and an ordered entry batch under one fence.
65    ///
66    /// Exact redelivery is a no-op. Any conflicting delivery, missing/corrupt
67    /// payload, stale fence, sequence gap, or wrong predecessor is rejected.
68    pub fn publish(
69        &self,
70        lease: &Lease,
71        expected: Option<&JournalHead>,
72        objects: Vec<JournalObject>,
73        entries: Vec<JournalEntry>,
74    ) -> Result<JournalHead, JournalError> {
75        if entries.is_empty() {
76            return Err(JournalError::EmptyBatch);
77        }
78        let before = self.backend.read_state()?;
79        verify::verify_state(&before)?;
80        verify::verify_batch(&before, expected, &objects, &entries)?;
81        let head = self.backend.admit(Admission {
82            fence: lease.fence,
83            expected: expected.cloned(),
84            objects,
85            entries: entries.clone(),
86        })?;
87        Ok(head)
88    }
89
90    /// Reads and verifies the complete journal closure.
91    pub fn verify(&self) -> Result<Verification, JournalError> {
92        verify::verify_state(&self.backend.read_state()?)
93    }
94
95    /// Replays verified entries in sequence order.
96    pub fn replay(&self) -> Result<Replay, JournalError> {
97        replay(self.backend.read_state()?)
98    }
99
100    /// Creates a detached, read-only table projection.
101    pub fn table_projection(&self) -> Result<TableProjection, JournalError> {
102        Ok(TableProjection::from_verification(self.verify()?))
103    }
104
105    /// Creates a detached, read-only directory projection.
106    pub fn dir_projection(&self) -> Result<DirProjection, JournalError> {
107        Ok(DirProjection::from_verification(self.verify()?))
108    }
109
110    /// Convenience constructor for an entry with an open kind symbol.
111    pub fn entry(
112        sequence: u64,
113        previous: Option<ContentId>,
114        kind: Symbol,
115        payloads: Vec<ContentId>,
116    ) -> JournalEntry {
117        JournalEntry::new(sequence, previous, kind, payloads)
118    }
119}
120
121#[cfg(test)]
122mod tests;