HeliosDB Transactions User Guide
HeliosDB Transactions User Guide
Version: 1.0 Last Updated: January 4, 2026 Feature Status: Production Ready (100%)
Table of Contents
- Overview
- Getting Started
- Transaction Basics
- Isolation Levels
- Savepoints
- Distributed Transactions
- Error Handling and Recovery
- Deadlock Prevention
- Performance Optimization
- Best Practices
- Troubleshooting
- API Reference
Overview
HeliosDB provides full ACID-compliant transaction support designed for high-concurrency workloads. Transactions ensure data integrity even under concurrent access, system failures, or network partitions.
ACID Properties
| Property | Description | HeliosDB Implementation |
|---|---|---|
| Atomicity | All operations succeed or all fail | WAL-based recovery |
| Consistency | Data always moves from valid state to valid state | Constraint enforcement |
| Isolation | Transactions don’t interfere with each other | MVCC with multiple isolation levels |
| Durability | Committed data survives system failures | WAL + checkpointing |
Key Features
- Lock-Free MVCC: High concurrency without blocking
- Multiple Isolation Levels: From READ UNCOMMITTED to SERIALIZABLE
- Savepoints: Partial rollback within transactions
- Distributed 2PC: Two-phase commit across nodes
- XA Support: Integration with external transaction coordinators
- Automatic Deadlock Detection: With configurable resolution strategies
Performance Characteristics
| Metric | Typical Value | Notes |
|---|---|---|
| Transaction Start | <1ms | Lock-free |
| Single Row Update | 1-3ms | Including WAL write |
| Transaction Commit | 3-7ms | With fsync |
| Deadlock Detection | <50ms | Immediate detection |
| Recovery Time | <30s | For 10GB database |
Getting Started
Prerequisites
- HeliosDB v7.0 or later
- Understanding of SQL basics
- Configured database connection
Quick Start (5 minutes)
1. Basic Transaction
-- Start a transactionBEGIN;
-- Perform operationsINSERT INTO accounts (id, balance) VALUES (1, 1000);INSERT INTO accounts (id, balance) VALUES (2, 500);
-- Commit to make changes permanentCOMMIT;2. Transaction with Rollback
-- Start a transactionBEGIN;
-- Perform operationsUPDATE accounts SET balance = balance - 100 WHERE id = 1;UPDATE accounts SET balance = balance + 100 WHERE id = 2;
-- Oops, wrong amount! Roll back all changesROLLBACK;3. Safe Money Transfer
-- Atomic money transferBEGIN;
-- Check balance firstSELECT balance FROM accounts WHERE id = 1 FOR UPDATE;
-- If balance >= 100, proceedUPDATE accounts SET balance = balance - 100 WHERE id = 1;UPDATE accounts SET balance = balance + 100 WHERE id = 2;
-- If all succeeded, commitCOMMIT;Transaction Basics
Starting Transactions
-- Standard BEGINBEGIN;
-- With explicit transaction keywordBEGIN TRANSACTION;
-- With isolation levelBEGIN TRANSACTION ISOLATION LEVEL SERIALIZABLE;
-- With read-only modeBEGIN TRANSACTION READ ONLY;
-- Combined optionsBEGIN TRANSACTION ISOLATION LEVEL REPEATABLE READ READ ONLY;Committing Transactions
-- Standard commitCOMMIT;
-- Alternative syntaxEND;
-- With options (rarely needed)COMMIT AND CHAIN; -- Immediately starts new transactionRolling Back Transactions
-- Full rollbackROLLBACK;
-- Alternative syntaxABORT;
-- With chain (starts new transaction)ROLLBACK AND CHAIN;Implicit vs Explicit Transactions
-- Implicit: Each statement is its own transactionINSERT INTO users (name) VALUES ('Alice'); -- Auto-commits
-- Explicit: Multiple statements in one transactionBEGIN; INSERT INTO users (name) VALUES ('Bob'); INSERT INTO users (name) VALUES ('Charlie');COMMIT; -- Both inserts committed togetherRead-Only Transactions
-- Read-only transaction (better performance for reads)BEGIN TRANSACTION READ ONLY;
SELECT * FROM accounts WHERE balance > 1000;SELECT * FROM transactions WHERE account_id = 1;
COMMIT;Benefits of read-only transactions:
- No write locks acquired
- Can use read replicas
- Better snapshot consistency
- Slightly lower overhead
Isolation Levels
Overview
| Level | Dirty Read | Non-Repeatable Read | Phantom Read | Serialization Anomaly |
|---|---|---|---|---|
| READ UNCOMMITTED | Possible | Possible | Possible | Possible |
| READ COMMITTED | No | Possible | Possible | Possible |
| REPEATABLE READ | No | No | Possible* | Possible |
| SERIALIZABLE | No | No | No | No |
*In HeliosDB’s implementation, REPEATABLE READ uses snapshot isolation which prevents phantoms in most cases.
READ UNCOMMITTED
Allows reading uncommitted data (dirty reads). Rarely used.
BEGIN TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;
-- Can see uncommitted changes from other transactionsSELECT * FROM accounts;
COMMIT;READ COMMITTED (Default)
Each statement sees only committed data. The default and most commonly used level.
BEGIN TRANSACTION ISOLATION LEVEL READ COMMITTED;
-- First query sees committed data as of query startSELECT balance FROM accounts WHERE id = 1; -- Returns 1000
-- If another transaction commits a change here...
-- Second query sees the new committed dataSELECT balance FROM accounts WHERE id = 1; -- Might return 900
COMMIT;Best for: General applications, OLTP workloads, high concurrency
REPEATABLE READ
Transaction sees a consistent snapshot from the first query.
BEGIN TRANSACTION ISOLATION LEVEL REPEATABLE READ;
-- First query establishes the snapshotSELECT balance FROM accounts WHERE id = 1; -- Returns 1000
-- Even if another transaction commits a change...
-- This query sees the same snapshotSELECT balance FROM accounts WHERE id = 1; -- Still returns 1000
COMMIT;Best for: Reports, analytics, long-running reads
SERIALIZABLE
Strictest level. Transactions execute as if they were serial.
BEGIN TRANSACTION ISOLATION LEVEL SERIALIZABLE;
-- All operations checked for conflictsUPDATE accounts SET balance = balance - 100 WHERE id = 1;
-- If a conflict is detected...COMMIT; -- May fail with serialization_failureBest for: Financial transactions, inventory management, critical operations
Handling Serialization Failures
-- Retry loop patternDO $$DECLARE retry_count INT := 0; max_retries INT := 3;BEGIN LOOP BEGIN -- Start serializable transaction BEGIN TRANSACTION ISOLATION LEVEL SERIALIZABLE;
-- Perform operations UPDATE accounts SET balance = balance - 100 WHERE id = 1; UPDATE accounts SET balance = balance + 100 WHERE id = 2;
COMMIT; EXIT; -- Success, exit loop
EXCEPTION WHEN serialization_failure THEN retry_count := retry_count + 1; IF retry_count >= max_retries THEN RAISE EXCEPTION 'Max retries exceeded'; END IF; -- Small delay before retry PERFORM pg_sleep(0.1 * retry_count); END; END LOOP;END $$;Savepoints
Savepoints allow partial rollback within a transaction.
Creating Savepoints
BEGIN;
INSERT INTO orders (customer_id, total) VALUES (1, 500);SAVEPOINT after_order;
INSERT INTO order_items (order_id, product_id) VALUES (1, 100);SAVEPOINT after_first_item;
INSERT INTO order_items (order_id, product_id) VALUES (1, 200);-- Oops, wrong product!
ROLLBACK TO SAVEPOINT after_first_item;-- order and first item still there, second item rolled back
INSERT INTO order_items (order_id, product_id) VALUES (1, 201);-- Correct product added
COMMIT;Releasing Savepoints
BEGIN;
SAVEPOINT sp1;INSERT INTO logs (message) VALUES ('Step 1');
SAVEPOINT sp2;INSERT INTO logs (message) VALUES ('Step 2');
-- Release sp1, merge its changes into parentRELEASE SAVEPOINT sp1;
-- Now only sp2 can be rolled back toROLLBACK TO SAVEPOINT sp2; -- OKROLLBACK TO SAVEPOINT sp1; -- ERROR: savepoint does not exist
COMMIT;Nested Savepoints
BEGIN;
SAVEPOINT outer; INSERT INTO accounts (id, balance) VALUES (1, 1000);
SAVEPOINT inner; UPDATE accounts SET balance = 900 WHERE id = 1;
-- Decide to roll back the update only ROLLBACK TO SAVEPOINT inner;
-- Insert still preserved SELECT balance FROM accounts WHERE id = 1; -- Returns 1000
COMMIT;Savepoint Best Practices
-- Pattern: Try operation, rollback on errorBEGIN;
SAVEPOINT before_risky_operation;
BEGIN -- Risky operation CALL external_api_update();EXCEPTION WHEN OTHERS THEN ROLLBACK TO SAVEPOINT before_risky_operation; RAISE NOTICE 'External update failed, continuing without it';END;
-- Continue with rest of transactionUPDATE local_table SET synced = false;
COMMIT;Distributed Transactions
Two-Phase Commit (2PC)
For transactions spanning multiple HeliosDB nodes:
-- Phase 1: PrepareBEGIN;UPDATE accounts@node1 SET balance = balance - 100 WHERE id = 1;UPDATE accounts@node2 SET balance = balance + 100 WHERE id = 2;PREPARE TRANSACTION 'transfer_001';
-- Phase 2: Commit (on all nodes)COMMIT PREPARED 'transfer_001';
-- Or rollback if any node failsROLLBACK PREPARED 'transfer_001';XA Transactions
For integration with external transaction coordinators:
-- Start XA transactionXA START 'xid_12345';
-- Perform operationsINSERT INTO orders (id, total) VALUES (1, 500);
-- End work phaseXA END 'xid_12345';
-- Prepare for commitXA PREPARE 'xid_12345';
-- Commit (from coordinator)XA COMMIT 'xid_12345';
-- Or rollbackXA ROLLBACK 'xid_12345';Monitoring Distributed Transactions
-- List prepared transactionsSELECT * FROM pg_prepared_xacts;
-- Check for stuck transactionsSELECT gid, prepared, owner, databaseFROM pg_prepared_xactsWHERE prepared < NOW() - INTERVAL '5 minutes';
-- Recovery of stuck transactions (admin only)ROLLBACK PREPARED 'stuck_transaction_id';Error Handling and Recovery
Transaction States
START -> ACTIVE -> FAILED (on error) -> COMMITTED (on commit) -> ABORTED (on rollback)Error Handling Patterns
Basic Try-Catch
DO $$BEGIN BEGIN TRANSACTION;
-- Operations that might fail INSERT INTO orders VALUES (...); UPDATE inventory SET qty = qty - 1 WHERE ...;
COMMIT;
EXCEPTION WHEN unique_violation THEN ROLLBACK; RAISE NOTICE 'Duplicate order detected'; WHEN check_violation THEN ROLLBACK; RAISE NOTICE 'Invalid data'; WHEN OTHERS THEN ROLLBACK; RAISE;END $$;Automatic Retry
CREATE OR REPLACE FUNCTION safe_transfer( from_account INT, to_account INT, amount DECIMAL) RETURNS BOOLEAN AS $$DECLARE retry_count INT := 0; max_retries INT := 3;BEGIN LOOP BEGIN -- Serializable for safety SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;
-- Check and transfer IF (SELECT balance FROM accounts WHERE id = from_account) >= amount THEN UPDATE accounts SET balance = balance - amount WHERE id = from_account; UPDATE accounts SET balance = balance + amount WHERE id = to_account; RETURN TRUE; ELSE RETURN FALSE; -- Insufficient funds END IF;
EXCEPTION WHEN serialization_failure OR deadlock_detected THEN retry_count := retry_count + 1; IF retry_count >= max_retries THEN RAISE EXCEPTION 'Transfer failed after % retries', max_retries; END IF; -- Wait before retry PERFORM pg_sleep(0.1 * random()); END; END LOOP;END $$ LANGUAGE plpgsql;Recovery After Crash
HeliosDB automatically recovers from crashes using WAL:
-- Check recovery status after restartSELECT * FROM pg_stat_recovery;
-- Verify data consistencySELECT COUNT(*) FROM accounts;
-- Check for incomplete transactionsSELECT * FROM pg_stat_activityWHERE state = 'idle in transaction';Deadlock Prevention
How Deadlocks Occur
-- Transaction ABEGIN;UPDATE accounts SET balance = balance - 100 WHERE id = 1; -- Locks row 1-- Waits for row 2...
-- Transaction B (concurrent)BEGIN;UPDATE accounts SET balance = balance - 50 WHERE id = 2; -- Locks row 2UPDATE accounts SET balance = balance + 50 WHERE id = 1; -- Waits for row 1-- DEADLOCK!Prevention Strategies
1. Consistent Lock Ordering
-- Always lock in the same order (e.g., by ID ascending)BEGIN;
-- Lock both accounts in consistent orderSELECT * FROM accounts WHERE id IN (1, 2) ORDER BY id FOR UPDATE;
-- Now safe to update in any orderUPDATE accounts SET balance = balance - 100 WHERE id = 1;UPDATE accounts SET balance = balance + 100 WHERE id = 2;
COMMIT;2. Use NOWAIT
BEGIN;
-- Fail immediately if can't acquire lockSELECT * FROM accounts WHERE id = 1 FOR UPDATE NOWAIT;
-- If we get here, we have the lockUPDATE accounts SET balance = balance - 100 WHERE id = 1;
COMMIT;3. Use SKIP LOCKED
-- Process available rows, skip locked onesBEGIN;
-- Get unlocked work itemsSELECT * FROM work_queueWHERE status = 'pending'FOR UPDATE SKIP LOCKEDLIMIT 10;
-- Process them...
COMMIT;4. Set Lock Timeout
-- Fail if can't acquire lock within timeoutSET lock_timeout = '5s';
BEGIN;UPDATE accounts SET balance = balance - 100 WHERE id = 1;COMMIT;Deadlock Detection
HeliosDB automatically detects deadlocks and aborts one transaction:
-- View deadlock eventsSELECT * FROM pg_stat_databaseWHERE deadlocks > 0;
-- Enable detailed deadlock loggingSET log_lock_waits = ON;SET deadlock_timeout = '1s';Performance Optimization
Reducing Lock Contention
-- BAD: Long transaction with many locksBEGIN;SELECT * FROM large_table FOR UPDATE; -- Locks all rows-- ... long processing ...COMMIT;
-- GOOD: Short transactions with minimal locksBEGIN;SELECT id FROM large_table WHERE status = 'pending' LIMIT 100 FOR UPDATE;-- Quick processingUPDATE large_table SET status = 'processing' WHERE id IN (...);COMMIT;Batch Operations
-- BAD: Many small transactionsFOR i IN 1..10000 LOOP BEGIN; INSERT INTO events VALUES (i, ...); COMMIT;END LOOP;
-- GOOD: Batch in single transactionBEGIN;INSERT INTO eventsSELECT generate_series(1, 10000), ...;COMMIT;Advisory Locks
For application-level locking without row locks:
-- Acquire advisory lock (doesn't lock any rows)SELECT pg_advisory_lock(12345);
-- Do work...
-- Release lockSELECT pg_advisory_unlock(12345);
-- Or use try variant (non-blocking)SELECT pg_try_advisory_lock(12345); -- Returns true/falseConnection Pooling Considerations
-- Reset session state after returning to poolRESET ALL;DISCARD ALL;
-- Or use RESET for specific settingsRESET transaction_isolation;RESET lock_timeout;Best Practices
1. Keep Transactions Short
-- BAD: Long-running transactionBEGIN;SELECT * FROM orders WHERE date > '2020-01-01'; -- 1M rows-- ... process in application for 30 seconds ...COMMIT;
-- GOOD: Process in batchesDO $$DECLARE batch_size INT := 1000; offset_val INT := 0;BEGIN LOOP -- Short transaction per batch BEGIN; UPDATE orders SET processed = true WHERE id IN ( SELECT id FROM orders WHERE processed = false ORDER BY id LIMIT batch_size ); COMMIT;
offset_val := offset_val + batch_size; EXIT WHEN NOT FOUND; END LOOP;END $$;2. Use Appropriate Isolation Level
-- For most queries: READ COMMITTED (default)SELECT * FROM products WHERE category = 'electronics';
-- For reports: REPEATABLE READBEGIN TRANSACTION ISOLATION LEVEL REPEATABLE READ;SELECT COUNT(*) FROM orders WHERE date = CURRENT_DATE;SELECT SUM(total) FROM orders WHERE date = CURRENT_DATE;COMMIT;
-- For critical operations: SERIALIZABLEBEGIN TRANSACTION ISOLATION LEVEL SERIALIZABLE;-- Check inventory, create order, decrement inventoryCOMMIT;3. Handle Errors Properly
-- Always handle potential errorsBEGIN; -- Your operations INSERT INTO orders VALUES (...);
-- Explicit commit COMMIT;EXCEPTION WHEN OTHERS THEN -- Log the error RAISE NOTICE 'Error: %', SQLERRM; -- Clean up ROLLBACK; -- Re-raise or handle RAISE;END;4. Monitor Transaction Health
-- Check for long-running transactionsSELECT pid, usename, state, query, age(now(), xact_start) AS transaction_ageFROM pg_stat_activityWHERE state IN ('idle in transaction', 'active') AND xact_start < NOW() - INTERVAL '5 minutes'ORDER BY xact_start;
-- Check lock contentionSELECT blocked.pid AS blocked_pid, blocking.pid AS blocking_pid, blocked.query AS blocked_queryFROM pg_locks blockedJOIN pg_locks blocking ON blocking.locktype = blocked.locktype AND blocking.relation = blocked.relation AND blocking.pid != blocked.pidWHERE NOT blocked.granted;5. Use Explicit Locking Wisely
-- FOR UPDATE: Write lock (blocks other FOR UPDATE and writes)SELECT * FROM inventory WHERE product_id = 1 FOR UPDATE;
-- FOR SHARE: Read lock (blocks writes but not other reads)SELECT * FROM inventory WHERE product_id = 1 FOR SHARE;
-- FOR KEY SHARE: Minimal lock (blocks DELETEs but not updates)SELECT * FROM orders WHERE customer_id = 1 FOR KEY SHARE;
-- FOR NO KEY UPDATE: Allows key reads (doesn't block FOR KEY SHARE)SELECT * FROM orders WHERE id = 1 FOR NO KEY UPDATE;Troubleshooting
Common Issues
1. “idle in transaction” Sessions
-- Find idle transactionsSELECT pid, usename, state, query, age(now(), xact_start)FROM pg_stat_activityWHERE state = 'idle in transaction';
-- Terminate if stuckSELECT pg_terminate_backend(pid);2. Lock Timeout Errors
-- Increase timeout if neededSET lock_timeout = '30s';
-- Or check what's blockingSELECT blocked.pid, blocked.query, blocking.pid AS blocking_pidFROM pg_locks blockedJOIN pg_locks blocking ON blocking.relation = blocked.relationWHERE NOT blocked.granted;3. Serialization Failures
-- Add retry logic (see Error Handling section)-- Or consider lowering isolation level if appropriate4. Too Many Connections
-- Check connection countSELECT count(*) FROM pg_stat_activity;
-- Use connection pooling (PgBouncer, etc.)-- Set per-connection limitsALTER USER app_user CONNECTION LIMIT 50;Diagnostic Queries
-- Current transactionsSELECT pid, usename, state, query, xact_startFROM pg_stat_activityWHERE state != 'idle';
-- Lock statisticsSELECT * FROM pg_stat_database;
-- Current locksSELECT relation::regclass, mode, granted, pidFROM pg_locksWHERE relation IS NOT NULL;
-- Transaction statisticsSELECT xact_commit, xact_rollback, deadlocksFROM pg_stat_databaseWHERE datname = current_database();API Reference
Transaction Control Statements
| Statement | Description |
|---|---|
BEGIN / START TRANSACTION | Start a new transaction |
COMMIT / END | Commit current transaction |
ROLLBACK / ABORT | Roll back current transaction |
SAVEPOINT name | Create a savepoint |
ROLLBACK TO SAVEPOINT name | Roll back to savepoint |
RELEASE SAVEPOINT name | Release (remove) savepoint |
Transaction Options
BEGIN TRANSACTION [ISOLATION LEVEL {READ UNCOMMITTED | READ COMMITTED | REPEATABLE READ | SERIALIZABLE}] [READ WRITE | READ ONLY] [DEFERRABLE | NOT DEFERRABLE];Lock Modes
| Mode | Description | Conflicts With |
|---|---|---|
FOR UPDATE | Exclusive write lock | All |
FOR NO KEY UPDATE | Write lock, allows key reads | FOR UPDATE, FOR SHARE |
FOR SHARE | Shared read lock | FOR UPDATE, FOR NO KEY UPDATE |
FOR KEY SHARE | Minimal shared lock | FOR UPDATE |
Session Variables
| Variable | Description | Default |
|---|---|---|
transaction_isolation | Default isolation level | read committed |
lock_timeout | Max wait for locks | 0 (infinite) |
statement_timeout | Max statement runtime | 0 (infinite) |
idle_in_transaction_session_timeout | Max idle time | 0 (infinite) |
Support: For issues or questions, contact transactions-support@heliosdb.com
Related Documentation:
License: Included with all HeliosDB editions.
Version: HeliosDB v7.0+