Skip to main content

lora_database/wal/
admin.rs

1//! Storage-agnostic admin surface for the WAL.
2//!
3//! Transports (`lora-server`, language bindings) type-erase on
4//! `Arc<dyn WalAdmin>` so they don't need to name the database's
5//! backend type parameter. All LSNs cross the trait boundary as raw
6//! `u64` so callers don't pull a direct dependency on `lora-wal`.
7
8use std::path::Path;
9
10use lora_store::{InMemoryGraph, SnapshotMeta};
11use lora_wal::Lsn;
12
13use crate::database::Database;
14use crate::error::{LoraError, LoraErrorCode};
15
16/// Storage-agnostic admin surface for the WAL.
17///
18/// `Database<InMemoryGraph>` picks up the impl below when a WAL is
19/// attached.
20pub trait WalAdmin: Send + Sync + 'static {
21    /// Take a checkpoint at `path`. The snapshot's header is stamped
22    /// with the WAL's `durable_lsn`; older sealed segments are then
23    /// dropped.
24    fn checkpoint(&self, path: &Path) -> Result<SnapshotMeta, LoraError>;
25
26    /// Snapshot of the WAL's current state — durable / next LSN,
27    /// active / oldest segment id. Cheap; a single WAL mutex acquisition.
28    fn wal_status(&self) -> Result<WalStatus, LoraError>;
29
30    /// Drop sealed segments at or below `fence_lsn`. Idempotent.
31    fn wal_truncate(&self, fence_lsn: u64) -> Result<(), LoraError>;
32}
33
34/// Snapshot of WAL state returned by [`WalAdmin::wal_status`].
35///
36/// `bg_failure` is the reserved latched durability-error slot for a future
37/// background/group flusher. In this single-threaded release it normally stays
38/// `None`; when `Some`, the WAL is poisoned and every subsequent commit fails
39/// loudly until the operator restarts from the last consistent snapshot + WAL.
40#[derive(Debug, Clone)]
41pub struct WalStatus {
42    pub durable_lsn: u64,
43    pub next_lsn: u64,
44    pub active_segment_id: u64,
45    pub oldest_segment_id: u64,
46    pub bg_failure: Option<String>,
47}
48
49impl WalAdmin for Database<InMemoryGraph> {
50    fn checkpoint(&self, path: &Path) -> Result<SnapshotMeta, LoraError> {
51        self.checkpoint_to(path)
52    }
53
54    fn wal_status(&self) -> Result<WalStatus, LoraError> {
55        let recorder = self
56            .wal
57            .as_ref()
58            .ok_or_else(|| LoraError::new(LoraErrorCode::Internal, "WAL not enabled"))?;
59        let wal = recorder.wal();
60        Ok(WalStatus {
61            durable_lsn: wal.durable_lsn().raw(),
62            next_lsn: wal.next_lsn().raw(),
63            active_segment_id: wal.active_segment_id(),
64            oldest_segment_id: wal.oldest_segment_id(),
65            bg_failure: wal.bg_failure(),
66        })
67    }
68
69    fn wal_truncate(&self, fence_lsn: u64) -> Result<(), LoraError> {
70        let recorder = self
71            .wal
72            .as_ref()
73            .ok_or_else(|| LoraError::new(LoraErrorCode::Internal, "WAL not enabled"))?;
74        recorder.truncate_up_to(Lsn::new(fence_lsn))?;
75        Ok(())
76    }
77}