Skip to main content

ShardManager

Struct ShardManager 

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

Manages time-sharded event files in a .bones repository.

The shard manager handles:

  • Directory initialization
  • Shard rotation on month boundaries
  • Atomic append with advisory locking
  • Monotonic clock maintenance
  • Torn-write recovery
  • Replay (reading all shards chronologically)
  • Sealed shard manifest generation

Implementations§

Source§

impl ShardManager

Source

pub fn new(bones_dir: impl Into<PathBuf>) -> Self

Create a new ShardManager for the given .bones directory.

Does not create directories on construction; call init or ensure_dirs first if needed.

Source

pub fn events_dir(&self) -> PathBuf

Path to the events directory.

Source

pub fn lock_path(&self) -> PathBuf

Path to the advisory lock file.

Source

pub fn clock_path(&self) -> PathBuf

Path to the monotonic clock file.

Path to the current.events symlink.

Source

pub fn shard_filename(year: i32, month: u32) -> String

Generate the shard filename for a given year and month.

Source

pub fn shard_path(&self, year: i32, month: u32) -> PathBuf

Path to a specific shard file.

Source

pub fn manifest_path(&self, year: i32, month: u32) -> PathBuf

Path to a manifest file for a given shard.

Source

pub fn ensure_dirs(&self) -> Result<(), ShardError>

Create the .bones/events/ and .bones/cache/ directories if they don’t exist. Idempotent.

§Errors

Returns ShardError::InitFailed if directory creation fails.

Source

pub fn init(&self) -> Result<(i32, u32), ShardError>

Initialize the shard directory and create the first shard file with the standard header if no shards exist.

Returns the (year, month) of the active shard.

§Errors

Returns ShardError on I/O failure or if directories cannot be created.

Source

pub fn list_shards(&self) -> Result<Vec<(i32, u32)>, ShardError>

List all shard files in chronological order as (year, month) pairs.

Shard filenames must match YYYY-MM.events. Invalid filenames are silently skipped.

§Errors

Returns ShardError::Io if the directory cannot be read.

Source

pub fn active_shard(&self) -> Result<Option<(i32, u32)>, ShardError>

Get the active (most recent) shard, if any.

§Errors

Returns ShardError::Io if the directory cannot be read.

Source

pub fn create_shard(&self, year: i32, month: u32) -> Result<PathBuf, ShardError>

Create a new shard file with the standard header.

Returns the path of the created file. Does nothing if the file already exists.

§Errors

Returns ShardError::Io if the file cannot be written.

Update the current.events symlink to point to the given shard.

§Errors

Returns ShardError::Io if the symlink cannot be created.

Source

pub fn rotate_if_needed(&self) -> Result<(i32, u32), ShardError>

Check if the current month differs from the active shard’s month. If so, seal the old shard (generate manifest) and create a new one.

Returns the (year, month) of the now-active shard.

§Errors

Returns ShardError on I/O failure during rotation.

Source

pub fn write_manifest( &self, year: i32, month: u32, ) -> Result<ShardManifest, ShardError>

Generate and write a manifest file for a sealed shard.

§Errors

Returns ShardError::Io if the shard file cannot be read or the manifest cannot be written.

Source

pub fn read_manifest( &self, year: i32, month: u32, ) -> Result<Option<ShardManifest>, ShardError>

Read a manifest file if it exists.

§Errors

Returns ShardError::Io if the manifest file exists but cannot be read.

Source

pub fn append( &self, line: &str, durable: bool, lock_timeout: Duration, ) -> Result<i64, ShardError>

Append an event line to the active shard.

This method:

  1. Acquires the repo-wide advisory lock.
  2. Rotates shards if the month has changed.
  3. Reads and updates the monotonic clock.
  4. Appends the line using O_APPEND + write_all + flush.
  5. Optionally calls sync_data if durable is true.
  6. Releases the lock.

The line must be a complete TSJSON line ending with \n.

Returns the monotonic timestamp used.

§Errors

Returns ShardError::Lock if the lock cannot be acquired within lock_timeout, or ShardError::Io on write failure.

Source

pub fn append_raw( &self, year: i32, month: u32, line: &str, ) -> Result<(), ShardError>

Append a raw line without locking or clock update.

Used internally and in tests. The caller is responsible for holding the lock and managing the clock.

§Errors

Returns ShardError::Io on write failure.

Source

pub fn read_clock(&self) -> Result<i64, ShardError>

Read the current monotonic clock value.

Returns 0 if the clock file doesn’t exist.

§Errors

Returns ShardError::Io if the clock file exists but cannot be read.

Source

pub fn next_timestamp(&self) -> Result<i64, ShardError>

Compute the next monotonic timestamp and write it to the clock file.

next = max(system_time_us, last + 1)

The caller must hold the repo lock.

§Errors

Returns ShardError::Io if the clock file cannot be read or written.

Source

pub fn recover_torn_writes(&self) -> Result<Option<u64>, ShardError>

Scan the active shard for torn writes and truncate incomplete trailing lines.

A torn write leaves a partial line (no terminating \n) at the end of the file. This method finds the last complete newline and truncates everything after it.

Returns Ok(Some(bytes_truncated)) if a torn write was repaired, or Ok(None) if the file was clean.

§Errors

Returns ShardError::Io if the shard file cannot be read or truncated.

Source

pub fn replay(&self) -> Result<String, ShardError>

Read all event lines from all shards in chronological order.

Shards are read in lexicographic order (YYYY-MM sorts correctly). Returns the concatenated content of all shard files.

§Errors

Returns ShardError::Io if any shard file cannot be read.

Source

pub fn read_shard(&self, year: i32, month: u32) -> Result<String, ShardError>

Read event lines from a specific shard.

§Errors

Returns ShardError::Io if the shard file cannot be read.

Source

pub fn total_content_len(&self) -> Result<usize, ShardError>

Compute the total concatenated byte size of all shards without reading their full contents.

This is used for advancing the projection cursor without paying the cost of a full replay.

§Errors

Returns ShardError::Io if any shard file metadata cannot be read.

Source

pub fn replay_from_offset( &self, offset: usize, ) -> Result<(String, usize), ShardError>

Read shard content starting from the given absolute byte offset in the concatenated shard sequence.

Sealed shards that end entirely before offset are skipped without reading their contents — only their file sizes are stat(2)’d. Only content from offset onward is returned, bounding memory use to new/unseen events rather than the full log.

Returns (new_content, total_len) where:

  • new_content is the bytes from offset to the end of all shards.
  • total_len is the total byte size of all shards concatenated (usable as the new cursor offset after processing new_content).
§Errors

Returns ShardError::Io if shard metadata or file reads fail.

Source

pub fn read_content_range( &self, start_offset: usize, end_offset: usize, ) -> Result<String, ShardError>

Read bytes from the concatenated shard sequence in [start_offset, end_offset).

Only shards that overlap with the requested range are read. Shards entirely outside the range are stat(2)’d but not read.

This is used to read a small window around the projection cursor for hash validation without loading the full shard content.

§Errors

Returns ShardError::Io if any shard file cannot be read.

Source

pub fn event_count(&self) -> Result<u64, ShardError>

Count event lines across all shards (excluding comments and blanks).

§Errors

Returns ShardError::Io if any shard file cannot be read.

Source

pub fn replay_lines( &self, ) -> Result<impl Iterator<Item = Result<(usize, String)>>, ShardError>

Iterate over all event lines across all shards.

Yields (absolute_offset, line_content) pairs.

§Errors

Returns ShardError::Io if directory or shard reading fails.

Source

pub fn replay_lines_from_offset( &self, offset: usize, ) -> Result<impl Iterator<Item = Result<(usize, String)>>, ShardError>

Iterate over event lines starting from a given absolute byte offset.

Yields (absolute_offset, line_content) pairs.

§Errors

Returns ShardError::Io if directory or shard reading fails.

Source

pub fn is_empty(&self) -> Result<bool, ShardError>

Check if the repository has any event shards.

§Errors

Returns ShardError::Io if the events directory cannot be read.

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> Instrument for T

Source§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided Span, returning an Instrumented wrapper. Read more
Source§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an Instrumented wrapper. Read more
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> Same for T

Source§

type Output = T

Should always be Self
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.
Source§

impl<T> WithSubscriber for T

Source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a WithDispatch wrapper. Read more
Source§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a WithDispatch wrapper. Read more