> Not sure if this is the problem you are running into, but it might be
> because SQLite is detecting a potential deadlock.

I think that's exactly what's going on.  SQLite is not bothering with the busy 
handler since it already knows no amount of waiting will solve the problem.

> When you just use BEGIN, SQLite will acquire locks lazily - the first
> read operation will acquire a shared lock, and the first write operation
> will acquire a reserved lock.

If I understand him correctly, drh said in another email that I could try using 
BEGIN IMMEDIATE rather than unqualified BEGIN to signal my intent to eventually 
do a write even though my first operation is a read.  You say the same thing.

> 1. Connection A begins a transaction. No locks acquired yet.
> 2. Connection B begins a transaction. No locks acquired yet.
> 3. Connection A reads from the database. It acquires a shared lock.
> 4. Connection B begin a transaction. No locks acquired yet.
> 5. Connection B writes to the database. It acquires a reserved lock.

(5) is the point where your example deviates from what my example program is 
actually doing.  My example program always starts with a read.  Thus, both 
connections will first try to acquire a shared lock.

> 6. Connection A writes to the database. It tries to acquire an exclusive
> lock.  But connection B already has one.

Wouldn't connection A try to acquire a reserved lock?  It's not going to go for 
the exclusive lock (by way of pending) until the COMMIT comes along.  But 
nevertheless, in your example, A can't get reserved because B already has it.

> Presumably, connection B would try to actually commit to the database at
> some point. But doing so would require it to wait for all shared locks
> to be released. Connection A is holding a shared lock. So now we could
> have a deadlock - A is waiting for B so it can acquire a reserved lock,
> and B is waiting for A so it can acquire an exclusive lock. SQLite
> detects this and connection A immediately returns SQLITE_BUSY, bypassing
> any busy timeout or busy handler.

I'm thinking one possible sequence might be:

1. A and B both run BEGIN.
2. A and B both run SELECT and both acquire SHARED.
3. A runs INSERT and acquires RESERVED.
4. B runs INSERT, but it can't acquire RESERVED because of A.  B is now 
blocked, and the busy handler is invoked.
5. A runs COMMIT and acquires PENDING.  However, it can't acquire EXCLUSIVE 
because B still holds SHARED.

SQLite breaks this deadlock by smiting A with SQLITE_BUSY, at which point A is 
expected to give up and let B proceed.

> To avoid this issue, use "BEGIN IMMEDIATE" instead of "BEGIN". This
> makes it so the writer immediately acquires a reserved lock. Since it
> jumps directly there instead of acquiring a shared lock first, there's
> no deadlock potential, so the busy timeout will actually be respected.

Like this?

1. A runs BEGIN IMMEDIATE and acquires RESERVED.
2. B runs BEGIN IMMEDIATE but can't acquire RESERVED.  B is now blocked, and 
the busy handler is invoked.
3. A runs SELECT and INSERT.
4. A runs COMMIT and acquires PENDING and EXCLUSIVE.
5. A completes and drops all locks.  B is now free to run.
6. B does everything A just did.
------------------------------------------- CONFIDENTIALITY NOTICE: This email 
and any attachments are for the sole use of the intended recipient and may 
contain material that is proprietary, confidential, privileged or otherwise 
legally protected or restricted under applicable government laws. Any review, 
disclosure, distributing or other use without expressed permission of the 
sender is strictly prohibited. If you are not the intended recipient, please 
contact the sender and delete all copies without reading, printing, or saving..
_______________________________________________
sqlite-users mailing list
[email protected]
http://mailinglists.sqlite.org/cgi-bin/mailman/listinfo/sqlite-users

Reply via email to