Skip to content

Transaction API

Primatomic provides distributed transactions with ACID guarantees using optimistic concurrency control.

Transactions allow you to:

  • Read and write multiple keys atomically
  • Ensure consistency across operations
  • Handle concurrent modifications safely
  • Integrate with distributed locks
from primatomic import Client
client = Client("your-auth-token")
# Start a transaction
txn = client.begin_transaction()
try:
# Read values
value1 = txn.get("namespace", "key1")
value2 = txn.get("namespace", "key2")
# Perform logic
result = process(value1, value2)
# Write results
txn.put("namespace", "result", result)
txn.delete("namespace", "temp-key")
# Commit transaction
txn.commit()
except Exception as e:
# Transaction automatically aborted on error
print(f"Transaction failed: {e}")
import { Client } from '@primatomic/sdk';
const client = new Client('your-auth-token');
// Start a transaction
const txn = await client.beginTransaction();
try {
// Read values
const value1 = await txn.get('namespace', 'key1');
const value2 = await txn.get('namespace', 'key2');
// Perform logic
const result = process(value1, value2);
// Write results
await txn.put('namespace', 'result', result);
await txn.delete('namespace', 'temp-key');
// Commit transaction
await txn.commit();
} catch (error) {
// Transaction automatically aborted on error
console.error('Transaction failed:', error);
}
  • get(namespace, key): Read a value
  • put(namespace, key, value): Write a value
  • delete(namespace, key): Delete a key
  • compareAndSwap(namespace, key, expected, new): Conditional update

Operations are batched and executed atomically on commit:

txn = client.begin_transaction()
# All operations are queued
for i in range(100):
txn.put("namespace", f"key{i}", f"value{i}")
# Executed atomically
txn.commit() # All or nothing

Transactions use optimistic concurrency control:

  1. No locks during reads - High concurrency
  2. Conflict detection at commit - Automatic retry on conflicts
  3. Atomic writes - All or nothing guarantee
import time
from primatomic.errors import TransactionConflictError
def transfer_with_retry(client, from_account, to_account, amount):
max_retries = 3
backoff = 0.01 # 10ms
for attempt in range(max_retries):
try:
txn = client.begin_transaction()
# Read balances
from_balance = txn.get("accounts", from_account)
to_balance = txn.get("accounts", to_account)
# Check sufficient funds
if from_balance < amount:
raise ValueError("Insufficient funds")
# Update balances
txn.put("accounts", from_account, from_balance - amount)
txn.put("accounts", to_account, to_balance + amount)
# Commit transaction
txn.commit()
return True
except TransactionConflictError:
if attempt < max_retries - 1:
time.sleep(backoff)
backoff *= 2 # Exponential backoff
continue
raise
return False
txn = client.begin_transaction()
# Acquire lock within transaction
txn.acquire_lock("namespace", "resource", owner="worker-1", ttl=30)
# Perform protected operations
data = txn.get("namespace", "protected-data")
txn.put("namespace", "protected-data", process(data))
# Lock released with transaction
txn.commit()
  • Locks acquired in transactions are released on abort
  • Locks transfer to owner on successful commit
  • Non-transactional operations respect transaction locks
# Good: Short, focused transaction
txn = client.begin_transaction()
balance = txn.get("accounts", account_id)
txn.put("accounts", account_id, balance + amount)
txn.commit()
# Bad: Long-running transaction
txn = client.begin_transaction()
data = txn.get("data", "large-dataset")
result = expensive_computation(data) # Don't do this!
txn.put("data", "result", result)
txn.commit()
# Good: Specific keys
txn = client.begin_transaction()
txn.get("users", f"user:{user_id}")
txn.put("users", f"user:{user_id}", updated_user)
txn.commit()
# Bad: Broad key ranges
txn = client.begin_transaction()
for i in range(1000):
txn.get("users", f"user:{i}") # High conflict probability
def safe_transaction(client, operation):
try:
txn = client.begin_transaction()
result = operation(txn)
txn.commit()
return result
except TransactionConflictError:
# Expected - retry logic
raise
except Exception as e:
# Unexpected - log and handle
logger.error(f"Transaction failed: {e}")
raise
  • Begin: ~100μs
  • Commit (no conflicts): ~1ms
  • Commit (with conflicts): ~2ms + retry time
  1. Batch operations to reduce round trips
  2. Use read-only transactions when possible
  3. Order operations consistently to avoid deadlocks
  4. Set appropriate timeouts (default: 30s)
  • TransactionConflictError: Concurrent modification detected
  • TransactionTimeoutError: Transaction exceeded timeout
  • TransactionNotFoundError: Invalid transaction ID
  • ValidationError: Invalid operation parameters
from primatomic.errors import (
TransactionConflictError,
TransactionTimeoutError
)
try:
execute_transaction()
except TransactionConflictError:
# Retry with backoff
retry_transaction()
except TransactionTimeoutError:
# Transaction took too long
handle_timeout()
except Exception as e:
# Unexpected error
log_error(e)
def increment_counter(client, namespace, key):
txn = client.begin_transaction()
# Read current value
current = txn.get(namespace, key) or 0
# Increment
txn.put(namespace, key, current + 1)
# Commit
txn.commit()
return current + 1
def update_user_profile(client, user_id, updates):
txn = client.begin_transaction()
# Read all related data
profile = txn.get("users", f"profile:{user_id}")
settings = txn.get("users", f"settings:{user_id}")
# Apply updates
profile.update(updates.get("profile", {}))
settings.update(updates.get("settings", {}))
# Write back atomically
txn.put("users", f"profile:{user_id}", profile)
txn.put("users", f"settings:{user_id}", settings)
txn.put("users", f"updated:{user_id}", time.time())
txn.commit()
  • Maximum transaction duration: 30 seconds
  • Maximum operations per transaction: 1000
  • Maximum key size: 1KB
  • Maximum value size: 1MB per operation