Technically not an answer to the question, but I want to demo this anomaly descried in the question using Postgres with the isolation level Read Committed:
db=# create table customer (name varchar(10), balance int);
CREATE TABLE
db=# insert into customer values ('foo', 0);
INSERT 0 1
db=# select * from customer;
name | balance
------+---------
foo | 0
(1 row)
Now execute the following queries using 2 transactions (2 psql clients):
tx1: begin isolation level read committed;
tx2: begin isolation level read committed;
tx1: update customer set balance = (select balance from customer where name='foo')+1 where name='foo';
tx2: update customer set balance = (select balance from customer where name='foo')+1 where name='foo';
# here tx2 will BLOCK as we are updating the same row from 2 transactions
# Postgres locks it
tx1: commit;
# tx2 no longer blocks because tx1 commits and the lock has been released
tx2: commit;
tx1 & tx2: select * from customer;
name | balance
------+---------
foo | 1
(1 row)
NOTE
If you change the "update" query to update customer set balance = balance + 1 where name='foo';
, then you won't reproduce it as when tx2 reads the balance, it reads the committed data, i.e., 1, so ultimately, balance will be 2 (the correct result).
If you try the above example with other isolation levels, you cannot reproduce it either as Postgres will abort tx2
:
ERROR: could not serialize access due to concurrent update