Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Locking a sqlite3 database in Python (re-asking for clarification)

Tags:

sqlite

A few weeks ago, I posted this question on SO regarding how to lock a sqlite3 database in python:

How to lock a sqlite3 database in Python?

However, I'm not quite convinced that the answer works. Or, maybe I'm just misunderstanding the answer.

Here's the situation I ran into:

  • I've got a database "test"
  • In database "test" there is one table "book"
  • In table "book" there are two columns: "title", and "checked_out_by"

Then I have a function that works like this:

def checkout(title, user):
    con = get_connection_from_db()
    with con:
        checked_out_by = get_checked_out_by(title)
        if checked_out_by == '': # If NOT checked out:
            checkout(title, user)
            print user, "checked out", title
        elif checked_out_by == 'user':
            print user, "already got it"
        else:
            print user, "can't check it out because", checked_out_by, "has it!"

So the checkout() function first verifies that the book is NOT checked out, and, if so, checks out the book. Note that I'm using the recommended "with con:" trick to ensure that everything is transactional and happy and copacetic.

However, I ran a bunch of concurrency tests and found problems. Specifically, when I run the two following calls concurrently:

checkout('foo', 'steve')
checkout('foo', 'tim')

The output indicates that it doesn't work quite right. I expect to see one of the two following possible outputs:

steve checked out foo
tim can't check it out because steve has it!

OR:

tim checked out foo
steve can't check it out because tim has it!

But occasionally, I'll get this output:

tim checked out foo
steve checked out foo

I thought the 'with con:' trick would ensure that my DB calls would be bundled together. Can someone explain to me if/how I got this wrong? If so, is there a way to make this work?

like image 403
Stephen Gross Avatar asked Jan 30 '12 20:01

Stephen Gross


1 Answers

'with con' is NOT what is wanted here. (or this thread locking rubbish)

To get exclusive access for a specific period (not just while an individual query/trasaction is taking place) you need to do;

con = sqlite3.connect()
con.isolation_level = 'EXCLUSIVE'
con.execute('BEGIN EXCLUSIVE')
#exclusive access starts here. Nothing else can r/w the db, do your magic here.
con.commit()
con.close()

Hopefully this saves someone from the searching/experimenting i've just been through!

Remember it's not exclusive until you run begin exclusive, and it will stay exclusive until you close (or run commit, i think). You can always test w/ the python interpreter / CL sqlite3 app if you aren't sure.

like image 76
hops Avatar answered Jan 01 '23 08:01

hops