{"post":{"slug":"acid-transactions-explained-2026","title":"ACID transactions explained in 2026: what the guarantees really mean","excerpt":"ACID is four guarantees that make database transactions reliable, but each property has nuance that matters in distributed systems. Here is the real story.","category":"code","date":"April 8, 2026","author":"ByteLedger Team","content":"<span class=\"dropcap\">A</span>CID is a set of four properties that define what a reliable database transaction must guarantee: Atomicity, Consistency, Isolation, and Durability. The acronym is decades old, but its implications are still misunderstood — especially as distributed databases and ORMs abstract away the details. Getting ACID wrong is how you get double-charges, lost writes, and phantom inventory counts in production.\n\n## What changed in 2026\n\n- **Serializable isolation became practical.** PostgreSQL's Serializable Snapshot Isolation (SSI) has been production-grade for years, and CockroachDB uses it as the default. More teams are now running serializable where they previously accepted weaker levels.\n- **Distributed ACID went mainstream.** CockroachDB, Spanner, YugabyteDB, and TiDB all offer full ACID across nodes with multi-region support — no longer a research achievement.\n- **ORMs expose isolation mismatches.** Prisma and SQLAlchemy 2.x now make isolation level configuration explicit in their API, reducing accidental weak-isolation deployments.\n- **LLM-assisted SQL review flags missing transactions.** Code review tools using Claude/GPT-class models flag multi-step database operations not wrapped in a transaction.\n\n## The four properties\n\n### Atomicity\n\nAll operations in a transaction succeed together or all fail together. There is no partial success.\n\n```sql\nBEGIN;\n  UPDATE accounts SET balance = balance - 100 WHERE id = 1;  -- debit\n  UPDATE accounts SET balance = balance + 100 WHERE id = 2;  -- credit\nCOMMIT;\n-- If the second UPDATE fails, the first is rolled back automatically\n```\n\nWithout atomicity, a crash between the debit and credit would leave money missing.\n\n### Consistency\n\nA transaction brings the database from one valid state to another. Constraints (NOT NULL, FOREIGN KEY, CHECK) are enforced at commit time.\n\nConsistency is partially a database responsibility (enforcing constraints) and partially an application responsibility (writing correct business logic).\n\n### Isolation\n\nConcurrent transactions should not interfere with each other. The degree of interference permitted is controlled by the **isolation level**.\n\n### Durability\n\nOnce a transaction commits, the data survives crashes, power loss, and restarts. PostgreSQL achieves this via the write-ahead log (WAL) — the change is written to disk before the commit acknowledgment.\n\n## Isolation levels compared\n\n| Level | Dirty Read | Non-repeatable Read | Phantom Read | Lost Update |\n| --- | --- | --- | --- | --- |\n| Read Uncommitted | Possible | Possible | Possible | Possible |\n| Read Committed (PG default) | Prevented | Possible | Possible | Possible |\n| Repeatable Read | Prevented | Prevented | Possible in SQL; prevented in PG | Prevented in PG |\n| Serializable | Prevented | Prevented | Prevented | Prevented |\n\nPostgreSQL's implementation of Repeatable Read and Serializable is stronger than the SQL standard requires — Postgres uses snapshot isolation and SSI.\n\n```sql\n-- Set isolation level for a transaction\nBEGIN ISOLATION LEVEL SERIALIZABLE;\n  SELECT balance FROM accounts WHERE id = 1;  -- snapshot is taken here\n  -- another transaction cannot change this row and commit before we do\nCOMMIT;\n```\n\n## How to pick the right isolation level\n\n1. **Read Committed** — safe default for most OLTP. Simple reads and writes with no multi-step logic.\n2. **Repeatable Read** — use when a transaction reads the same row multiple times and needs consistency (e.g., computing a running total).\n3. **Serializable** — use when correctness requires that transactions appear to run one-at-a-time: inventory reservation, seat booking, financial double-entry.\n\n```python\n# SQLAlchemy 2.x — serializable transaction\nfrom sqlalchemy import text\n\nwith engine.begin() as conn:\n    conn.execute(text(\"SET TRANSACTION ISOLATION LEVEL SERIALIZABLE\"))\n    balance = conn.scalar(text(\"SELECT balance FROM accounts WHERE id = :id\"), {\"id\": 1})\n    if balance >= 100:\n        conn.execute(text(\"UPDATE accounts SET balance = balance - 100 WHERE id = :id\"), {\"id\": 1})\n```\n\n## Common mistakes\n\n**Using Read Committed for financial operations.** Two concurrent transactions both reading a balance of $200, both deciding to spend $150, and both succeeding — because neither sees the other's uncommitted write. This is a lost update.\n\n**Long-running transactions.** A transaction open for minutes holds row locks (or snapshot overhead) that block other writers. Keep transactions short and commit early.\n\n**Not handling serialization failures.** Serializable transactions can fail with `ERROR: could not serialize access due to concurrent update`. Your application must catch `40001` error codes and retry.\n\n```python\nimport psycopg2\nfrom psycopg2 import OperationalError\n\nfor attempt in range(3):\n    try:\n        with conn:  # auto-commit/rollback\n            # ... transaction logic\n            break\n    except OperationalError as e:\n        if e.pgcode == \"40001\":  # serialization failure\n            continue\n        raise\n```\n\n**Assuming ORMs wrap individual queries in transactions.** By default, Prisma auto-commits each operation. Multi-step operations must be explicitly wrapped in `prisma.$transaction([...])`.\n\n## What to skip\n\n- **Distributed sagas for simple operations** — a single-node ACID transaction is simpler and safer than a saga pattern when all your data lives in one database.\n- **Compensating transactions as a first resort** — prefer serializable ACID; add sagas only when the transaction must span external systems.\n- **Disabling fsync for \"performance\"** — this breaks Durability entirely and risks data corruption on crash.\n\n## FAQ\n\n**Does PostgreSQL support full ACID?**\nYes. PostgreSQL provides full ACID with Serializable Snapshot Isolation as the strongest isolation level, and WAL-based durability.\n\n**Is MongoDB ACID?**\nMongoDB 4.0+ supports multi-document ACID transactions within a replica set. Cross-shard transactions are supported but slower. Single-document operations have always been atomic.\n\n**What is the difference between a transaction and a savepoint?**\nA savepoint marks a point within a transaction you can roll back to without aborting the entire transaction. Useful for nested operations where partial rollback is acceptable.\n\n**How do distributed databases implement ACID across nodes?**\nUsing protocols like Two-Phase Commit (2PC) or variants like Percolator (used by TiDB/CockroachDB). These add network round-trips to the commit path, increasing latency compared to single-node commits.\n\n## Where to go next\n\n- [The CAP theorem explained in 2026](/blog/the-cap-theorem-explained-2026)\n- [Sharding explained in 2026](/blog/sharding-explained-2026)\n- [ORMs explained in 2026](/blog/orms-explained-2026)","hue":322,"metaTitle":"ACID Transactions Explained 2026","metaDescription":"Deep-dive into ACID transactions in 2026. What each property means, isolation levels compared, and how distributed databases handle ACID guarantees.","keywords":["acid transactions explained 2026","acid properties database","database isolation levels 2026","serializable isolation","atomicity consistency isolation durability","distributed transactions 2026","postgres transaction isolation","read committed vs serializable","lost update problem","transaction anomalies"],"tldr":["**ACID stands for Atomicity, Consistency, Isolation, Durability** — four properties that guarantee transactions behave correctly even during failures.","**Isolation is the most complex** — the default level in most databases is Read Committed, not Serializable, which still allows anomalies.","**Distributed ACID is hard and expensive** — systems like CockroachDB and Spanner provide it, but at higher latency than single-node Postgres.","**Most bugs come from wrong isolation level** — missed phantom reads, lost updates, and non-repeatable reads are all isolation failures, not app bugs.","**Skip:** wrapping every database call in a transaction \"just to be safe\" — long-running transactions hold locks and cause contention."],"readTime":"4 min read","wordCount":961},"related":[{"slug":"database-transactions-explained-2026","title":"Database transactions explained in 2026: ACID, isolation, and real examples","excerpt":"Transactions are the mechanism that keeps your database consistent when multiple operations run concurrently. This is the 2026 guide to ACID properties, isolation levels, and the patterns that prevent data corruption.","category":"code","date":"April 13, 2026","author":"ByteLedger Team","readTime":"6 min read","hue":318},{"slug":"transactions-explained-2026","title":"Database transactions explained in 2026: ACID, isolation, and beyond","excerpt":"Transactions are the safety net of every database write. This 2026 guide covers ACID, isolation levels, deadlocks, and the patterns that prevent data corruption.","category":"code","date":"April 21, 2026","author":"ByteLedger Team","readTime":"5 min read","hue":310},{"slug":"database-isolation-levels-2026","title":"Database Isolation Levels: The Bugs You Only Get Under Load","excerpt":"Your code is correct when one user runs it. Two users at once produce a result neither would accept. Isolation levels decide which of those anomalies you get.","category":"code","date":"August 12, 2026","author":"ByteLedger Team","readTime":"5 min read","hue":304}],"sameCategory":[{"slug":"bazel-build-explained-2026","title":"Bazel Explained: What Hermetic Builds Actually Cost","excerpt":"Bazel delivers reproducible, precisely cached builds and demands that you describe everything. Here is what you get, what it costs, and who should say no.","category":"code","date":"August 19, 2026","author":"ByteLedger Team","readTime":"3 min read","hue":145},{"slug":"graphql-federation-2026","title":"GraphQL Federation: One Graph, Many Teams","excerpt":"Federation composes independently-owned subgraphs into a single API. Here is what it solves, the operational cost it adds, and when a simpler gateway is enough.","category":"code","date":"August 19, 2026","author":"ByteLedger Team","readTime":"3 min read","hue":300},{"slug":"hermetic-builds-explained-2026","title":"Hermetic Builds Explained: Same Inputs, Same Output","excerpt":"A hermetic build depends only on declared inputs. Here is what that property gives you, the common leaks that break it, and how to move toward it incrementally.","category":"code","date":"August 19, 2026","author":"ByteLedger Team","readTime":"3 min read","hue":130},{"slug":"monorepo-tooling-compared-2026","title":"Monorepo Tooling Compared: What You Actually Need","excerpt":"Monorepo tools solve task orchestration and caching, not repository layout. Here is what each tier provides and how to tell when you have outgrown the simple option.","category":"code","date":"August 19, 2026","author":"ByteLedger Team","readTime":"3 min read","hue":150}],"newer":{"slug":"64gb-vs-128gb-vs-256gb-phone-2026","title":"How Much Phone Storage Do I Need in 2026: 64 vs 128 vs 256","excerpt":"A plain-language guide to 64GB, 128GB, and 256GB phones in 2026, with a simple rule for picking the right storage tier without paying for space you never use.","category":"tech","date":"April 8, 2026","author":"ByteLedger Team","readTime":"4 min read","hue":216},"older":{"slug":"active-recall-explained-2026","title":"Active recall explained in 2026: the most effective study technique you are not using","excerpt":"Active recall — retrieving information from memory instead of re-reading it — is the highest-evidence study technique available. Here is the 2026 guide to what it is and how to use it.","category":"guides","date":"April 8, 2026","author":"ByteLedger Team","readTime":"5 min read","hue":150}}