我有一个服务器和一个客户端。
客户端发送请求。该请求具有与其关联的某个键,例如a-1
, a-2
, b-1
, b-4
。
如果同一密钥的两个请求同时出现,则会出现冲突错误,因为正在修改相同的数据结构。
我可以让客户端简单地不一次发送相同密钥的两个请求。但是,我希望这个系统也能与多个客户一起工作。让客户端协调他们发送到服务器的内容似乎很愚蠢。相反,如果该密钥已被修改,我希望服务器简单地阻止对该密钥的请求,直到完成具有相同密钥的其他请求。
为此,我创建了一个锁定系统。在服务器上的功能开始时,我这样做:
key = ...
print "Acquiring %s lock..." % (key,)
KEY_LOCKS[key].acquire()
print "%s lock acquired." % (key,)
def after_commit_hook(success):
KEY_LOCKS[key].release()
print "(after %s commit): Released %s lock" % (('failed', 'successful')[success], key)
transaction.get().addAfterCommitHook(after_commit_hook)
哪里KEY_LOCKS
是 dict 映射键到threading.Lock
s。之后是修改持久数据结构的代码。
我假设会发生的是,如果一个请求进入一个已经被处理的密钥,它会在获取锁时阻塞。只有当较早的请求已经提交(因此超出任何冲突错误)时,新请求才会恢复。在获取锁之前,请求不会发生任何冲突。
大多数请求都可以正常工作:
Acquiring a-b lock...
a-b lock acquired.
(after successful commit): Released a-b lock
Acquiring a-c lock...
a-c lock acquired.
(after successful commit): Released a-c lock
但是,发送相同的密钥时仍然存在问题,即使锁定似乎有效:
Acquiring q-q lock...
q-q lock acquired.
Acquiring q-q lock...
(after successful commit): Released q-q lock
q-q lock acquired.
(after failed commit): Released q-q lock
repoze.retry retrying, count = 1
Traceback (most recent call last):
...
ConflictError: database conflict error (oid 0x13009b, class persistent.list.PersistentList)
然后请求重试。请注意,q-q lock
仅在成功提交后才获得。
是什么赋予了?为什么这个系统不能防止冲突错误?我的假设在哪里不正确?
编辑:好吧,如果在transaction.get().addAfterCommitHook(after_commit_hook)
我放的那一行之前transaction.begin()
,它可以工作。对于我的生活,我无法弄清楚为什么。在该transaction.begin()
行之前,我的整个代码是:
post = request.params
if not post: return Response("No data!")
data = eval(post['data'])
time_parsed = time.time()
my_app = request.context
这解决了我的问题,但我没有把它作为答案,因为我仍然想知道:如果我之前没有开始新的交易,为什么会出现冲突错误?