datalog-dafsa

Time Travel

Time-travel is a first-class feature, not an add-on: because the engine is built around a publish-then-serve lifecycle and keeps every published snapshot by default, the database is a versioned point-in-time history. Any past state can be queried as if it were current.

That buys real capabilities on a batch-write / read-heavy workload:

Time-travel is available through the C API only — there is no Datalog syntax and no CLI command. It reuses the same versioned-snapshot directories and mmap views as the ordinary read path.

Versioned snapshots

Each successful publish produces a monotonically increasing version number starting at 1. Enumerate the available versions ascending with dl_snapshot_versions, using the two-call idiom:

long total = dl_snapshot_versions(db, NULL, 0);   /* size */
uint32_t *vers = malloc((size_t)total * sizeof(*vers));
dl_snapshot_versions(db, vers, (size_t)total);    /* fill */
free(vers);

It returns the total number of versions even when the output buffer is smaller (filling at most cap entries), returns 0 when no snapshot has been published, and -1 on a NULL db.

As-of queries

Query a relation as of a specific published version with dl_query_version, or bind leading columns with dl_query_bound_version:

long n = dl_query_version(db, version, "edge", cb, user);
long m = dl_query_bound_version(db, version, "edge", leading, k, cb, user);

Semantics and guarantees:

Retention

By default every version is kept forever. To bound disk usage, opt in to prune-to-N with dl_set_snapshot_retain: after each successful publish, the oldest versions beyond the most-recent n are pruned. n == 0 (the default) restores keep-all.

dl_set_snapshot_retain(db, 5);   /* keep the 5 most-recent versions */
dl_set_snapshot_retain(db, 0);   /* back to keep-all */

A pruned version is gone — querying it returns -1 (loud), matching the nonexistent-version contract.

Concurrency model

This fits the engine’s single-writer / multiple-reader model. Readers hold mmap views and keep reading valid data even after a retention prune unlinks the underlying snapshot directory. A reader holding an mmap of snapshots/<V>/<rel>.dafsa keeps reading valid data after the pruner removes the directory — the unlink does not disturb the open mapping.