Looks like simple CAS setup doesn't work
I've tested simple CAS setup, two parallel python processes updating some key on single shared server:
- Set the key:
mc.set('k', 0)
- Run two processes with that code:
for x in range(10**5):
while True:
k = mc.gets('k')
if mc.cas('k', k+1):
break
- Read the key:
mc.get('k')
# some random number less than 2*10^5 (usually about 1.5*10^5)
The cas() call never failed.
Same test, but with cas() replaced by incr() always gives 2*10^5
Python 3.6.3, python-memcached==1.59, everything was inside docker-compose setup
You can use ngrep to dump the traffic, and look at STORE and EXISTS responses.
From the protocol "spec":
After sending the command line and the data block the client awaits
the reply, which may be:
- "STORED\r\n", to indicate success.
- "NOT_STORED\r\n" to indicate the data was not stored, but not
because of an error. This normally means that the
condition for an "add" or a "replace" command wasn't met.
- "EXISTS\r\n" to indicate that the item you are trying to store with
a "cas" command has been modified since you last fetched it.
- "NOT_FOUND\r\n" to indicate that the item you are trying to store
with a "cas" command did not exist.
I've tried ngrep and discovered that cas command is actually not used.
Looks like cas_ids are not set for some reason and that causes using set command in place of cas:
In [10]: cl.gets('a')
Out[10]: '2'
In [11]: cl.cas_ids
Out[11]: {}
I can write tests and fixes for cas functionality, if PR's for that subsystem are welcome.
Did you ensure that cache_cas=True was set when you initialized the clients?
According to this line if that isn't set, the cas_id basically gets ignored.
I've finally got time to debug that
Looks like there are two problems:
-
casis ignored silently withoutcache_cas=True -
cascommand checks for'i'key incas_ids, but the dictionary hasb'i'key, socasis just ignored silently
After settting cache_cas=True and manually patching cas_ids between gets() and cas() calls I've finally got actual cas command in ngrep output