SkillAgentSearch skills...

Hermitage

What are the differences between the transaction isolation levels in databases? This is a suite of test cases which differentiate isolation levels.

Install / Use

/learn @ept/Hermitage
About this skill

Quality Score

0/100

Supported Platforms

Universal

README

Hermitage: Testing transaction isolation levels

“Aristotle maintained that women have fewer teeth than men; although he was twice married, it never occurred to him to verify this statement by examining his wives' mouths.”

― Bertrand Russell, The Impact of Science on Society (1952)

Hermitage is an attempt to nail down precisely what different database systems actually mean with their isolation levels. It's a suite of tests that simulates various concurrency issues — some common, some more obscure — and documents how different databases handle those situations.

This project was started by Martin Kleppmann as background research for his book, Designing Data-Intensive Applications. In this repository you'll find a lot of nitty-gritty detail. For a gentle, friendly introduction to the topic, please read the book. There is also a blog post with some background story.

Summary of test results

This repo contains tests for PostgreSQL, MySQL with InnoDB, Oracle, SQL Server, FoundationDB, CockroachDB, YugabyteDB, and Memgraph. If you would like to port the test suite to another database, please don't add it to this repository; instead you can make your own repository and add it to the following list. The test suite has also been ported to:

The cryptic abbreviations (G1c, PMP etc) are different kinds of concurrency anomalies — issues which can occur when multiple clients are executing transactions at the same time, and which can cause application bugs. The precise definitions of these anomalies are given in the literature (see below for details).

| DBMS | So-called isolation level | Actual isolation level | G0 | G1a | G1b | G1c | OTV | PMP | P4 | G-single | G2-item | G2 | |:--------------|:-----------------------------|:-----------------------|:--:|:---:|:---:|:---:|:---:|:---:|:--:|:--------:|:-------:|:----:| | PostgreSQL | "read committed" ★ | monotonic atomic view | ✓ | ✓ | ✓ | ✓ | ✓ | — | — | — | — | — | | | "repeatable read" | snapshot isolation | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | — | — | | | "serializable" | serializable | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | | | | | | | | | | | | | | | | MySQL/InnoDB | "read uncommitted" | read uncommitted | ✓ | — | — | — | — | — | — | — | — | — | | | "read committed" | monotonic atomic view | ✓ | ✓ | ✓ | ✓ | ✓ | — | — | — | — | — | | | "repeatable read" ★ | monotonic atomic view | ✓ | ✓ | ✓ | ✓ | ✓ | R/O | — | R/O | — | — | | | "serializable" | serializable | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | | | | | | | | | | | | | | | | Oracle DB | "read committed" ★ | monotonic atomic view | ✓ | ✓ | ✓ | ✓ | ✓ | — | — | — | — | — | | | "serializable" | snapshot isolation | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | — | some | | | | | | | | | | | | | | | | MS SQL Server | "read uncommitted" | read uncommitted | ✓ | — | — | — | — | — | — | — | — | — | | | "read committed" (locking) ★ | monotonic atomic view | ✓ | ✓ | ✓ | ✓ | ✓ | — | — | — | — | — | | | "read committed" (snapshot) | monotonic atomic view | ✓ | ✓ | ✓ | ✓ | ✓ | — | — | — | — | — | | | "repeatable read" | repeatable read | ✓ | ✓ | ✓ | ✓ | ✓ | — | ✓ | some | ✓ | — | | | "snapshot" | snapshot isolation | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | — | — | | | "serializable" | serializable | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | | | | | | | | | | | | | | | | FDB SQL Layer | "serializable" ★ | serializable | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | | | | | | | | | | | | | | | | CockroachDB | "read committed" | monotonic atomic view | ✓ | ✓ | ✓ | ✓ | ✓ | — | — | — | — | — | | | "repeatable read" | snapshot isolation | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | — | — | | | "serializable" ★ | serializable | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | | | | | | | | | | | | | | | | YugabyteDB | "read committed" ★ | monotonic atomic view | ✓ | ✓ | ✓ | ✓ | ✓ | — | — | — | — | — | | | "repeatable read" | snapshot isolation | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | — | — | | | "serializable" | serializable | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | | | | | | | | | | | | | | | | Memgraph | "snapshot isolation" ★ | snapshot isolation | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | — | — | | | "read committed" | read committed | ✓ | ✓ | ✓ | ✓ | ✓ | — | ✓ | — | — | — | | | "read uncommitted" | read uncommitted | ✓ | — | — | — | ✓ | — | ✓ | — | — | — |

Legend:

  • ★ = default configuration
  • ✓ = isolation level prevents this anomaly from occurring
  • — = isolation level does not prevent this anomaly, so it can occur
  • R/O = isolation level prevents this anomaly in a read-only context, but when you perform writes, the anomaly can occur (see test cases for details)
  • some = isolation level prevents this anomaly in some cases, but not in others (see test cases for details)
  • anomalies
    • G0: Write Cycles (dirty writes)
    • G1a: Aborted Reads (dirty reads, cascaded aborts)
    • G1b: Intermediate Reads (dirty reads)
    • G1c: Circular Information Flow (dirty reads)
    • OTV: Observed Transaction Vanishes
    • PMP: Predicate-Many-Preceders
    • P4: Lost Update
    • G-single: Single Anti-dependency Cycles (read skew)
    • G2-item: Item Anti-dependency Cycles (write skew on disjoint read)
    • G2: Anti-Dependency Cycles (write skew on predicate read)

Background

Isolation is the I in ACID, and it describes how a database protects an application from concurrency problems (race conditions). If you read a traditional database theory textbook, it will tell you that isolation is supposed to mean serializability, i.e. you can pretend that transactions are executed one after another, and concurrency problems do not happen. However, if you look at the implementations of isolation in practice, you see that serializability is rarely used, and some popular databases (such as Oracle) don't even implement it.

So what does isolation actually mean? Well, in practice, many database systems allow you to choose your isolation level, as a trade-off between performance and safety (weaker isolation is faster but exposes you to more potential race conditions). Unfortunately, those weaker isolation levels are quite poorly understood. Even though our industry has been working with this stuff for 20 years or more, there are not many people who can explain off-the-cuff the difference between, say, read committed and repeatable read. This is a problem, because if you don't know what guarantees you can expect from your database, you cannot know whether your code has concurrency bugs and race conditions.

The SQL standard tried to define four isolation levels (read uncommitted, read committed, repeatable read and serializable), but its definition is flawed. Several researchers have tried to nail down more precise definitions of weak (i.e. non-serializable) isolation levels. In particular:

  • Peter Bailis, Alan Fekete, Ali Ghodsi, Joseph M. Hellerstein, and Ion Stoica: "Scalable Atomic Visibility with RAMP Transactions" at ACM Transactions on Database Systems, Vol. 41, No. 3, Article 15, Publication date: July 2016.
  • Peter Bailis, Aaron Davidson, Alan Fekete, Ali Ghodsi, Joseph M Hellerstein and Ion Stoica: “[Highly Available Transactions: Virtue
View on GitHub
GitHub Stars2.7k
CategoryData
Updated1d ago
Forks192

Security Score

80/100

Audited on Mar 26, 2026

No findings