The following is part of a Python Flask application running on Google App Engine:
@app.route('/blabla', methods=['GET'])
def blabla():
# memcache.add('key', None) # this "fixes" it!
memcache_client = memcache.Client()
while True:
value = memcache_client.gets('key')
if value is None: # First time
updated_value = 'Bla'
else:
updated_value = value + ', bla'
if memcache_client.cas('key', updated_value):
return updated_value
Starting with an empty cache, if we make consecutive GET requests to /blabla, I would expect the requests to return:
Bla
Bla, bla
Bla, bla, bla
.
.
(If for some reason at some point between .gets()
and cas()
the cache gets flushed, then I would expect the sequence to restart, no problem.)
But we don’t get anything because memcache_client.cas()
keeps returning False
forever, so the program gets stuck in the while
-loop. Apparently this happens because the key 'key'
does not exist in the beginning.
I know this because if I uncomment the memcache.add('key', None)
, it sort-of works, because then the key exists and .cas()
is happy and returns True
. But if right between the .add()
and the .gets()
some other process were to flush the cache, we’d be back to where we started from, with a missing key, and .cas()
would go back to returning False
indefinitely. So it is not a good solution.
Why doesn’t .cas()
work if the key is missing in the beginning? Or at least, why doesn’t .cas()
accept an initial_value=
parameter, like its sibling decr()? Is it a bug or a feature? I can’t find this documented properly anywhere, except that Guido van Rossum alludes to it in his single blog post on the matter—reffering to an assert
he makes that the .gets()
does not return None
, he says:
Aside 2: The assert is kind of naive; in practice you'll have to somehow deal with counter initialization.
Dank je wel Guido—does anyone know how, please?
Ok, I figured it out.
It’s more complicated than I would have liked it to be, but it works.
The last
else: continue
is redundant but I’m writing it to make it clear that we’re going to continue trying until we succeed.Although in practice you'll have to somehow deal with giving up after a number of retries.