HeliosDB Time-Travel Debugging User Guide
HeliosDB Time-Travel Debugging User Guide
Version: 1.0 Last Updated: January 4, 2026 Feature Status: Production Ready (100%)
Table of Contents
- Overview
- Getting Started
- Core Concepts
- Basic Usage
- Advanced Queries
- Flashback Queries (Oracle-Compatible)
- Retention Policies
- Performance Tuning
- Use Cases
- Troubleshooting
- Best Practices
- API Reference
Overview
HeliosDB Time-Travel Debugging enables you to query historical states of your data at any point in time. This powerful feature provides:
- Point-in-Time Queries: See your data as it existed at any timestamp
- Version History: Track all changes to any row over time
- Oracle Compatibility: Full Flashback Query support (AS OF SCN, VERSIONS BETWEEN)
- Efficient Storage: Delta compression keeps overhead under 10%
- Production Ready: ACID-compliant with snapshot isolation
Key Benefits
| Benefit | Description |
|---|---|
| Debugging | Investigate what data looked like when a bug occurred |
| Audit Trail | Complete history of all changes for compliance |
| Data Recovery | Retrieve accidentally deleted or modified data |
| Analytics | Analyze trends and changes over time |
| Testing | Compare data states before and after changes |
Performance Characteristics
| Metric | Target | Typical |
|---|---|---|
| Storage Overhead (7-day history) | <10% | 5-8% |
| Query Latency (vs current) | <2x | 1.3-1.8x |
| Write Throughput | 1M+ ops/sec | 1.2M ops/sec |
| Temporal Index Lookup | <100ms | 20-50ms |
Getting Started
Prerequisites
- HeliosDB v7.0 or later
- Temporal extension enabled
- Sufficient storage for version history (estimate: 5-10% of data size per retained day)
Quick Start (5 minutes)
1. Enable Time-Travel for a Table
-- Create a temporal table (automatic version tracking)CREATE TABLE orders ( id SERIAL PRIMARY KEY, customer_id INT NOT NULL, total DECIMAL(10,2), status VARCHAR(50), updated_at TIMESTAMP DEFAULT NOW()) WITH ( temporal_enabled = true, retention_days = 7);
-- Or enable on existing tableALTER TABLE orders SET (temporal_enabled = true);2. Make Some Changes
-- Insert initial dataINSERT INTO orders (customer_id, total, status)VALUES (1, 100.00, 'pending');
-- Wait a moment, then updateUPDATE orders SET status = 'processing' WHERE id = 1;
-- Wait again, then updateUPDATE orders SET status = 'shipped', total = 105.00 WHERE id = 1;3. Query Historical States
-- See data as it was 1 minute agoSELECT * FROM ordersAS OF TIMESTAMP (NOW() - INTERVAL '1 minute')WHERE id = 1;
-- See all versions of a rowSELECT * FROM ordersVERSIONS BETWEEN TIMESTAMP (NOW() - INTERVAL '1 hour') AND NOW()WHERE id = 1;Core Concepts
Version Chain
Every change creates a new version in the history:
Time: T1 T2 T3 NOW | | | | v v v v [v1] -----> [v2] -----> [v3] -----> [v4] INSERT UPDATE UPDATE CURRENT pending processing shipped shippedTimestamps and SCNs
HeliosDB tracks changes using two systems:
- Timestamp: Human-readable time (e.g.,
2026-01-04 10:30:00) - SCN (System Change Number): Monotonic counter for precise ordering
Both can be used for temporal queries:
-- By timestampSELECT * FROM orders AS OF TIMESTAMP '2026-01-04 10:30:00';
-- By SCNSELECT * FROM orders AS OF SCN 12345678;Storage Model
Current Table (Fast Lookups)+----+-------------+-------+------------+| id | customer_id | total | status |+----+-------------+-------+------------+| 1 | 1 | 105 | shipped | <- Current version+----+-------------+-------+------------+
History Table (Delta Compressed)+----+--------+-----------------+-------------+-------------+| id | seq | columns_changed | valid_from | valid_to |+----+--------+-----------------+-------------+-------------+| 1 | 1 | {all} | T1 | T2 || 1 | 2 | {status} | T2 | T3 || 1 | 3 | {status,total} | T3 | NULL |+----+--------+-----------------+-------------+-------------+Basic Usage
AS OF TIMESTAMP Queries
Query data as it existed at a specific point in time:
-- Exact timestampSELECT * FROM ordersAS OF TIMESTAMP '2026-01-04 10:30:00';
-- Relative timestampSELECT * FROM ordersAS OF TIMESTAMP (NOW() - INTERVAL '1 hour');
-- With filtersSELECT order_id, total, statusFROM ordersAS OF TIMESTAMP '2026-01-04 10:00:00'WHERE customer_id = 100 AND total > 50.00;
-- Join with current dataSELECT h.id, h.status AS old_status, c.status AS current_statusFROM orders AS OF TIMESTAMP '2026-01-04 10:00:00' hJOIN orders c ON h.id = c.idWHERE h.status != c.status;VERSIONS BETWEEN Queries
Retrieve all versions of a row within a time range:
-- All versions in last hourSELECT *FROM ordersVERSIONS BETWEEN TIMESTAMP (NOW() - INTERVAL '1 hour') AND NOW()WHERE id = 1ORDER BY versions_starttime;
-- With version metadataSELECT id, status, versions_starttime, -- When this version became valid versions_endtime, -- When this version was superseded versions_operation -- INSERT, UPDATE, or DELETEFROM ordersVERSIONS BETWEEN TIMESTAMP '2026-01-01' AND '2026-01-04'WHERE customer_id = 100;FROM…TO Range Queries
Query data changes within a time range:
-- All changes from yesterday to nowSELECT *FROM ordersFROM TIMESTAMP (NOW() - INTERVAL '1 day')TO TIMESTAMP NOW()ORDER BY versions_starttime;
-- From a specific point onwardsSELECT *FROM ordersFROM TIMESTAMP '2026-01-01 00:00:00'WHERE status = 'cancelled';Version History for Specific Keys
-- Complete history of a specific orderSELECT id, total, status, versions_starttime AS changed_at, versions_operation AS operationFROM ordersVERSIONS ALLWHERE id = 1ORDER BY versions_starttime;
-- Count changes per orderSELECT id, COUNT(*) AS version_count, MIN(versions_starttime) AS first_version, MAX(versions_starttime) AS last_versionFROM ordersVERSIONS BETWEEN TIMESTAMP (NOW() - INTERVAL '7 days') AND NOW()GROUP BY idORDER BY version_count DESCLIMIT 10;Advanced Queries
Comparing States at Different Times
-- Compare order status between two points in timeWITH before AS ( SELECT id, status, total FROM orders AS OF TIMESTAMP '2026-01-03 00:00:00'),after AS ( SELECT id, status, total FROM orders AS OF TIMESTAMP '2026-01-04 00:00:00')SELECT COALESCE(b.id, a.id) AS order_id, b.status AS status_before, a.status AS status_after, a.total - COALESCE(b.total, 0) AS total_changeFROM before bFULL OUTER JOIN after a ON b.id = a.idWHERE b.status IS DISTINCT FROM a.status OR b.total IS DISTINCT FROM a.total;Finding When Data Changed
-- Find when a specific order status changed to 'shipped'SELECT id, versions_starttime AS shipped_at, totalFROM ordersVERSIONS BETWEEN TIMESTAMP (NOW() - INTERVAL '30 days') AND NOW()WHERE id = 12345 AND status = 'shipped' AND versions_operation IN ('INSERT', 'UPDATE')ORDER BY versions_starttimeLIMIT 1;Reconstructing Deleted Data
-- Find and recover deleted ordersSELECT *FROM ordersVERSIONS BETWEEN TIMESTAMP (NOW() - INTERVAL '7 days') AND NOW()WHERE versions_operation = 'DELETE';
-- Restore a deleted orderINSERT INTO orders (id, customer_id, total, status)SELECT id, customer_id, total, statusFROM ordersVERSIONS BETWEEN TIMESTAMP (NOW() - INTERVAL '1 day') AND NOW()WHERE id = 999 AND versions_operation = 'DELETE'ORDER BY versions_starttime DESCLIMIT 1;Audit Queries
-- Who changed what and when (requires audit extension)SELECT o.id, o.status, o.versions_starttime, o.versions_operation, a.user_name, a.client_ipFROM orders VERSIONS ALL oJOIN audit_log a ON a.table_name = 'orders' AND a.row_id = o.id AND a.timestamp = o.versions_starttimeWHERE o.id = 12345ORDER BY o.versions_starttime;Flashback Queries (Oracle-Compatible)
HeliosDB provides full Oracle Flashback Query compatibility.
AS OF SCN
-- Get current SCNSELECT current_scn(); -- Returns: 12345678
-- Query at specific SCNSELECT * FROM ordersAS OF SCN 12345670;
-- Use SCN for precise consistency across tablesSELECT o.*, c.nameFROM orders AS OF SCN 12345670 oJOIN customers AS OF SCN 12345670 c ON o.customer_id = c.id;VERSIONS BETWEEN SCN
-- All versions between two SCNsSELECT id, status, versions_startscn, versions_endscn, versions_operationFROM ordersVERSIONS BETWEEN SCN 12345000 AND 12346000WHERE customer_id = 100;Flashback Transaction
-- See all changes made by a specific transactionSELECT *FROM ordersFLASHBACK TRANSACTION transaction_id = 'txn_abc123';
-- Undo a transaction's changesFLASHBACK TABLE ordersTO BEFORE TRANSACTION 'txn_abc123';SCN-Timestamp Mapping
-- Convert timestamp to SCNSELECT timestamp_to_scn('2026-01-04 10:30:00');
-- Convert SCN to timestampSELECT scn_to_timestamp(12345678);
-- Find SCN range for a time periodSELECT timestamp_to_scn('2026-01-04 00:00:00') AS start_scn, timestamp_to_scn('2026-01-05 00:00:00') AS end_scn;Retention Policies
Configuring Retention
-- Set retention at table creationCREATE TABLE events ( id SERIAL PRIMARY KEY, event_type VARCHAR(50), payload JSONB, created_at TIMESTAMP DEFAULT NOW()) WITH ( temporal_enabled = true, retention_days = 30, -- Keep 30 days of history retention_size_gb = 10, -- Max 10GB of history retention_max_versions = 100 -- Max 100 versions per row);
-- Modify retention on existing tableALTER TABLE orders SET ( retention_days = 14, retention_cleanup_strategy = 'time_based_oldest');Cleanup Strategies
| Strategy | Description | Use Case |
|---|---|---|
time_based_oldest | Delete oldest versions first | Standard FIFO cleanup |
time_based_newest | Keep oldest, delete newest | Preserve historical snapshots |
largest_first | Delete largest versions first | Optimize storage quickly |
smallest_first | Delete smallest versions first | Keep detailed history |
-- Configure cleanup strategyALTER TABLE orders SET ( retention_cleanup_strategy = 'time_based_oldest');Manual Cleanup
-- Force immediate cleanupCALL temporal_cleanup('orders');
-- Cleanup with specific cutoffCALL temporal_cleanup('orders', TIMESTAMP '2026-01-01 00:00:00');
-- Check cleanup statisticsSELECT * FROM temporal_cleanup_statsWHERE table_name = 'orders';Monitoring Retention
-- View temporal storage usageSELECT table_name, current_row_count, history_row_count, history_size_bytes, oldest_version_time, newest_version_time, retention_daysFROM temporal_table_stats;
-- Check for tables exceeding retention limitsSELECT table_name, history_size_bytesFROM temporal_table_statsWHERE history_size_bytes > retention_size_limit_bytes;Performance Tuning
Index Optimization
-- Create temporal index for faster lookupsCREATE INDEX idx_orders_temporalON orders (id, versions_starttime)WITH (temporal = true);
-- Create index on frequently queried historical columnsCREATE INDEX idx_orders_status_temporalON orders (status, versions_starttime)WITH (temporal = true);Query Optimization Tips
-- GOOD: Specific time rangeSELECT * FROM ordersAS OF TIMESTAMP '2026-01-04 10:30:00'WHERE id = 12345; -- Uses index
-- LESS EFFICIENT: Wide time range scanSELECT * FROM ordersVERSIONS BETWEEN TIMESTAMP '2020-01-01' AND NOW() -- Scans entire historyWHERE status = 'pending';
-- BETTER: Narrow the rangeSELECT * FROM ordersVERSIONS BETWEEN TIMESTAMP (NOW() - INTERVAL '7 days') AND NOW()WHERE status = 'pending';Configuration Parameters
-- View temporal configurationSHOW temporal_query_timeout;SHOW temporal_cache_size;SHOW temporal_parallel_workers;
-- Tune for better performanceSET temporal_cache_size = '1GB'; -- Increase cacheSET temporal_parallel_workers = 4; -- Enable parallel scansSET temporal_query_timeout = '30s'; -- Query timeoutMonitoring Performance
-- Check temporal query statisticsSELECT query_type, avg_execution_time_ms, max_execution_time_ms, total_executions, cache_hit_rateFROM temporal_query_statsORDER BY avg_execution_time_ms DESC;
-- Monitor version chain length (long chains = slower queries)SELECT table_name, avg_versions_per_row, max_versions_per_row, rows_with_long_chainsFROM temporal_chain_statsWHERE avg_versions_per_row > 50;Use Cases
1. Debugging Production Issues
-- What did the order look like when the error occurred?SELECT * FROM ordersAS OF TIMESTAMP '2026-01-04 10:30:45' -- Error timestamp from logsWHERE id = 99999;
-- What changed just before the error?SELECT *FROM ordersVERSIONS BETWEEN TIMESTAMP '2026-01-04 10:25:00' AND '2026-01-04 10:31:00'WHERE id = 99999ORDER BY versions_starttime;2. Compliance and Audit
-- Generate audit report for a customerSELECT o.id AS order_id, o.total, o.status, o.versions_starttime AS changed_at, o.versions_operation AS actionFROM orders VERSIONS ALL oWHERE o.customer_id = 12345 AND o.versions_starttime BETWEEN '2026-01-01' AND '2026-02-01'ORDER BY o.versions_starttime;
-- Export complete change historyCOPY ( SELECT * FROM orders VERSIONS BETWEEN TIMESTAMP '2026-01-01' AND NOW() ORDER BY id, versions_starttime) TO '/tmp/orders_audit.csv' WITH CSV HEADER;3. Data Recovery
-- Find accidentally updated rowsSELECT DISTINCT idFROM ordersVERSIONS BETWEEN TIMESTAMP '2026-01-04 14:00:00' AND '2026-01-04 14:05:00'WHERE versions_operation = 'UPDATE' AND status = 'cancelled'; -- Shouldn't have been cancelled
-- Restore to previous stateUPDATE orders oSET status = h.status, total = h.totalFROM ( SELECT DISTINCT ON (id) * FROM orders AS OF TIMESTAMP '2026-01-04 13:59:00' WHERE id IN (/* affected IDs */)) hWHERE o.id = h.id;4. Trend Analysis
-- Daily order status distribution over past weekWITH daily_snapshot AS ( SELECT date_trunc('day', d) AS snapshot_date, status, COUNT(*) AS count FROM generate_series( NOW() - INTERVAL '7 days', NOW(), INTERVAL '1 day' ) d CROSS JOIN LATERAL ( SELECT status FROM orders AS OF TIMESTAMP d ) o GROUP BY 1, 2)SELECT * FROM daily_snapshotORDER BY snapshot_date, status;Troubleshooting
Common Issues
Query Returns No Results
-- Check if temporal is enabledSELECT temporal_enabled FROM pg_tablesWHERE tablename = 'orders';
-- Check if data exists in the time rangeSELECT MIN(versions_starttime) AS earliest, MAX(versions_starttime) AS latestFROM orders VERSIONS ALL;
-- Verify timestamp formatSELECT '2026-01-04 10:30:00'::timestamp; -- Should not errorSlow Temporal Queries
-- Check version chain lengthSELECT id, COUNT(*) AS version_countFROM orders VERSIONS ALLGROUP BY idORDER BY version_count DESCLIMIT 10;
-- Check if temporal indexes existSELECT indexname, indexdefFROM pg_indexesWHERE tablename = 'orders' AND indexdef LIKE '%temporal%';
-- Run EXPLAIN on slow queriesEXPLAIN ANALYZESELECT * FROM ordersAS OF TIMESTAMP '2026-01-04 10:30:00'WHERE customer_id = 100;Storage Growing Too Fast
-- Check history sizeSELECT * FROM temporal_table_statsWHERE table_name = 'orders';
-- Reduce retention if neededALTER TABLE orders SET (retention_days = 7);
-- Force cleanupCALL temporal_cleanup('orders');Error Messages
| Error | Cause | Solution |
|---|---|---|
temporal_not_enabled | Temporal feature not enabled | ALTER TABLE ... SET (temporal_enabled = true) |
timestamp_out_of_range | Requested time before retention | Query within retention period |
scn_not_found | Invalid or expired SCN | Use timestamp instead or recent SCN |
version_chain_too_long | Too many versions | Reduce update frequency or enable cleanup |
Best Practices
1. Choose Appropriate Retention
- Development/Testing: 1-7 days
- Production (debugging): 7-30 days
- Compliance (audit): 365+ days (consider tiered storage)
2. Index Strategy
-- Index primary key for temporal lookupsCREATE INDEX idx_orders_id_temporalON orders (id, versions_starttime)WITH (temporal = true);
-- Index frequently filtered columnsCREATE INDEX idx_orders_customer_temporalON orders (customer_id, versions_starttime)WITH (temporal = true);3. Limit Query Scope
-- GOOD: Narrow time range + specific filterSELECT * FROM ordersAS OF TIMESTAMP (NOW() - INTERVAL '1 hour')WHERE id = 12345;
-- AVOID: Wide range without filterSELECT * FROM ordersVERSIONS BETWEEN TIMESTAMP '2020-01-01' AND NOW();4. Use SCNs for Consistency
-- Capture SCN at start of analysisDO $$DECLARE analysis_scn BIGINT := current_scn();BEGIN -- All queries use same SCN for consistency SELECT * FROM orders AS OF SCN analysis_scn; SELECT * FROM customers AS OF SCN analysis_scn; SELECT * FROM payments AS OF SCN analysis_scn;END $$;5. Monitor and Maintain
-- Regular monitoring querySELECT table_name, history_size_bytes / 1024 / 1024 AS history_mb, avg_versions_per_row, CASE WHEN history_size_bytes > retention_size_limit_bytes THEN 'OVER LIMIT' WHEN history_size_bytes > retention_size_limit_bytes * 0.8 THEN 'WARNING' ELSE 'OK' END AS statusFROM temporal_table_statsORDER BY history_size_bytes DESC;API Reference
SQL Syntax
AS OF TIMESTAMP
SELECT columns FROM tableAS OF TIMESTAMP timestamp_expression[WHERE conditions];AS OF SCN
SELECT columns FROM tableAS OF SCN scn_number[WHERE conditions];VERSIONS BETWEEN TIMESTAMP
SELECT columns FROM tableVERSIONS BETWEEN TIMESTAMP start_time AND end_time[WHERE conditions];VERSIONS BETWEEN SCN
SELECT columns FROM tableVERSIONS BETWEEN SCN start_scn AND end_scn[WHERE conditions];VERSIONS ALL
SELECT columns FROM tableVERSIONS ALL[WHERE conditions];System Functions
| Function | Description | Example |
|---|---|---|
current_scn() | Get current SCN | SELECT current_scn() |
timestamp_to_scn(ts) | Convert timestamp to SCN | SELECT timestamp_to_scn('2026-01-04') |
scn_to_timestamp(scn) | Convert SCN to timestamp | SELECT scn_to_timestamp(12345678) |
temporal_cleanup(table) | Force cleanup | CALL temporal_cleanup('orders') |
Version Metadata Columns
| Column | Description |
|---|---|
versions_starttime | When this version became valid |
versions_endtime | When this version was superseded (NULL if current) |
versions_startscn | SCN when version became valid |
versions_endscn | SCN when version was superseded |
versions_operation | INSERT, UPDATE, or DELETE |
versions_xid | Transaction ID that created this version |
Support: For issues or questions, contact temporal-support@heliosdb.com or open an issue on GitHub.
Related Documentation:
License: Enterprise license required for production use.
Version: HeliosDB v7.0+ with Time-Travel extension