我使用 ZODB 作为将通过 Web 服务修改的对象的持久存储。 下面是我简化问题的示例。 增量函数是从多个线程调用的。 我的问题是,当从两个线程同时调用不同键的增量时,我收到冲突错误。
我想应该可以解决这个问题,至少只要以正确的方式修改不同的键? 如果是这样,我没有找到关于如何...的示例(zodb 文档似乎有点分布在不同的站点上:/)
很高兴有任何想法......
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 :-('
最佳答案
这里有两个选择:实现冲突解决,或使用新数据重试提交。
Conflict resolution仅适用于您存储在 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
关于python - 同时修改ZODB中的不同键,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/18098979/