Transaction API
Primatomic provides distributed transactions with ACID guarantees using optimistic concurrency control.
Overview
Section titled “Overview”Transactions allow you to:
- Read and write multiple keys atomically
- Ensure consistency across operations
- Handle concurrent modifications safely
- Integrate with distributed locks
Basic Usage
Section titled “Basic Usage”Python SDK
Section titled “Python SDK”from primatomic import Client
client = Client("your-auth-token")
# Start a transactiontxn = 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}")
JavaScript SDK
Section titled “JavaScript SDK”import { Client } from '@primatomic/sdk';
const client = new Client('your-auth-token');
// Start a transactionconst 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);}
Transaction Operations
Section titled “Transaction Operations”Supported Operations
Section titled “Supported Operations”- 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
Operation Batching
Section titled “Operation Batching”Operations are batched and executed atomically on commit:
txn = client.begin_transaction()
# All operations are queuedfor i in range(100): txn.put("namespace", f"key{i}", f"value{i}")
# Executed atomicallytxn.commit() # All or nothing
Concurrency Control
Section titled “Concurrency Control”Optimistic Locking
Section titled “Optimistic Locking”Transactions use optimistic concurrency control:
- No locks during reads - High concurrency
- Conflict detection at commit - Automatic retry on conflicts
- Atomic writes - All or nothing guarantee
Handling Conflicts
Section titled “Handling Conflicts”import timefrom 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
Lock Integration
Section titled “Lock Integration”Acquiring Locks in Transactions
Section titled “Acquiring Locks in Transactions”txn = client.begin_transaction()
# Acquire lock within transactiontxn.acquire_lock("namespace", "resource", owner="worker-1", ttl=30)
# Perform protected operationsdata = txn.get("namespace", "protected-data")txn.put("namespace", "protected-data", process(data))
# Lock released with transactiontxn.commit()
Lock Behavior
Section titled “Lock Behavior”- Locks acquired in transactions are released on abort
- Locks transfer to owner on successful commit
- Non-transactional operations respect transaction locks
Best Practices
Section titled “Best Practices”1. Keep Transactions Short
Section titled “1. Keep Transactions Short”# Good: Short, focused transactiontxn = client.begin_transaction()balance = txn.get("accounts", account_id)txn.put("accounts", account_id, balance + amount)txn.commit()
# Bad: Long-running transactiontxn = client.begin_transaction()data = txn.get("data", "large-dataset")result = expensive_computation(data) # Don't do this!txn.put("data", "result", result)txn.commit()
2. Minimize Conflict Scope
Section titled “2. Minimize Conflict Scope”# Good: Specific keystxn = client.begin_transaction()txn.get("users", f"user:{user_id}")txn.put("users", f"user:{user_id}", updated_user)txn.commit()
# Bad: Broad key rangestxn = client.begin_transaction()for i in range(1000): txn.get("users", f"user:{i}") # High conflict probability
3. Handle Failures Gracefully
Section titled “3. Handle Failures Gracefully”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
Performance Considerations
Section titled “Performance Considerations”Transaction Overhead
Section titled “Transaction Overhead”- Begin: ~100μs
- Commit (no conflicts): ~1ms
- Commit (with conflicts): ~2ms + retry time
Optimization Tips
Section titled “Optimization Tips”- Batch operations to reduce round trips
- Use read-only transactions when possible
- Order operations consistently to avoid deadlocks
- Set appropriate timeouts (default: 30s)
Error Handling
Section titled “Error Handling”Common Errors
Section titled “Common Errors”- TransactionConflictError: Concurrent modification detected
- TransactionTimeoutError: Transaction exceeded timeout
- TransactionNotFoundError: Invalid transaction ID
- ValidationError: Invalid operation parameters
Error Recovery
Section titled “Error Recovery”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)
Examples
Section titled “Examples”Counter with Transactions
Section titled “Counter with Transactions”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
Multi-Key Update
Section titled “Multi-Key Update”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()
Limitations
Section titled “Limitations”- Maximum transaction duration: 30 seconds
- Maximum operations per transaction: 1000
- Maximum key size: 1KB
- Maximum value size: 1MB per operation