Google App Engine memcache.Client.cas()会为丢失的密钥返回False

时间:2016-12-23 22:34:46

标签: python google-app-engine caching google-app-engine-python

以下是在Google App Engine上运行的Python Flask应用程序的一部分:

@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

从空缓存开始,如果我们向/ blabla发出连续的GET请求,我会希望返回请求:

Bla
Bla, bla
Bla, bla, bla
.
.

(如果出于某种原因,在.gets()cas()之间的某个时刻缓存被刷新,那么我希望序列重启,没问题。)

但是我们没有得到任何东西,因为memcache_client.cas()永远不断返回False,所以程序卡在while - 循环中。显然这是因为键'key'在开头不存在。

我知道这一点,因为如果我取消对memcache.add('key', None)的注释,那么它就会有效,因为密钥存在且.cas()很高兴并返回True。但是如果在.add().gets()其他进程之间正好刷新缓存,我们会回到我们开始的位置,缺少密钥,.cas()会去无限期地返回False。所以这不是一个好的解决方案。

如果在开头缺少密钥,为什么.cas()不起作用?或者至少,为什么.cas()不接受initial_value=参数,例如它的兄弟decr()?这是一个错误还是一个功能?我无法在任何地方找到正确的文件,除了Guido van Rossum在他的single blog post on the matter中提到它 - 向assert提出要求他使.gets()没有返回None他说:

  

除了2:断言是天真的;在实践中,你必须以某种方式处理计数器初始化。

Dank je wel Guido-有谁知道怎么样,拜托?

1 个答案:

答案 0 :(得分:1)

好的,我明白了。

@app.route('/blabla', methods=['GET'])
def blabla():
    memcache_client = memcache.Client()
    while True:

        if memcache.add('key', 'Bla'):
            # That's all folks!
            return 'Bla'

        # add() failed => the key must already exist, we have to compare-and-set.

        value = memcache_client.gets(key_name)

        if value is None:
            # At the time add() failed the key existed, but now gets() is returning None,
            # so somebody must have deleted the cache entry in between.
            # Let's start from scratch.
            continue

        updated_value = value + ', bla'

        if memcache_client.cas(key_name, updated_value):
            return updated_value
        else:
            continue

它比我希望的更复杂,但它确实有用。

最后else: continue是多余的,但我写的是为了表明我们将继续尝试,直到我们成功。

虽然在实践中你不得不以某种方式处理经过多次重试后的放弃。