Skip to content

Branching & Time Travel

Branching & Time Travel

HeliosDB-Lite provides Git-like branching for databases and point-in-time queries with AS OF TIMESTAMP. Branches are copy-on-write, creating instant snapshots with minimal storage overhead.

Prerequisites

  • HeliosDB-Lite v3.5 or later with time_travel_enabled = true (enabled by default)
  • Access to the SQL shell (REPL, PostgreSQL wire protocol, or REST API)

Part A — Time Travel

Step 1 — Create a Table and Insert Data

CREATE TABLE accounts (
id INT PRIMARY KEY,
name TEXT,
balance FLOAT8
);
INSERT INTO accounts VALUES (1, 'Alice', 1000.00);
INSERT INTO accounts VALUES (2, 'Bob', 2000.00);

Every committed transaction creates a versioned snapshot. HeliosDB stores version keys with the pattern v:{table}:{row_id}:{timestamp} so historical states can be reconstructed.

Step 2 — Record a Timestamp, Then Modify Data

Note the current time, then perform some changes:

-- Remember this timestamp (e.g., '2025-03-26 10:00:00')
SELECT NOW();
UPDATE accounts SET balance = 500.00 WHERE id = 1;
UPDATE accounts SET balance = 3000.00 WHERE id = 2;

Step 3 — Query Current State

SELECT * FROM accounts;
id | name | balance
----+-------+---------
1 | Alice | 500.00
2 | Bob | 3000.00

Step 4 — Query Historical State with AS OF TIMESTAMP

Retrieve the data as it existed before the updates:

SELECT * FROM accounts AS OF TIMESTAMP '2025-03-26 10:00:00';
id | name | balance
----+-------+---------
1 | Alice | 1000.00
2 | Bob | 2000.00

The query reads from the versioned snapshot closest to the specified timestamp without modifying any data.

Step 5 — AS OF Variants

HeliosDB supports three AS OF targets:

-- By wall-clock timestamp
SELECT * FROM accounts AS OF TIMESTAMP '2025-03-26 10:00:00';
-- By internal transaction ID
SELECT * FROM accounts AS OF TRANSACTION 42;
-- By System Change Number (Oracle-compatible)
SELECT * FROM accounts AS OF SCN 1000;

Step 6 — Snapshot Metadata

Query system views to see available snapshots:

SELECT * FROM helios_snapshots;
timestamp | transaction_id | scn | wall_clock_time | gc_eligible
--------------+----------------+------+--------------------------+------------
1711451200 | 1 | 1 | 2025-03-26T09:00:00Z | true
1711454800 | 5 | 5 | 2025-03-26T10:00:00Z | true

Part B — Branching

Step 7 — Create a Branch

Create a named branch from the current state:

CREATE BRANCH dev AS OF NOW;

This captures a point-in-time snapshot. The branch uses copy-on-write storage: it shares all existing data with the parent and only stores new or modified keys.

You can also branch from a historical point:

CREATE BRANCH rollback_point AS OF TIMESTAMP '2025-03-26 10:00:00';

Step 8 — Work on a Branch

Switch to a branch context to read and write data in isolation. Branch operations are available through the REST API, embedded Rust API, or programmatic access:

-- Via REST API
POST /api/v1/query
{
"sql": "INSERT INTO accounts VALUES (3, 'Charlie', 500.00)",
"branch": "dev"
}
-- Via embedded Rust API
db.execute_on_branch("dev", "INSERT INTO accounts VALUES (3, 'Charlie', 500.00)")?;

Changes on the dev branch are isolated. Querying the main branch still shows only Alice and Bob.

Step 9 — Branch Isolation

Each branch maintains its own view of the data:

-- On branch 'dev':
SELECT * FROM accounts;
id | name | balance
----+---------+---------
1 | Alice | 500.00
2 | Bob | 3000.00
3 | Charlie | 500.00
-- On main:
SELECT * FROM accounts;
id | name | balance
----+-------+---------
1 | Alice | 500.00
2 | Bob | 3000.00

Step 10 — Merge a Branch

Merge changes from one branch into another:

MERGE BRANCH dev INTO main;

HeliosDB supports multiple merge strategies:

StrategyBehavior
AutoPrefer source branch on conflicts
ManualFail on any conflict
TheirsAlways prefer source branch changes
OursAlways prefer target branch changes
MERGE BRANCH dev INTO main WITH (strategy = 'manual');

If conflicts are detected with the Manual strategy, the merge fails and reports which keys conflict.

Step 11 — Drop a Branch

DROP BRANCH dev;
DROP BRANCH IF EXISTS rollback_point;

Dropping a branch marks it as Dropped and frees copy-on-write storage that is no longer referenced.

Tips and Troubleshooting

  • Time travel requires versioning: The time_travel_enabled setting must be true (the default). Without it, historical versions are not retained.
[storage]
time_travel_enabled = true
  • Garbage collection: Old snapshots are garbage-collected periodically. If you need to query very old timestamps, ensure GC retention is configured long enough. Snapshots marked gc_eligible = true will eventually be reclaimed.

  • Storage overhead: Time travel and branching store additional version keys. For write-heavy workloads, monitor disk usage and tune GC frequency.

  • Branch naming: Branch names are case-sensitive strings. Use short, descriptive names like dev, staging, or experiment-42.

  • Performance: AS OF queries have less than 2x overhead compared to current-time queries. The snapshot manager caches recent lookups in an LRU cache for fast access.

  • Concurrent branches: Multiple branches can be active simultaneously. Each branch tracks its own set of modified keys independently.