Skip to main content

FileLog

Struct FileLog 

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

Second implementation (seam rule: feature-poor is fine, broken is not): JSON-lines file, append-only, rebuilt head on open.

Implementations§

Source§

impl FileLog

Source

pub fn open(path: &Path) -> Result<Self, LogError>

Opens (creating if absent) the log file at path and replays it to rebuild the in-memory index and head.

§Torn tails

A power cut can leave the file ending in a record that was only partly written, or — under delayed allocation — in a run of NUL bytes. Any final record with no terminating newline is treated as such a torn write and truncated away, whether or not it happens to decode: a complete record whose newline never landed would otherwise be concatenated with the next append into one line that never parses again.

Discarding it cannot lose an acknowledged op. The sequencer acknowledges only after OpLog::sync returns, and that call returns only once every byte before it is on the platter, so anything in an unterminated tail was never acknowledged to anyone. The truncation is reported by FileLog::torn_tail_bytes rather than performed silently.

A decode failure in a newline-terminated record is not a torn write — it is damage to a record that was once written whole — and still fails as LogError::Corrupt. That includes a NUL-filled gap followed by further records: refusing to start is the right answer there, because the alternative is silently dropping ops from the middle of the log.

§Errors

Returns LogError::Io on filesystem failure and LogError::Corrupt when a terminated line fails to decode.

Source

pub fn torn_tail_bytes(&self) -> u64

Bytes of partly written tail that FileLog::open truncated away, or 0 if the log ended on a record boundary.

Non-zero means this process started after an unclean stop. The discarded bytes were never acknowledged (see FileLog::open), so this is a fact worth reporting, not a fault — but a caller that never reports it turns a crash into a silent one.

Source

pub fn torn_tail_quarantine(&self) -> Option<&Path>

Where the truncated bytes were saved, if any were.

The truncation is automatic because a node has to come back up unattended after a power cut, but the bytes are not thrown away: an operator asking “what was lost” gets a file to look at rather than a number. None means the log ended on a record boundary.

Trait Implementations§

Source§

impl Drop for FileLog

Source§

fn drop(&mut self)

Last-resort flush. The sequencer syncs per batch, so in normal operation this finds an empty buffer; it exists so a log dropped on an error path does not silently discard buffered entries. Errors are unreportable here, hence the ok() — durability is the sequencer’s job via OpLog::sync, not this.

Source§

fn pin_drop(self: Pin<&mut Self>)

🔬This is a nightly-only experimental API. (pin_ergonomics)
Execute the destructor for this type, but different to Drop::drop, it requires self to be pinned. Read more
Source§

impl OpLog for FileLog

Source§

fn get(&self, seq: u64) -> Option<OpEntry>

Reads one entry back: from the pending buffer if it has not been flushed yet, otherwise from the file at its recorded offset.

Returns None for an out-of-range seq and also for a stored line that fails to decode or read. The trait signature has no way to say “present but unreadable”, and inventing one is a wider change than this belongs in — but a corrupt log is a real condition, and open does report it as LogError::Corrupt, so damage is caught when the log is next opened rather than never.

Source§

fn sync(&mut self) -> Result<(), LogError>

Flush the buffer to the OS, then ask the OS to put it on the platter. Both halves are required and neither substitutes for the other: flush alone leaves the bytes in the page cache, and sync_data alone would sync a buffer that was never written.

sync_data rather than sync_all: the file’s length and contents must survive, its mtime need not, and skipping the metadata write is the cheaper half of an fsync.

Source§

fn append(&mut self, entry: OpEntry) -> Result<ContentHash, LogError>

Appends entry and returns its content hash (the new head). Read more
Source§

fn head(&self) -> Option<ContentHash>

Content hash of the newest entry, or None for an empty log.
Source§

fn len(&self) -> u64

Number of entries in the log.
Source§

fn last(&self) -> Option<&OpEntry>

Borrows the newest entry without cloning it. Read more
Source§

fn is_empty(&self) -> bool

Whether the log has no entries.

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.