pub struct LedgerIndex { /* private fields */ }Expand description
seq → (segment file name, byte offset of the line start).
The maps live in [Folded]; what this type adds is the filesystem
seam they are folded from. The seam sits behind a lock so a caller
holding &LedgerIndex asks questions without exclusive access, and
every question this type answers is a read: nothing here writes.
Implementations§
Source§impl LedgerIndex
impl LedgerIndex
Sourcepub fn rebuild(dir: &Path) -> Result<LedgerIndex, StorageError>
pub fn rebuild(dir: &Path) -> Result<LedgerIndex, StorageError>
Scans the ledger directory into a fresh index.
§Errors
Propagates a ledger directory that cannot be listed or read.
Sourcepub fn folding(
dir: &Path,
each: impl FnMut(&[u8]) -> Result<Option<Located>, AxError>,
) -> Result<LedgerIndex, AxError>
pub fn folding( dir: &Path, each: impl FnMut(&[u8]) -> Result<Option<Located>, AxError>, ) -> Result<LedgerIndex, AxError>
Reads the ledger once for a fold and for the index together: each
complete line is lent to each in ledger order, then indexed at
its offset. each returns the line’s place when it has read it
already, so the index does not parse the line again, or None for
the index to locate it by the rule rebuild uses. One segment’s
bytes are resident at a time, and the index covers exactly the
lines each saw: a line appended during the fold waits for
refresh.
§Errors
The first error each returns, or a ledger directory that cannot
be listed or read. The lines before it were already lent, so the
caller discards what it folded.
Sourcepub fn empty() -> LedgerIndex
pub fn empty() -> LedgerIndex
An index over nothing.
For a holder that wants one before the ledger directory exists: the
first refresh fills it, and until then every lookup answers
SeqMissing, which is the truth.
Sourcepub fn refresh(&mut self, dir: &Path) -> Result<Refreshed, StorageError>
pub fn refresh(&mut self, dir: &Path) -> Result<Refreshed, StorageError>
Folds whatever has been appended since the last look.
A resident index is the point: rebuilding one scans every segment
and allocates a String for every line in it, which on a fifty
thousand record ledger was 14.4 ms charged to every single
history query. Refreshing costs one directory listing plus a
stat per segment, and reads only the bytes that are new.
The incremental face is here rather than an “index, a record just
landed” call on the writer’s side, because a caller holds an
EventRecord and segments are this crate’s own business. Handing
an offset to an observer would leak the layout to everyone.
A segment that shrank or vanished forces a full rebuild: tail recovery truncates, and after it the offsets held for that segment describe bytes that are no longer there.
The answer reports how many bytes this refresh read, which is what separates seeking to the tail from lifting the segment that holds it.
§Errors
Propagates a ledger directory that cannot be listed, stat’ed or read.
Sourcepub fn reader(&self, dir: &Path) -> LineReader<'_>
pub fn reader(&self, dir: &Path) -> LineReader<'_>
A cursor for reading lines out of this index.
The unit of work is a stretch of sequences rather than one of them, so the handle belongs to the stretch: a caller asks for a reader once and then for lines as often as it likes.
Sourcepub fn run_seqs_before(
&self,
run: RunId,
before: Option<Seq>,
) -> impl Iterator<Item = Seq>
pub fn run_seqs_before( &self, run: RunId, before: Option<Seq>, ) -> impl Iterator<Item = Seq>
The sequences one run wrote, newest first, below before.
Newest first because what a reader opens a session for is its end; a caller that wants them oldest first takes what it needs and reverses a list it already holds, which also lets it read the segment forwards.
before is exclusive, the same word and the same meaning the wire
gives HistoryAnswer::earlier, so paging by handing one answer’s
cursor to the next question can neither repeat nor skip a record.
A run this index never saw yields nothing, which is the truth and needs no separate answer.
The walk is lazy: taking n values costs n steps, not the
length of the run.
Sourcepub fn seqs(&self) -> impl DoubleEndedIterator<Item = Seq> + '_
pub fn seqs(&self) -> impl DoubleEndedIterator<Item = Seq> + '_
Every seq the index holds, ascending: the walk a reader takes to read the whole ledger forwards, which is its fast direction.
Sourcepub fn seqs_from(&self, from: Seq) -> impl DoubleEndedIterator<Item = Seq> + '_
pub fn seqs_from(&self, from: Seq) -> impl DoubleEndedIterator<Item = Seq> + '_
Every seq the index holds at or after from, ascending: the walk
a reader takes to read a stretch forwards from where it starts.
The walk starts at from rather than skipping the seqs before it,
so reading a stretch costs the stretch, however long the ledger
before it is (crates/storage/Spec.lean §8-38). from need not be
a seq anybody wrote; nothing at or after it yields nothing.
pub fn tail_seq(&self) -> Option<Seq>
pub fn len(&self) -> usize
pub fn is_empty(&self) -> bool
Trait Implementations§
Source§impl<'de> Deserialize<'de> for LedgerIndex
impl<'de> Deserialize<'de> for LedgerIndex
Source§fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error>
fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error>
Source§impl Serialize for LedgerIndex
Only the folded maps are written: the seam is this process’s own, and
an index read back reaches disk through a new one. A views snapshot
carries the index this way (crates/storage/spec/Index.lean §8-4).
impl Serialize for LedgerIndex
Only the folded maps are written: the seam is this process’s own, and
an index read back reaches disk through a new one. A views snapshot
carries the index this way (crates/storage/spec/Index.lean §8-4).