The timing of this example is tricky because the two update statements execute concurrently (which is only possible under read committed isolation; under serializable isolation it's much more like what you're describing).
Here's a full timeline in PG (U1 for first update, U2 for second update):
Comments
The timing of this example is tricky because the two update statements execute concurrently (which is only possible under read committed isolation; under serializable isolation it's much more like what you're describing).
Here's a full timeline in PG (U1 for first update, U2 for second update):
0. U1 begins executing, establishes read snapshot, starts pg_sleep(5).
1. U2 runs to completion.
2. U1 wakes up after 5 sec, scans `player` using snapshot from step 0.
3. U1 filters `team = Gophers`, gets 4, 5, 6.
4. U1 locks 4, 5, 6.
5. U1 performs EvalQualPlan: re-scans latest version of those locked rows, which sees U2's write to 4 but not to 3.
6. U1 performs EvalQualPlan: re-filters those locked rows using latest version, gets 5, 6.
7. U1 writes new versions.
CRDB is easier to reason about: after U1 wakes up from the sleep, it sees that it conflicts with U2 and simply retries the entire statement.