mdcms/sample-sites/neuraldb-docs/pages/nql-transactions.md
2026-05-18 14:30:49 +07:00

237 lines
6 KiB
Markdown

---
title: Transactions
sort: 140
section-id: query-language
keywords: transactions, ACID, isolation levels, MVCC, BEGIN, COMMIT, ROLLBACK
description: ACID transactions in NeuralDB — isolation levels, MVCC, savepoints, and advisory locks
language: en
---
# Transactions
NeuralDB provides full ACID transactions with MVCC (Multi-Version Concurrency Control). Unlike most vector databases, NeuralDB guarantees atomicity across both relational and vector data within a single transaction.
## ACID Guarantees
| Property | Guarantee |
|----------|---------|
| **Atomicity** | All operations in a transaction succeed or all are rolled back — including vector index updates |
| **Consistency** | Constraints (foreign keys, unique indexes, not null) are enforced at commit time |
| **Isolation** | Concurrent transactions do not see each other's uncommitted changes |
| **Durability** | Committed transactions survive crashes via the WAL |
## Basic Transaction Syntax
```sql
BEGIN;
-- Your operations here
INSERT INTO documents (content, embedding) VALUES ($1, $2);
UPDATE document_stats SET total_count = total_count + 1;
INSERT INTO audit_log (action, data) VALUES ('insert', $3);
COMMIT;
```
On error, roll back:
```sql
BEGIN;
INSERT INTO documents (content, embedding) VALUES ($1, $2);
-- Something went wrong
ROLLBACK;
```
## Isolation Levels
NeuralDB supports four isolation levels. Set them with `SET TRANSACTION ISOLATION LEVEL`:
```sql
BEGIN;
SET TRANSACTION ISOLATION LEVEL READ COMMITTED;
-- ... your queries ...
COMMIT;
```
### Read Committed (Default)
Each statement sees only rows committed before that statement began. Two successive reads within the same transaction may see different data if another transaction commits between them.
```sql
BEGIN;
-- Sees all rows committed before this SELECT
SELECT COUNT(*) FROM documents; -- Returns 1000
-- Another transaction inserts and commits a row here
-- Sees the new row (non-repeatable read)
SELECT COUNT(*) FROM documents; -- Returns 1001
COMMIT;
```
### Repeatable Read
A transaction sees only rows committed before the transaction began. Reads are stable throughout the transaction.
```sql
BEGIN ISOLATION LEVEL REPEATABLE READ;
SELECT COUNT(*) FROM documents; -- Returns 1000
-- Another transaction inserts and commits
SELECT COUNT(*) FROM documents; -- Still 1000 — repeatable read
COMMIT;
```
### Serializable
The strictest level. Transactions execute as if they ran serially one after another. NeuralDB uses Serializable Snapshot Isolation (SSI) — it allows concurrent execution but detects and aborts transactions that would produce a non-serializable outcome.
```sql
BEGIN ISOLATION LEVEL SERIALIZABLE;
-- ... complex read-modify-write patterns ...
COMMIT;
-- May raise: ERROR: could not serialize access — retry the transaction
```
### Read Uncommitted
NeuralDB maps this to Read Committed (it does not implement dirty reads).
## Retry Logic
Serializable transactions can fail with serialization errors. Always retry:
```python
from psycopg2 import errors
MAX_RETRIES = 5
for attempt in range(MAX_RETRIES):
try:
with conn.cursor() as cur:
cur.execute("BEGIN ISOLATION LEVEL SERIALIZABLE")
# ... your operations ...
cur.execute("COMMIT")
break
except errors.SerializationFailure:
conn.rollback()
if attempt == MAX_RETRIES - 1:
raise
time.sleep(0.1 * (2 ** attempt)) # exponential backoff
```
## Savepoints
Savepoints allow partial rollbacks within a transaction:
```sql
BEGIN;
INSERT INTO documents (content, embedding) VALUES ($1, $2);
SAVEPOINT after_insert;
-- Risky operation
UPDATE document_stats SET count = count + 1 WHERE id = $3;
-- Oh no, something went wrong — roll back to the savepoint
ROLLBACK TO SAVEPOINT after_insert;
-- The INSERT is still pending — we can try a different approach
UPDATE document_stats SET count = count + 1 WHERE id = $4;
COMMIT;
```
## Vector Transactions
Vector index updates are transactional in NeuralDB. An HNSW index entry is added atomically with the row:
```sql
BEGIN;
-- Both the row and the vector index entry are inserted atomically
INSERT INTO documents (id, content, embedding) VALUES ($1, $2, $3);
-- If we ROLLBACK, neither the row nor the index entry will exist
ROLLBACK;
-- After rollback, a similarity search will NOT find $1
SELECT id FROM documents ORDER BY embedding <=> $3 LIMIT 1;
-- $1 is not returned
```
## Long-Running Transactions
Avoid long-running transactions — they:
- Hold row-level locks, blocking other writes
- Prevent VACUUM from reclaiming dead rows (bloat)
- Increase the risk of deadlocks
Set a statement timeout to kill runaway queries:
```sql
SET statement_timeout = '30s';
```
Set a transaction timeout:
```sql
SET idle_in_transaction_session_timeout = '5min';
```
## Deadlock Detection
NeuralDB automatically detects deadlocks and aborts one of the transactions:
```
ERROR: deadlock detected
DETAIL: Process 12345 waits for ShareLock on transaction 67890; blocked by process 99999.
Hint: See server log for query details.
```
Minimise deadlock risk by always acquiring locks in the same order across all transactions.
## Advisory Locks
For application-level locking (e.g., ensuring only one worker processes a job):
```sql
-- Acquire a session-level advisory lock (blocks until acquired)
SELECT pg_advisory_lock(42);
-- Try to acquire (returns false if already held)
SELECT pg_try_advisory_lock(42); -- returns boolean
-- Release
SELECT pg_advisory_unlock(42);
-- Transaction-level (auto-released at commit/rollback)
SELECT pg_advisory_xact_lock(42);
```
## Two-Phase Commit (2PC)
For distributed transactions spanning multiple systems:
```sql
-- Phase 1: Prepare
BEGIN;
-- ... operations ...
PREPARE TRANSACTION 'my-distributed-txn-id';
-- Phase 2: Commit or rollback
COMMIT PREPARED 'my-distributed-txn-id';
-- or
ROLLBACK PREPARED 'my-distributed-txn-id';
```
Check pending prepared transactions:
```sql
SELECT gid, prepared, owner, database
FROM pg_prepared_xacts;
```