Nemo Logo
Nemo
Browse documentation
Home / Docs / transactions
Core Concepts

Transaction methods, context managers, autocommit modes, MVCC, and concurrency patterns.

Nemo Transactions Reference

This guide covers all transaction types, methods, callbacks, and patterns available in Nemo for both synchronous and asynchronous applications.


Transaction Methods Reference

Target Object Method / Function Signature Description
Connection conn.transaction() () -> Transaction Returns an ACID context manager (with or async with). Commits upon clean exit; rolls back on unhandled exceptions.
Connection conn.begin_transaction() () -> Transaction Explicitly starts a new transaction, returning the active Transaction handle.
Connection conn.commit() () -> None Commits the currently active transaction on this connection.
Connection conn.rollback() () -> None Rolls back the active transaction, reverting all modifications made during the transaction.
Connection conn.set_autocommit() (enabled: bool) -> None Toggles autocommit mode (True by default). When disabled, statements batch into a transaction until commit().
Transaction txn.begin() () -> Transaction Starts the transaction (called automatically by context manager or begin_transaction()).
Transaction txn.commit() () -> None Commits this transaction instance.
Transaction txn.rollback() () -> None Rolls back this transaction instance.

All 7 Transaction Patterns and Use Cases

Pattern 1: Automatic Context Manager Transactions

The recommended Pythonic approach. The context manager automatically commits if the block finishes cleanly, or rolls back if an exception is raised.

Synchronous Context Manager

import nemo

with nemo.connect("accounts") as db:
    with db.connect_sync() as conn:
        with conn.transaction():
            conn.execute("UPDATE accounts SET balance = balance - 100 WHERE id = 1;")
            conn.execute("UPDATE accounts SET balance = balance + 100 WHERE id = 2;")
            # Successfully committed on exit

Asynchronous Context Manager

import asyncio
import nemo

async def transfer():
    with nemo.connect("accounts") as db:
        async with db.connect_async() as conn:
            async with conn.transaction():
                await conn.execute("UPDATE accounts SET balance = balance - 100 WHERE id = 1;")
                await conn.execute("UPDATE accounts SET balance = balance + 100 WHERE id = 2;")

asyncio.run(transfer())

Pattern 2: Explicit Manual Transactions

For complex workflows where transaction boundaries span multiple helper functions or conditional steps:

import nemo

with nemo.connect("store") as db:
    with db.connect_sync() as conn:
        txn = conn.begin_transaction()
        try:
            conn.execute("INSERT INTO orders (id, customer) VALUES (?, ?);", (501, "Alice"))
            conn.execute("UPDATE inventory SET stock = stock - 1 WHERE item_id = 99;")

            # Commit explicitly
            txn.commit()
        except Exception as e:
            # Revert all changes
            txn.rollback()
            raise

Pattern 3: Batch Ingestion with Autocommit Disabled

When executing thousands of single write statements, disabling autocommit groups them into a single transaction commit, drastically increasing write throughput:

import nemo

with nemo.connect("metrics") as db:
    with db.connect_sync() as conn:
        # Disable per-statement autocommit
        conn.set_autocommit(False)

        for i in range(10000):
            conn.execute("INSERT INTO sensor_log VALUES (?, ?);", (i, 20.0 + (i % 5)))

        # Commit all 10,000 writes at once
        conn.commit()

        # Restore autocommit
        conn.set_autocommit(True)

Pattern 4: Read-Only Snapshot Isolation

Nemo provides snapshot isolation: readers execute against a consistent snapshot of data without blocking concurrent writers, and writers do not block readers.

import nemo

with nemo.connect("analytics") as db:
    with db.connect_sync() as conn:
        # Long-running analytical query sees consistent point-in-time data
        with conn.transaction():
            total = conn.execute("SELECT SUM(amount) FROM ledger;").fetchone()[0]
            avg = conn.execute("SELECT AVG(amount) FROM ledger;").fetchone()[0]
            print(f"Total: {total}, Average: {avg}")

Pattern 5: Multi-Connection Concurrency

Multiple connections can execute transactions simultaneously across threads or coroutines without database-wide lock collisions:

import threading
import nemo

def worker(worker_id: int):
    with nemo.connect("cluster") as db:
        with db.connect_sync() as conn:
            with conn.transaction():
                conn.execute("INSERT INTO worker_heartbeats VALUES (?, ?);", (worker_id, "alive"))

threads = [threading.Thread(target=worker, args=(i,)) for i in range(8)]
for t in threads: t.start()
for t in threads: t.join()

Pattern 6: Connection Pool Transactions

When working with connection pools, transactions are contained within the leased connection:

import nemo

with nemo.connect("app") as db:
    pool = db.create_pool_sync(max_connections=4)

    with pool.acquire() as conn:
        with conn.transaction():
            conn.execute("INSERT INTO sessions VALUES (?);", ("session_token_xyz",))

    pool.close()

Pattern 7: Transient Retry Logic

For mission-critical production services, wrap transactions in an exponential backoff retry handler:

import time
import nemo
from nemo import ConcurrencyError, OperationalError

def execute_with_retry(conn, fn, max_attempts=5):
    for attempt in range(max_attempts):
        try:
            with conn.transaction():
                return fn(conn)
        except (ConcurrencyError, OperationalError):
            if attempt == max_attempts - 1:
                raise
            time.sleep(0.01 * (2 ** attempt))

  • example_9.py: ACID Transactions, Rollbacks, and Multi-Version Concurrency (MVCC).
  • example_10.py: Multi-Threaded Connection Pooling and High Concurrency.
  • example_16.py: Exact Financial Decimals and Multi-Account Banking Ledgers.
  • example_39.py: ACID Transaction Guarantees and Rollback Semantics.