Problem with Lightweight Transactions

57 views
Skip to first unread message

Alan Pound

unread,
Nov 20, 2014, 12:50:55 PM11/20/14
to python-dr...@lists.datastax.com
Ok, this is probably not be an issue with the python driver, but I've got it in python, so it is for me.....

I was having a problem implementing a kind of 'row lock' using Lightweight Transactions.
I've boiled the issue down to a very short test program that demonstrates when run against a single 
(non-clustered) local cassandra instance - no complications - just as it is...  Just run it in
two terminals at the same time, with a different argv[1]...
#=============
import time, sys, os, cassandra
from cassandra.cluster      import Cluster
from cassandra.auth         import PlainTextAuthProvider

instance = sys.argv[1] 

cluster = Cluster( auth_provider=PlainTextAuthProvider( username='cassandra', password='cassandra'))
cdb = cluster.connect()
cdb.execute("CREATE KEYSPACE IF NOT EXISTS test WITH replication = {'class':'SimpleStrategy', 'replication_factor' : 1}")
cdb.execute("CREATE TABLE IF NOT EXISTS test.test ( id text primary key, val int, lock text )")
cdb.execute("INSERT INTO test.test (id, val, lock) VALUES ('session_id1', 0, '') ")
raw_input( '<Enter> to start ... ')

i = 0
while i < 10000:
    i += 1
    # set lock
    while True:
        r = cdb.execute( "UPDATE test.test SET lock = '%s' WHERE id = 'session_id1' IF lock = '' " % instance)
        if r[0].applied == True:
            break
    # check lock and increment val
    s0 = cdb.execute("SELECT val,lock FROM test.test WHERE id = 'session_id1' " )[0]
    if s0.lock != instance:
        print 'error: instance [%s] %s %s' % (instance, s0, r[0])
    cdb.execute( "UPDATE test.test SET val = %s WHERE id = 'session_id1'", (s0.val + 1,))        
    # clear lock
    cdb.execute( "UPDATE test.test SET lock = '' WHERE id = 'session_id1' ")        
    time.sleep( .01)
#=============
.... So the 'lock' is just being used here to protect the read/increment/write of the 'val' field.
With a count of 10000 as above, and two instances, the value of 'val' should reach 20000.  
It gets close, but no cigar - unless I patch in a 'fix' as follows...
#=============
    # set lock
    while True:
        r = cdb.execute( "UPDATE test.test SET lock = '%s' WHERE id = 'session_id1' IF lock = '' " % instance)
        if r[0].applied == True:
            s = cdb.execute("SELECT lock FROM test.test WHERE id = 'session_id1' " )
            if s[0].lock == instance:
                break
    # check lock and increment val
#=============
so here, after the string has been posted into 'lock', and applied==True, I now check to see if the value
of 'lock' is what I think it should be - and sometimes it isn't, so with the 'fixed' code it only breaks out of the loop
if the value of 'lock' is correct.

This really isn't what I expected!  To me, these Lightweight Transactions don't seem to do what they say on the tin...
Can you throw some light on this please? (I've had this on stackoverflow - 50+ views in 8 or so days, but not a single 
comment...)

Thanks again (last one for the moment - I promise...)
Regards
Alan

Tyler Hobbs

unread,
Nov 21, 2014, 5:30:40 PM11/21/14
to python-dr...@lists.datastax.com
Your query to clear the lock isn't going through paxos; that's what giving you problems.  Simply change this query:

On Thu, Nov 20, 2014 at 11:50 AM, Alan Pound <alan.p...@gmail.com> wrote:
    cdb.execute( "UPDATE test.test SET lock = '' WHERE id = 'session_id1' ")      

to:

cdb.execute("UPDATE test.test SET lock = '' WHERE id = 'session_id1' IF lock = '%s'" % (instance,))

The moral of the story is this: don't mix LWT and non-LWT writes to the same values unless you have a really good idea of what you're doing :)


--
Tyler Hobbs
DataStax

Alan Pound

unread,
Nov 24, 2014, 6:51:45 AM11/24/14
to python-dr...@lists.datastax.com
Hi Tyler.
Thanks very much for the reply.
I think if I was maintaining the usual kind of 'running total' kind of data, I would never have considered anything other than LWT for each and every update, but given what I was actually trying to do: "an atomic test-and-set of a variable to get a lock, then why would I care how it got cleared", it wasn't at all intuitive that I should use LWT at both ends...

...but now you have said (I think) "for the mechanism used to test and acquire the lock to work correctly, it depends upon the same mechanism being used to release it" - yes I get it (well, now I do)...

And it works (of course...)

Thanks again (I owe you a beer).
Alan
Reply all
Reply to author
Forward
0 new messages