by deathanatos | 2025-04-30 01:06:00+00:00
I empathize with the feeling of this being dense and unapproachable; I remember when I was first approaching these posts, and feeling the same.

For this particular one, the graph under "Results" is the most approachable portion, I think. (Don't skip the top two sections, though … and they're so short.) In the graph, each line is a transaction, and read them left-to-right.

Hopefully I get this right, though if I do not, I'm sure someone will correct me. Our database is a set of ordered lists of integers. Something like,

CREATE TABLE test ( id int primary key, -- (but specifically, this next column holds a list of ints, e.g., -- a value might be, '1,8,11'; the list of ints is a comma separated -- string.) list text not null );

The first transaction:

a 89 9

This is shorthand; means "(a)ppend to list #89 the integer 9" (in SQL, crudely this is perhaps something like

UPDATE test SET list = CONCAT(list, ',9') WHERE id = 89;

… though we'd need to handle the case where the list doesn't exist yet, turning it into an `INSERT … ON CONFLICT … DO UPDATE …`, so it would get gnarlier.[2]); the next:

r 90 nil # read list 90; the result is nil r 89 [4 9] # read list 89; the result is [4, 9] r 90 nil # read line 90; the result is (still) nil

I assume you can `SELECT` ;) That should provide sufficient syntax for one to understand the remainder.

The arrows indicate the dependencies; if you click "read-write dependencies"[1], that page explains it.

Our first transaction appends 9 to list 89. Our second transaction reads that same list, and sees that same 9, thus, it must start after the first transaction has committed. The remaining arrows form similar dependencies, and once you take them all into account, they form a cycle; this should feel problematic. It's that they're in a cycle, which snapshot isolation does not permit, so we've observed a contradiction in the system: these cannot be obeying snapshot isolation. (This is what "To understand why this cycle is illegal…" gets at; it is fairly straightforward. T₁ is the first row in the graph, T₂ the second, so forth. But it is only straight-forward once you've understood the graph, I think.)

> This is in such a thick academic style that it is difficult to follow what the problem actually might be and how it would impact someone.

I think a lot of this is because it is written with precision, and that precision requires a lot of academic terminology.

Some of it is just syntax peculiar to Jepsen, which I think comes from Clojure, which I think most of us (myself included) are just not familiar with. Hence why I used SQL and comma-sep'd lists in my commentary above; that is likely more widely read. It's a bit rough when you first approach it, but once you get the notation, the payoff is worth it, I guess.

More generally, I think once you grasp the graph syntax & simple operations used here, it becomes easier to read other posts, since they're mostly graphs of transactions that, taken together, make no logical sense at all. Yet they happened!

> This style of writing serves mostly to remind me that I am not a part of the world that writes like this, which makes me a little sad.

I think Jepsen posts, with a little effort, are approachable. This post is a good starter post; normally I'd say Jepsen posts tend to inject faults into the system, as we're testing if the guarantees of the system hold up under stress. This one has no fault injection, though, so it's a bit simpler.

Beware though, that if you learn to read these, that you'll never trust a database again.

[1]: https://jepsen.io/consistency/dependencies

[2]: I think this is it? https://github.com/jepsen-io/postgres/blob/225203dd64ad5e5e4... — but this is pushing the limits of my own understanding.