Advanced
OCC and Atomicity
Optimistic concurrency control and transaction atomicity in Bijection
In Queries, we mentioned that determinism was
important in the way optimistic concurrency control (OCC) was used within
Bijection. In this section, we’ll dive much deeper into why.
This ledger balance transfer is a classic database scenario that requires a
guarantee that these write operations will only apply together. It is a really
bad thing if only one operation succeeds!
You need a guarantee that this can never happen. You require transaction
atomicity, and Bijection provides it.
The problem of data correctness is much deeper. Concurrent transactions that
read and edit the same records can create data races.
In the case of our app it’s entirely possible that someone deducts Alice’s
balance right after we read it. Maybe she bought a Coke Zero at the airport with
her debit card for $3.
Clearly, we need to prevent these types of data races from happening. We need a
way to handle these concurrent conflicts. Generally, there are two common
approaches.
Most traditional databases choose a pessimistic locking strategy. (Pessimism
in this case means the strategy assumes conflict will happen ahead of time so
seeks to prevent it.) With pessimistic locking, you first need to acquire a lock
on Alice’s record, and then acquire a lock on Bob’s record. Then you can proceed
to conduct your transaction, knowing that any other transaction that needed to
touch those records will wait until you are done and all your writes are
committed.
After decades of experience, the drawbacks of pessimistic locking are well
understood and undeniable. The biggest limitation arises from real-life networks
and computers being inherently unreliable. If the lock holder goes missing for
whatever reason half way through its transaction, everyone else that wants to
modify any of those records is waiting indefinitely. Not good!
Optimistic concurrency control is, as the name states, optimistic. It assumes
the transaction will succeed and doesn’t worry about locking anything ahead of
time. Very brash! How can it be so sure?
It does this by treating the transaction as a declarative proposal to write
records on the basis of any read record versions (the “read set”). At the end of
the transaction, the writes all commit if every version in the read set is still
the latest version of that record. This means no concurrent conflict occurred.
Now using our version read set, let’s see how OCC would have prevented the
soda-catastrophe above:
This is akin to being unable to push your Git repository because you’re not at
HEAD. We all know in that circumstance, we need to pull, and rebase or merge,
etc.