2

我使用 ZODB 作为将通过 Web 服务修改的对象的持久存储。下面是我减少问题的一个例子。增量函数是从多个线程调用的。我的问题是,当从两个线程同时调用增量时,对于不同的键,我得到了冲突错误。

我想应该可以解决这个问题,至少只要修改不同的键,以适当的方式?如果是这样,我没能找到一个关于如何...的示例(zodb-documentation 似乎有些分散在不同的站点:/)

很高兴有任何想法...

import time
import transaction
from ZODB.FileStorage import FileStorage
from ZODB.DB import DB
from ZODB.POSException import ConflictError

def test_db():
    store = FileStorage('zodb_storage.fs')
    return DB(store)

db_test = test_db()     

# app here is a flask-app
@app.route('/increment/<string:key>')
def increment(key):
    '''increment the value of a certain key'''

    # open connection
    conn = db_test.open()
    # get the current value:
    root = conn.root()
    val = root.get(key,0)    

    # calculate new value 
    # in the real application this might take some seconds
    time.sleep(0.1)
    root[key] = val + 1     

    try:
        transaction.commit()
        return '%s = %g' % (key, val)
    except ConflictError:
        transaction.abort()
        return 'ConflictError :-('
4

1 回答 1

3

您有两个选择:实施冲突解决方案,或使用新数据重试提交。

冲突解决仅适用于您存储在 ZODB 中的自定义类型,并且只有在您知道如何将更改合并到新更改的状态时才能应用。

ZODB_p_resolveConflict()在自定义类型上查找方法,并使用旧状态、您与之冲突的已保存状态以及您尝试提交的新状态调用该方法;您应该返回合并状态。对于一个简单的计数器,就像在您的示例中一样,这就像使用旧状态和新状态之间的更改更新保存的状态一样简单:

class Counter(Persistent):
    def __init__(self, start=0):
        self._count = start

    def increment(self):
        self._count += 1
        return self._count

    def _p_resolveConflict(self, old, saved, new):
        # default __getstate__ returns a dictionary of instance attributes
        saved['_count'] += new['_count'] - old['_count']
        return saved

另一种选择是重试提交;您想限制重试次数,并且您可能希望将其封装在方法的装饰器中,但基本原则是循环到一个限制,根据 ZODB 数据进行计算(在发生冲突错误后,将在需要时自动读取新数据),然后尝试提交。如果提交成功,您就完成了:

max_retries = 10
retry = 0

conn = db_test.open()
root = conn.root()

while retry < max_retries:
    val = root.get(key,0)    
    time.sleep(0.1)
    root[key] = val + 1

    try:
        transaction.commit()
        return '%s = %g' % (key, val)
    except ConflictError:
        retry += 1

raise CustomExceptionIndicatingTooManyRetries
于 2013-08-07T10:18:30.657 回答