Dan Kennedy wrote:
On Wed, 2007-10-24 at 21:38 -0700, Richard Klein wrote:
As I was thinking about the locking mechanism in SQLite 3,
it occurred to me that the following race condition could
occur.

Imagine a joint bank account with a balance of $10,000.
The wife makes a withdrawal of $1,000 at ATM 'A' (serviced
by process A in the bank's mainframe), while at the same
time the husband makes a deposit of $1,000 at ATM 'B'
(serviced by process B).  The steps performed by each
process are as follows:

Process A
---------
BEGIN TRANSACTION;
SELECT balance FROM accounts WHERE accountId = '123-45-6789';
UPDATE accounts SET balance = <balance - withdrawal>
                 WHERE accountId = '123-45-6789';
COMMIT;

Process B
---------
BEGIN TRANSACTION;
SELECT balance FROM accounts WHERE accountId = '123-45-6789';
UPDATE accounts SET balance = <balance + deposit>
                 WHERE accountId = '123-45-6789';
COMMIT;

Both processes open the accounts database, obtain SHARED
locks, and proceed at about the same pace.  Process A
updates her local cache with a new balance of $900, while
process B updates his local cache with a new balance of
$11,000.

Evaluating the UPDATE requires a RESERVED lock on the database file. Since only one process can hold the RESERVED lock, this
particular scenario cannot occur. One or other of the updates
will fail with SQLITE_BUSY.

Dan.

I went over the documentation again, and it appears that you
are correct:  A process wanting to do an UPDATE must first
acquire a RESERVED lock.

However, I believe there is still a race condition.  Assume
that process B is the first to reach the UPDATE statement,
and so process B gets the RESERVED lock (which he later
escalates to PENDING and EXCLUSIVE).  Then process A is
stuck in a busy wait until process B commits and releases
his locks.  At this process A acquires the RESERVED lock
and does *her* UPDATE, but she does it using the old, now
*stale*, value of the account balance ($10,000).  That is,
she computes the new balance as ($10,000 - $1,000) = $9,000,
which is *wrong*.

She needs to *re-execute* her SELECT statement to pick up
the new balance of $11,000.  Or, in general:

She needs to start her transaction over from the beginning.

- Richard Klein

-----------------------------------------------------------------------------
To unsubscribe, send email to [EMAIL PROTECTED]
-----------------------------------------------------------------------------

Reply via email to