Skip to main content

JournalStore

Trait JournalStore 

Source
pub trait JournalStore {
    // Required methods
    fn lock_exclusive(&self, lock_path: &Path) -> Result<Box<dyn JournalLock>>;
    fn append_line(&self, path: &Path, line: &str) -> Result<()>;
    fn read_lines(&self, path: &Path) -> Result<Vec<String>>;
    fn read(&self, path: &Path) -> Result<Option<Vec<u8>>>;
    fn write_atomic(&self, path: &Path, bytes: &[u8]) -> Result<()>;
    fn list_dir(&self, dir: &Path) -> Result<Vec<String>>;
}
Expand description

Durable, atomic, lockable storage for the release journal — the seam that keeps the event-sourced journal (ADR-0003) testable without touching the real filesystem, while pinning down the atomicity discipline its production impl must honor.

The append-then-apply contract (ADR-0003 §2, borrowed from octl-core) maps onto these operations:

  1. Self::append_line fsyncs the event so it is durable before the reducer applies it — a crash between append and apply replays as a clean no-op-or-apply, because the journal (read back by Self::read_lines) is the single source of truth.
  2. Self::write_atomic persists the derived manifest via temp-file → flush → atomic rename → directory fsync, so a torn write can never leave a half-written manifest (it is disposable and rebuildable regardless).
  3. Self::lock_exclusive enforces a single active cut per repo (a flock on the releases-dir .lock): a concurrent cut/resume fails fast rather than corrupting a run.

The port is deliberately path-driven (the journal computes paths via crate::release::journal::JournalPaths); the impl adds no policy, only the durability guarantees documented per method.

Required Methods§

Source

fn lock_exclusive(&self, lock_path: &Path) -> Result<Box<dyn JournalLock>>

Take the single-active-cut exclusive lock at lock_path (creating parent directories as needed). The returned guard holds the lock until dropped. Err with io::ErrorKind::WouldBlock when another holder is active, so the caller can fail fast and name the active run.

Source

fn append_line(&self, path: &Path, line: &str) -> Result<()>

Append line (one serialized event, no embedded newline — the store adds the single trailing \n) to the JSONL file at path, creating the file and parent directories if absent, and fsync so it is durable before returning. This is the append half of append-then-apply.

The write must be atomic at line granularity: on return the line is either fully present or not present, never truncated. The production impl opens with O_APPEND, write_alls the line + newline, and fsyncs the file and — when it created the file or a parent directory — the containing directory, so a newly created RunCreated survives power loss (fsyncing only the file leaves the new directory entry non-durable). A torn final line from a hard kill mid-write is still possible in theory; recovering it (truncate-to-last-good under the lock) is a documented follow-up, not part of this port yet — crate::release::journal::read_events currently rejects any malformed line.

Source

fn read_lines(&self, path: &Path) -> Result<Vec<String>>

Read every line of the JSONL file at path. Ok(vec![]) when the file is absent (a not-yet-written journal is empty, not an error).

Source

fn read(&self, path: &Path) -> Result<Option<Vec<u8>>>

Read the full contents of the (atomically-written) file at path, or Ok(None) when it is absent. Used for the torn-free fast-path read of the manifest.json cache; unlike Self::read_lines it returns raw bytes.

Source

fn write_atomic(&self, path: &Path, bytes: &[u8]) -> Result<()>

Atomically replace the file at path with bytes: write a temp file in the same directory, flush + fsync it, rename it over path, then fsync the directory. Creates parent directories as needed.

Source

fn list_dir(&self, dir: &Path) -> Result<Vec<String>>

The immediate entry names (not full paths) within dir, or Ok(vec![]) when dir is absent — used to enumerate run-id subdirectories for release list.

Dyn Compatibility§

This trait is dyn compatible.

In older versions of Rust, dyn compatibility was called "object safety".

Implementors§