Apparently the following happens: 13875 Thread2 SAVEPOINT Thread2 success (shared lock acquired) 13875 Thread1 SAVEPOINT Thread1 success (shared lock acquired) 13880 Thread2 INSERT INTO TableB success (reserved lock) 13890 Thread2 RELEASE SAVEPOINT Thread2 fail (pending lock is acquired, exclusive lock cannot be acquired because of existing shared lock, the busyhandler callback is called here) 13890 Thread1 SELECT blah FROM TableB success (shared lock is still active) 13906 Thread1 SELECT blah FROM TableC success (shared lock is still active) 13906 Thread1 INSERT INTO TableD fail (reserved lock cannot be acquired because another thread already acquired one, busyhandler can NOT be called because another thread already have pending lock and waiting is senseless - error database is locked)
I'm not sure if SAVEPOINT actually acquires shared lock but probably something happened which you didn't tell and it acquired shared lock. General rule: if you're trying to promote read-only transaction into writing transaction and get SQLITE_BUSY you MUST rollback and try again. Another option - start transaction with BEGIN IMMEDIATE in the first place. Pavel On Fri, Feb 19, 2010 at 6:47 PM, Trainor, Chris <chris.trai...@ironmountain.com> wrote: > I am running into a "database is locked" error and I don't quite > understand what is going on. If someone could explain what is happening, > I'd appreciate it. (I am using sqlite version 3.6.17 on Windows XP.) > > A busy handler callback has been set up with sqlite3_busy_handler(). In > addition to some logging, the callback simply checks the number of > retries and either gives up (by returning 0) if it reaches the max retry > count or sleeps for 50 msecs then returns 1. > > I have two threads (each with their own connection) that are trying to > access the database. The 1st thread is doing some reads and writes and > the other one is just writing. For a while, things work as expected. > Thread1 does some reads and writes within explicit transactions (using > Savepoint and Release Savepoint syntax). Thread2 attempts to write to > the database and can't, so the busyhandler callback is called. > Eventually, Thread2 is able to get the exclusive lock and write to the > database (again within an explicit transaction). Thread1 does some more > reads and writes, etc. > > Here's where it stops making sense. I'm paraphrasing what is logged out > by our app. The first column is the number of msecs since the process > started. The second obviously is the active thread. 3rd is the query > (simplified for easy reading). The 4th indicates success or failure. > The 5th contains notes as to what appears to be happening. > > > 13875 Thread2 SAVEPOINT Thread2 success > (No locks acquired) > 13875 Thread1 SAVEPOINT Thread1 success > (No locks acquired) > 13880 Thread2 INSERT INTO TableB success > (exclusive lock) > 13890 Thread2 RELEASE SAVEPOINT Thread2 fail > (the busyhandler callback is called here) > 13890 Thread1 SELECT blah FROM TableB success > (shared lock???) > 13906 Thread1 SELECT blah FROM TableC success > (shared lock???) > 13906 Thread1 INSERT INTO TableD fail > (busyhandler is NOT called - database is locked) > > The call to sqlite3_step succeeds for the query "INSERT INTO TableB" at > 13880 msecs. I believe this means that an exclusive lock must have been > obtained for the connection on thread2. Thread2 attempts to release the > savepoint right around the same time as thread1 attempts to read from > TableB (at 13890 msecs). Thread1 is able to read from TableB, then is > able to read from TableC. This seems to indicate that the connection on > thread1 acquired a shared lock. I don't see how this is possible, since > thread2 should have had an exclusive lock at that point. I am assuming > that sqlite thinks that a deadlock will occur when thread1 tries to > write to the database at 13906 msecs and that is why the busyhandler > callback is not invoked. > > Also I'm not sure if it matters, but I am using sqlite3_exec to execute > the SAVEPOINT and RELEASE SAVEPOINT statements. All other queries are > executed using prepared statements and calls to sqlite3_step. Note that > there are NO nested transactions created. > > Can anyone shed some light on this? > > Thanks, > Chris > The information contained in this email message and its attachments > is intended > only for the private and confidential use of the recipient(s) named > above, unless the sender expressly agrees otherwise. Transmission > of email over the Internet > is not a secure communications medium. If you are requesting or > have requested > the transmittal of personal data, as defined in applicable privacy > laws by means > of email or in an attachment to email you must select a more > secure alternate means of transmittal that supports your > obligations to protect such personal data. If the reader of this > message is not the intended recipient and/or you have received this > email in error, you must take no action based on the information in > this email and you are hereby notified that any dissemination, > misuse, copying, or disclosure of this communication is strictly > prohibited. If you have received > this communication in error, please notify us immediately by email > and delete the original message. > _______________________________________________ > sqlite-users mailing list > sqlite-users@sqlite.org > http://sqlite.org:8080/cgi-bin/mailman/listinfo/sqlite-users > _______________________________________________ sqlite-users mailing list sqlite-users@sqlite.org http://sqlite.org:8080/cgi-bin/mailman/listinfo/sqlite-users