Skip to main content

SegmentCatalog

Struct SegmentCatalog 

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

Segment metadata catalog with manifest persistence.

Tracks all segment metadata organized by shard, along with measurement schemas. Changes are persisted to a manifest WAL file, with periodic snapshots to prevent unbounded manifest growth.

§Persistence

Changes are appended to manifest.wal as length-prefixed, CRC-32C framed postcard entries. Periodic snapshots written to manifest.snapshot.bin. On startup: load latest snapshot, replay WAL entries.

§Concurrency

The catalog currently uses a single &mut self borrow (i.e. external synchronisation via an RwLock or Mutex) for all shards. This is acceptable because catalog mutations (segment add/remove, schema updates) are infrequent (flush/compaction cadence) and short-lived.

If catalog contention ever becomes measurable under very high shard counts, the internal BTreeMap<ShardId, Vec<…>> can be replaced with a DashMap<ShardId, Vec<…>> to allow per-shard concurrent access without changing the external API.

Implementations§

Source§

impl SegmentCatalog

Source

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

Create a new empty catalog persisted in manifest_dir.

§Errors

Returns an error if the manifest directory cannot be created.

Source

pub fn open(manifest_dir: impl Into<PathBuf>) -> Result<Self>

Open an existing catalog, loading from snapshot + replaying manifest WAL.

§Errors

Returns an error if the snapshot or manifest cannot be read/parsed.

Source

pub fn add_segment(&mut self, entry: SegmentCatalogEntry) -> Result<()>

Add a segment to the catalog.

§Errors

Returns an error if the manifest cannot be written.

Source

pub fn remove_segment( &mut self, segment_id: SegmentId, ) -> Result<Option<SegmentCatalogEntry>>

Remove a segment from the catalog by its ID.

Returns the removed entry if found.

§Errors

Returns an error if the manifest cannot be written.

Source

pub fn get_segments_for_shard( &self, shard_id: &ShardId, ) -> &[SegmentCatalogEntry]

Get all segments for a specific shard.

Source

pub fn soft_delete_segment( &mut self, segment_id: SegmentId, now_ms: u64, ) -> Result<bool>

Mark a segment as soft-deleted.

The segment remains in the catalog and is still queryable by code that explicitly opts in to soft-deleted segments — but the default query path (via active_segments_for_measurement) hides it. A subsequent call to remove_segment will hard-delete it.

Source

pub fn active_segments_for_measurement( &self, measurement: &str, ) -> Vec<&SegmentCatalogEntry>

Get all active segments belonging to a specific measurement.

Excludes segments that are soft-deleted (waiting for GC), and every segment of a measurement pending a hard delete — a “drop” that leaves its rows scannable for the whole grace period is not a drop. The segments themselves are untouched, so a restore before the deadline costs nothing and loses nothing.

Source

pub fn retired_segments(&self) -> Vec<&SegmentCatalogEntry>

Segments that have been retired — taken out of every query — but whose files are still on disk.

A retirement unlinks the file in the same step that removes the catalog entry; an entry reaches this state only when a running scan had already been handed the path, so garbage collection is what finishes the job once the reader has gone.

Source

pub fn all_segments(&self) -> Vec<&SegmentCatalogEntry>

Get all segments across all shards.

Source

pub fn segments_for_measurement( &self, measurement: &str, ) -> Vec<&SegmentCatalogEntry>

Get all segments belonging to a specific measurement.

Source

pub fn max_segments_per_shard_measurement(&self) -> usize

Get the total number of segments. The largest number of active segments any one (shard, measurement) holds — the count a compaction pass can actually reduce, and so the number write backpressure is keyed on.

Source

pub fn memory_bytes(&self) -> usize

Approximate heap bytes held by the catalog.

One entry per segment, each carrying a measurement name, a path and its per-column statistics — so this grows with segment count, not with row count, and it is the term that a long-running gateway accumulates between compactions. The schema registry and the tombstone set are counted with it: all three live for the process’s lifetime, which is what makes them worth a number rather than an estimate.

Source

pub fn shard_count(&self) -> usize

Shards holding at least one active segment.

Derived rather than tracked: a TimeIndex was maintained beside the catalog on every flush, compaction and retirement to answer this and nothing else, and its own count drifted — a shard emptied by per-measurement retention kept its entry and stayed counted.

Source

pub fn segment_count(&self) -> usize

Total number of segments the catalog holds, in any state.

Source

pub fn next_segment_id(&mut self) -> SegmentId

Allocate the next segment ID.

Source

pub fn manifest_seq(&self) -> u64

Get the current manifest sequence number.

Source

pub fn rollups(&self) -> &BTreeMap<String, RollupRecord>

Every persisted rollup, by name.

Source

pub fn set_rollup(&mut self, name: &str, definition: Vec<u8>) -> Result<()>

Persist a rollup definition, durably.

§Errors

Returns an error if the manifest cannot be written or synced.

Source

pub fn set_rollup_state(&mut self, name: &str, state: Vec<u8>) -> Result<()>

Persist a rollup’s materialisation state, durably.

Called after the rollup’s points are durable, never before: a watermark that outlives the rows it claims is a permanent hole in the tier, because nothing recomputes a bucket the watermark has already passed.

§Errors

Returns an error if the manifest cannot be written or synced.

Source

pub fn remove_rollup(&mut self, name: &str) -> Result<bool>

Delete a rollup definition and its state.

§Errors

Returns an error if the manifest cannot be written.

Source

pub fn pending_measurement_drops(&self) -> &BTreeMap<String, u64>

Every measurement pending a hard delete, and the unix-ms deadline each was given.

Source

pub fn is_measurement_pending_drop(&self, measurement: &str) -> bool

Whether measurement is pending a hard delete.

Source

pub fn set_measurement_pending_drop( &mut self, measurement: &str, deadline_ms: u64, ) -> Result<()>

Mark measurement pending a hard delete at deadline_ms, durably.

§Errors

Returns an error if the manifest cannot be written or synced.

Source

pub fn cancel_measurement_pending_drop( &mut self, measurement: &str, ) -> Result<bool>

Cancel a pending drop \u2014 a restore, or the hard delete that finally acted on it \u2014 durably.

Returns whether measurement was pending.

§Errors

Returns an error if the manifest cannot be written or synced.

Source

pub fn tombstones(&self) -> &TombstoneSet

The tombstones recorded by deletes.

Source

pub fn record_tombstones(&mut self, tombstones: &[Tombstone]) -> Result<()>

Record the tombstones produced by a delete, durably.

The manifest append is fsynced before this returns, so a delete that has been acknowledged has been persisted — which is the property the previous design lacked, because it kept tombstones only in memory and in a data WAL that the next flush was free to truncate.

§Errors

Returns an error if the manifest cannot be written or synced.

Source

pub fn complete_compaction( &mut self, output: SegmentCatalogEntry, inputs: &[SegmentId], now_ms: u64, ) -> Result<()>

Register a compaction’s output and retire its inputs, as one durable step.

Adds output, soft-deletes every input, and extends every tombstone that named an input to the output. That last part is what closes the window in which a delete issued while the merge ran was undone: the merge applied the tombstones it snapshotted, the late tombstone named only the inputs, and once GC removed them the tombstone was reclaimed with its rows alive in the output.

§Errors

Returns an error if the manifest cannot be written.

Source

pub fn reclaim_tombstones(&mut self) -> Result<usize>

Reclaim tombstones whose segments have all left the catalog.

A segment leaves the catalog only by being rewritten — compaction applies tombstones as it merges — or by being deleted outright, so once none of a tombstone’s segments remains, no stored row can still match it. Returns the number reclaimed.

§Errors

Returns an error if the manifest cannot be written.

Source

pub fn set_schema(&mut self, schema: MeasurementSchema) -> Result<()>

Register or update a measurement schema.

§Errors

Returns an error if the manifest cannot be written.

Source

pub fn remove_schema( &mut self, measurement: &str, ) -> Result<Option<MeasurementSchema>>

Remove a measurement schema.

§Errors

Returns an error if the manifest cannot be written.

Source

pub fn wal_floor(&self) -> u64

The WAL floor: the highest data-WAL sequence number known to be fully represented by the segments this catalog registers.

open() replays only records above it.

Source

pub fn set_wal_floor(&mut self, sequence_no: u64) -> Result<()>

Raise the WAL floor to sequence_no.

Persisted before it is applied, like every other catalog change. A floor never moves backwards; a lower value is a no-op.

§Errors

Returns an error if the manifest append fails.

Source

pub fn get_schema(&self, measurement: &str) -> Option<&MeasurementSchema>

Look up a measurement schema.

Source

pub fn schema_names(&self) -> Vec<String>

Returns all schema names.

Source

pub fn all_schemas(&self) -> impl Iterator<Item = (&String, &MeasurementSchema)>

Returns an iterator over all (name, schema) pairs.

Source

pub fn force_snapshot(&mut self) -> Result<()>

Force a snapshot of the current catalog state.

§Errors

Returns an error if the snapshot cannot be written.

Source

pub fn manifest_syncs(&self) -> u64

How many times the manifest has been fsynced since this catalog was opened.

One per catalog transition, not per append: a compaction retiring four segments is one, and so is a delete however many tombstones it produces. Also exported as chronix_catalog_fsync_total.

Source

pub fn in_one_sync<T>( &mut self, body: impl FnOnce(&mut Self) -> Result<T>, ) -> Result<T>

Run body as one durable manifest transition: the appends it makes are written but fsynced once, when it returns.

A compaction of four segments is six appends — the output, four soft-deletes and the Compacted record — and a delete is one per tombstone. On flash-backed storage the fsync rate is the wear rate, which is why the WAL has a periodic policy.

Everything body appended is durable when this returns, on the error path as much as the success path, so a transition that failed half-way is as persistent as it would be if each append had synced. A crash during body leaves what it always could: the manifest is a replayed log, so a partial transition means an input segment still live beside its compaction output, which reads deduplicate and the next compaction clears.

Only for a transition that ends in a sync, and only where the caller does nothing irreversible until it returns: a file unlinked while the appends are still unsynced would survive a crash as a catalog entry naming a path that is gone.

§Errors

Returns body’s error, or the error of the fsync that follows it.

Source

pub fn sync_manifest(&mut self) -> Result<()>

Flush any pending manifest writes to durable storage.

Call this after a batch of mutations to ensure all appended entries survive a power failure. This is a no-op if there are no pending un-synced writes.

Source

pub fn write_snapshot_to(&self, manifest_dir: &Path) -> Result<()>

Write this catalog, as it stands, as the whole catalog of a database rooted at manifest_dir — a directory that is not this one.

This is what a backup captures instead of copying catalog/ file by file. Copying was the defect: a snapshot landing mid-copy replaces manifest.snapshot.bin and truncates manifest.wal, so a reader walking the directory can pair the old snapshot with the emptied log and lose every transition since — silently, and reported as a successful backup. One encode of one consistent in-memory state cannot be torn, and the empty log beside it says there is nothing to replay.

The caller must hold the catalog read lock for as long as it needs the segments this names to still be there.

§Errors

Returns an error if the directory cannot be created or written.

Trait Implementations§

Source§

impl Debug for SegmentCatalog

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<ST, DT> CastableFrom<ST, Initialized, Initialized> for DT
where ST: ?Sized, DT: ?Sized,

Source§

impl<ST, DT> CastableFrom<ST, Uninit, Uninit> for DT
where ST: ?Sized, DT: ?Sized,

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

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self> ⓘ

Converts self into a Left variant of Either<Self, Self> if into_left is true. Converts self into a Right variant of Either<Self, Self> otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self> ⓘ
where F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> if into_left(&self) returns true. Converts self into a Right variant of Either<Self, Self> otherwise. Read more
Source§

impl<T> Pointable for T

Source§

const ALIGN: usize

The alignment of pointer.
Source§

type Init = T

The type for initializers.
Source§

unsafe fn init(init: <T as Pointable>::Init) -> usize

Initializes a with the given initializer. Read more
Source§

unsafe fn deref<'a>(ptr: usize) -> &'a T

Dereferences the given pointer. Read more
Source§

unsafe fn deref_mut<'a>(ptr: usize) -> &'a mut T

Mutably dereferences the given pointer. Read more
Source§

unsafe fn drop(ptr: usize)

Drops the object pointed to by the given pointer. Read more
Source§

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

Source§

fn and<P, B, E>(self, other: P) -> And<T, P>
where T: Sized + Policy<B, E>, P: Policy<B, E>,

Create a new Policy that returns Action::Follow only if self and other return Action::Follow. Read more
Source§

fn or<P, B, E>(self, other: P) -> Or<T, P>
where T: Sized + Policy<B, E>, P: Policy<B, E>,

Create a new Policy that returns Action::Follow if either self or other returns Action::Follow. Read more
Source§

impl<T> Read<Exclusive, BecauseExclusive> for T
where T: ?Sized,

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 = !

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

fn try_from(value: U) -> Result<T, !>

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