• Stars
    star
    2,400
  • Rank 19,162 (Top 0.4 %)
  • Language
  • Created about 10 years ago
  • Updated 5 months ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

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

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

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 "serializable" β˜… serializable βœ“ βœ“ βœ“ βœ“ βœ“ βœ“ βœ“ βœ“ βœ“ βœ“
YugabyteDB "read committed" β˜… monotonic atomic view βœ“ βœ“ βœ“ βœ“ βœ“ β€” β€” β€” β€” β€”
"repeatable read" snapshot isolation βœ“ βœ“ βœ“ βœ“ βœ“ βœ“ βœ“ βœ“ β€” β€”
"serializable" serializable βœ“ βœ“ βœ“ βœ“ βœ“ βœ“ βœ“ βœ“ βœ“ βœ“

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:

This project is based on the formal definition of weak isolation introduced by Adya, as extended by Bailis et al. They mathematically define certain anomalies (or phenomena) which can occur in an unrestricted concurrency model, and define isolation levels as prohibiting or preventing certain anomalies from occurring.

The formal definitions are not easy to understand, but at least they are precise. By comparison, the database vendors' documentation of isolation levels is also hard to understand, but on top of that it's also frustratingly vague:

Goals of this project

This repository contains a series of tests which probe for a range of concurrency anomalies. They are based on the definitions in the literature above. This is useful for several reasons:

  • It allows us to compare isolation levels easily: the more check marks in the table above, the stronger its guarantees.
  • For anyone who needs help choosing the right isolation level for their application, the test suites provide concrete examples of the differences between isolation levels.
  • Various new databases have claimed to support ACID transactions, but their marketing materials often don't make clear what guarantees are actually provided. This test suite can allow a fair comparison of different databases, at least on the isolation aspect of ACID.
  • Hopefully, this effort can be part of a journey towards a better understanding of weak isolation. It looks like weak isolation isn't going away, so we need to learn to be more precise about what it means, and build tools to help us deal with it, otherwise we'll just continue creating buggy applications.

Caveats

  • This is a test suite. It obviously cannot prove that a database always behaves in a certain way, it can only probe certain examples and observe what happens.
  • Tests are currently executed by hand. This means that any concurrency issues that depend on fast timings will not be found. However, it's remarkable that even at the slow speed of a human, you can still easily demonstrate concurrency issues. It's not the speed that matters, it's the ordering of events.
  • The summary table above only describes safety properties, i.e. whether the database allows a certain race condition to occur. It doesn't describe how the anomaly is prevented (usually by blocking or aborting some of the transactions). In practice, how much transactions need to be blocked or aborted makes a big performance difference. For example, although PostgreSQL's serializable and MySQL's serializable have the same isolation guarantees, they have totally different implementations and very different performance characteristics.
  • We're not trying to compare performance here. Performance depends on the workload, so please do your own benchmarking.
  • More check marks doesn't necessarily mean better. This is not Top Trumps, it's a game of trade-offs. All we're trying to do here is to understand what we gain and what we lose at different isolation levels.

Using this project

The tests are currently executed by hand: you simply open two or three connections to the same database in different terminal windows, and run the queries in the order they appear in the test script. A comment indicates which transaction executes a particular query, and what the expected result is.

This could probably be automated, but it's actually quite interesting to go through the exercise of stepping through transactions one line at a time, and watching how the database responds. If you want to build an intuition for database concurrency, running through the test suite is a good exercise. For some databases, setup instructions are included at the bottom of the file.

At the moment, this project only compares five databases, but many more databases offer transactions. It would be especially interesting to add the new generation of distributed transactional databases ("NewSQL" if you like marketing-speak) to this comparison: Aerospike, NuoDB, MemSQL, etc. FoundationDB is currently included.

If you would like to port the test suite to another database, or add new tests, your contribution would be most welcome!

Thank you to contributors:

License

Copyright Martin Kleppmann, 2014. This work is licensed under a Creative Commons Attribution 4.0 International License.

Creative Commons License

More Repositories

1

ddia-references

Literature references for β€œDesigning Data-Intensive Applications”
5,786
star
2

crdt-website

Source of the crdt.tech website
CSS
407
star
3

invoicing

Ruby invoicing framework gem
Ruby
202
star
4

avrodoc

Documentation tool for Avro schemas
JavaScript
148
star
5

uploadr.py

Command-line Python script to upload photos to Flickr
Python
108
star
6

newsfeed

Demo: implementing a Twitter-like news feed using Apache Samza
Java
83
star
7

dist-sys

Distributed systems lecture notes
TeX
56
star
8

warc-hadoop

WARC (Web Archive) Input and Output Formats for Hadoop
Java
35
star
9

neo4j-scala-template

Template for a new Scala project using the Neo4j graph database and Jersey JSON REST API, including build config and example tests
Scala
32
star
10

neo4j-resources

Scala implementation of RESTful JSON HTTP resources on top of the Neo4j graph database and Jersey
Scala
25
star
11

fuego-diff

Java library for computing structural differences between XML document trees
Java
23
star
12

byzantine-eventual

Byzantine Eventual Consistency
TeX
21
star
13

ddia2-feedback

Reader feedback on the early release of Designing Data-Intensive Applications, second edition
20
star
14

jxirr

Excel compatible XIRR (Internal Rate of Return) implementation in Java
Java
20
star
15

cap-critique

Source of paper β€œA critique of the CAP theorem”
TeX
16
star
16

plotkit

Git import of Alastair Tse's chart and graph plotting library for Javascript
JavaScript
15
star
17

saas-template

Ruby
14
star
18

spake2-signal

JavaScript
14
star
19

blog

Source of my personal blog, using Markdown, Jekyll and Heroku
HTML
13
star
20

pushpin-papoc

PushPin: Towards Production-Quality Peer-to-Peer Collaboration
TeX
12
star
21

maniation

Constrained rigid body simulation for 3D character animation
Java
10
star
22

slate-automerge

JavaScript
9
star
23

debs-keynote

Keynote at the 15th ACM International Conference on Distributed and Event-Based Systems (DEBS)
TeX
8
star
24

compsci

Computer Science exercises and teaching materials
OCaml
7
star
25

oaccounts

Open standard for companies' financial data - storing and transmitting
Scala
7
star
26

bespin-on-rails

Bespin on Rails is a simple Ruby on Rails plugin that allows you to embed the Mozilla Bespin code editor component in your Rails views using simple helper tags.
JavaScript
7
star
27

selenium-rc

Python
6
star
28

selenium-client

Go Test It fork of Ruby client library for Selenium RC
Ruby
6
star
29

selenium-core

JavaScript
5
star
30

cracktastic

Very silly Rails app for demonstrating the Ruby invoicing gem. It's about selling christmas cracker jokes.
Ruby
5
star
31

windmill

Git import of the Windmill project
JavaScript
5
star
32

hbase-es

HBase-to-Elasticsearch proof of concept
Java
4
star
33

cotweet-export

Download/dump the data in your CoTweet standard account before it gets shut down
Ruby
4
star
34

chinese

Resources for Chinese learners
3
star
35

libv8-debian

For building v8 (Google's JavaScript engine) as a Debian package
C++
3
star
36

ept.github.com

Website at ept.github.com
2
star
37

flowquery

Ruby
2
star
38

drawing-game

Ruby
2
star
39

invoicing_generator

Scaffold generation tool for the invoicing Ruby gem
Ruby
1
star
40

curve25519

Deriving an implementation of Curve25519 from first principles
TeX
1
star
41

asciidraw

Objective-C
1
star
42

flow

Ruby
1
star
43

discrete-maths

Isabelle formalisation of discrete maths problems
Isabelle
1
star