Time-travel — guide
Class:
guide· Function:time-travel. The what is intime-travel-design.md; the why intime-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)
- Ids at or below the current cutoff are served from the lock-free Ragnar
STree64index — a handful of cache-line probes, no redb. - Ids above the cutoff (recent commits not yet compacted) fall through to the redb
commitslive tail — one point lookup.
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.