Skip to main content

MiniStore

Struct MiniStore 

Source
pub struct MiniStore { /* private fields */ }
Expand description

A durable, append-only log store for serializable records.

MiniStore manages a journal file (or set of rotated segments) on disk. It provides two core operations:

  • append: write a record and guarantee durability.
  • replay: read all records in order (static method).

§Concurrency

MiniStore is not thread-safe by itself. To share it across tasks, wrap it in a synchronization primitive like Arc<RwLock<MiniStore>> (for write-heavy workloads) or Arc<Mutex<MiniStore>>.

§Durability & Rotation

  • Every append() performs an fsync before returning.
  • When the active segment reaches max_bytes_per_segment, it is rotated:
    • Closed, renamed to journal.jsonl.001, etc.
    • A new active file is created.
  • Only max_segments files are kept; older ones are deleted.
  • replay() automatically reads all segments in correct order.

Implementations§

Source§

impl MiniStore

Source

pub async fn open<P: AsRef<Path>>(path: P) -> Result<Self>

Opens a ministore journal at the given path with default options.

Equivalent to MiniStoreOptions::new().open(path).

§Behavior
  • If the file does not exist, it is created and initialized with the magic header.
  • If the file exists and is empty, the magic header is written.
  • If the file exists and is non-empty, it is assumed to be a valid journal (the magic header must already be present; validated during [replay]).
§Errors

Returns an error if:

  • The path is not writable.
  • Parent directories cannot be created.
  • Disk I/O fails during magic header write.
Source

pub async fn append<R>(&mut self, record: &R) -> Result<()>
where R: Serialize,

Appends a serializable record to the journal and ensures it is durably stored.

The record is serialized as a single JSON line and immediately fsynced to disk. This operation is atomic  either the entire record is written, or nothing is.

If the active segment exceeds max_bytes_per_segment, it is rotated before return.

§Guarantees

After this method returns Ok(()):

  • The record is visible in subsequent [replay] calls.
  • The record will survive process termination or system crash.
§Errors

Returns an error if:

  • Serialization fails (e.g., unsupported type).
  • Disk write fails (e.g., full disk).
  • fsync fails (e.g., I/O error).
§Performance

This is a slow operation due to the fsync. Use it for critical metadata, not high-frequency data.

Source

pub async fn replay<R, P: AsRef<Path>>(path: P) -> Result<Vec<R>>

Replays all records from a journal (including rotated segments) as a Vec of strongly-typed values.

This is a static method  it does not require an open MiniStore instance.

§Behavior
  • Scans the directory for files matching base.001, base.002, etc.
  • Reads segments in ascending order (.001.002 � … � active file).
  • Validates magic header in each file.
  • If the journal does not exist or is empty, returns an empty Vec.
§Errors

Returns an error if:

  • Any segment has a missing or invalid magic header.
  • Any line fails to deserialize as type R.
  • File I/O fails (e.g., permission denied).
§Example
let events: Vec<Event> = MiniStore::replay("/tmp/events.log").await?;
Source

pub async fn stream<T>(path: impl AsRef<Path>) -> Result<JournalStream<T>>
where T: for<'de> Deserialize<'de>,

Returns a stream (line iterator) over the records in a single journal file.

Each line is parsed on-demand as Result<T, MiniStoreError>. This avoids loading the entire journal into memory.

Note: this function reads only one file, not rotated segments. Use [replay] to read the full journal history.

Trait Implementations§

Source§

impl Debug for MiniStore

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.