Skip to content

VersionLog Usage

Use this page when you want to enable VersionLog and call history, point-in-time read, or rollback APIs. For storage layout, recovery, sidecars, retention, and operational limits, see VersionLog Architecture and VersionLog Operations.

Version history is disabled by default. Enable it before opening the engine.

akkaradb::engine::AkkEngineOptions opts;
opts.paths.dataDir = "data/history";
opts.components.versionLogEnabled = true;
opts.vlog.syncMode = akkaradb::engine::vlog::VLogSyncMode::BATCHED_SYNC;
auto db = akkaradb::engine::AkkEngine::open(std::move(opts));

When paths.dataDir is set, paths.versionLogPath is derived as <dataDir>/history.akvlog. If you set opts.vlog.logPath directly, it overrides the derived path.

VersionLog and WAL are separate components. A durable latest-value path does not automatically mean every history entry has crossed a VersionLog sync boundary.

ModeBehaviorTypical use
SYNCWrites and durability-syncs each VersionLog entry before returning.Strongest history durability, lower write throughput.
ASYNCUpdates the readable history immediately and flushes the file through a background worker.Higher throughput when a small history durability window is acceptable.
BATCHED_SYNCUses the background worker, then durability-syncs grouped batches.Balanced mode for durable history without one sync per write.

groupN, groupMicros, and groupBytes control async and batched flush grouping. asyncMaxPendingBytes limits queued history bytes and can apply backpressure to writers.

Use AkkEngine::forceSync() at application checkpoint boundaries when recent history must cross the durable boundary before continuing.

VersionLog compression is opt-in.

opts.vlog.codec = akkaradb::engine::vlog::VLogCodec::ZSTD;
opts.vlog.zstdCompressionLevel = 1;

Compressed records retain their original value size internally and are decompressed before VersionEntry values are returned. A record is stored compressed only when the compressed payload plus its size prefix is smaller than the raw value.

By default, VersionLog uses segmented storage with segmentBytes = 64 MiB. Set retention boundaries when old history should be pruned from closed segments.

opts.vlog.segmentBytes = 64ULL * 1024ULL * 1024ULL;
opts.vlog.retentionDays = 30;
opts.vlog.retentionMinCommitSeq = 0;

retentionDays and retentionMinCommitSeq are ORed. A closed segment is eligible when either boundary matches. Before deletion, VersionLog writes synthetic retention-base entries for keys whose boundary state would otherwise disappear, so retained history still has a usable starting point.

Queries before the retained base boundary are unavailable. history() may return a synthetic first entry marked with VLOG_FLAG_RETENTION_BASE.

For write-heavy local workloads, PARALLEL admission can persist VersionLog records through independent lane workers.

opts.vlog.syncMode = akkaradb::engine::vlog::VLogSyncMode::ASYNC;
opts.vlog.writeAdmission = akkaradb::engine::vlog::VLogWriteAdmissionMode::PARALLEL;
opts.vlog.parallelWriteLanes = 4;
opts.vlog.parallelPendingLimitScope =
akkaradb::engine::vlog::VLogParallelPendingLimitScope::GLOBAL;

PARALLEL requires syncMode = ASYNC. Writes for the same key remain ordered by stable key fingerprint lane selection. GLOBAL applies asyncMaxPendingBytes once across all lanes; PER_LANE applies it independently per lane.

Every successful write appends a VersionEntry for the key.

db->put(bytes("profile:1"), bytes("v1"));
db->put(bytes("profile:1"), bytes("v2"));
db->remove(bytes("profile:1"));
auto history = db->history(bytes("profile:1"));
for (const auto& entry : history) {
const uint64_t seq = entry.seq;
const uint64_t sourceNodeId = entry.sourceNodeId;
const uint64_t timestampNs = entry.timestampNs;
const uint8_t flags = entry.flags;
}

history(key) returns entries ordered by sequence number. If VersionLog is disabled or the key has no history, it returns an empty vector.

FieldMeaning
seqMonotonic engine sequence number for the mutation. Use this as the input to getAt() or rollback APIs.
sourceNodeIdOrigin of the mutation. Local writes use the local source, replica-applied writes carry the source node, and rollback writes use ROLLBACK_NODE.
timestampNsAppend timestamp recorded by the engine. Useful for diagnostics; not the ordering key.
flagsStorage flags. Rollback and retention-base entries are marked here.
valuePublic value bytes. Compressed VersionLog records are decompressed before exposure.

Treat seq as the stable history cursor. Wall-clock time is metadata; it is not used to resolve getAt().

getAt(key, seq) returns the latest historical value whose sequence is less than or equal to seq.

db->put(bytes("profile:1"), bytes("v1"));
auto h1 = db->history(bytes("profile:1"));
db->put(bytes("profile:1"), bytes("v2"));
auto previous = db->getAt(bytes("profile:1"), h1.front().seq);
if (previous) {
// previous == "v1"
}

If the matching historical entry is a tombstone, getAt() returns std::nullopt. If VersionLog is disabled, it also returns std::nullopt.

APIVersionLog disabledMissing key or no visible valueClosed engine
history(key)Empty vectorEmpty vectorThrows
getAt(key, seq)std::nulloptstd::nulloptThrows
rollbackKey(key, seq)ThrowsWrites a tombstone if no older value existsThrows
rollbackTo(seq)ThrowsSkips keys already at or before the targetThrows

The rollback APIs are write APIs. They reserve new sequence numbers, append WAL and VersionLog records when enabled, update MemTable, and ship through Cluster when replication is enabled.

rollbackKey(key, targetSeq) writes a new record that restores the key to its state at targetSeq.

auto history = db->history(bytes("profile:1"));
if (!history.empty()) {
db->rollbackKey(bytes("profile:1"), history.front().seq);
}

Rollback is not an in-place rewrite of the history file. It appends a new engine mutation, marks the VersionLog entry with the rollback flag, and preserves the previous history.

rollbackTo(targetSeq) scans VersionLog history and restores every key whose latest known version is newer than targetSeq.

db->rollbackTo(checkpointSeq);

Use this carefully. It creates new writes for all affected keys, and in cluster mode those rollback writes are shipped through the replication runtime.

AkkEngine::forceSync() syncs both WAL and VersionLog when they are enabled.

db->forceSync();
db->close();

close() stops VersionLog workers, flushes files, writes derived sidecars where applicable, and surfaces any async write error that occurred earlier.