Your browser sucks, JavaScript disabled! Gunnar's console needs JavaScript and WebAssembly. Git over SSH on port 2222 does not.

nordisk/skade

.nornir/time-travel-guide.md

Time-travel — guide

Class: guide · Function: time-travel. The what is in time-travel-design.md; the why in time-travel-reasoning.md.

Read a table as it was at a snapshot

// A full Table handle as of a historical snapshot:
let old = catalog.load_table_at(&ident, snapshot_id).await?;

// Just the metadata as of that snapshot (no Table build):
let meta = catalog.resolve_metadata_at(&ident, snapshot_id).await?;

snapshot_id is any Iceberg snapshot id that was ever committed for that table (they live in TableMetadata::snapshots()). An id that was never committed for that table → TableNotFound naming the snapshot.

Resolve many snapshots at once (query planners)

If you are fanning out over many snapshots — a planner reading a range of history — resolve them in one call. It pays one static-tree walk and one redb transaction for all N ids, not N of each:

let metas: Vec<Option<Arc<TableMetadata>>> =
    catalog.resolve_many(&ident, &[sid0, sid1, sid2, /* … */]).await?;
// one slot per input id, in order; None where that snapshot was never committed.

How routing works (so you can reason about latency)

You don't set the cutoff; the background compactor advances it as history grows (every 1024 commits, single-flight). Reads never block on it.

Forcing a rebuild

Compaction is automatic, but you can force it — useful in tests or right before a read-heavy historical scan:

let indexed = catalog.compact_static_index().await?;   // rebuild now; returns count

After a reopen the index is warm-started from the durable commit log automatically, so a restarted process is immediately accelerated for history it already had.