我有一臺服務器和一臺客戶機。zodb:數據庫衝突失敗
客戶端發送請求。該請求具有與其相關的特定密鑰,例如, 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
是一個字典映射鍵threading.Lock
秒。之後遵循修改持久數據結構的代碼。
我認爲會發生的是,如果有一個請求進入正在處理的密鑰,它會在獲取鎖時阻塞。只有當先前的請求已經提交(因此超出任何衝突錯誤)時,新請求才會恢復。這些請求在獲取鎖之前不做任何衝突。
大部分請求做工精細:
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
這解決了我的問題,但我不把那作爲一個答案,因爲我還是想知道:爲什麼它給衝突錯誤,如果我不之前沒有開始新的交易?
請考慮將此發佈到ZODB-dev郵件列表。如果您得到答案,請將其發回。 – sdupton 2012-03-21 21:10:12
@sdupton:謝謝,會做! – Claudiu 2012-03-21 22:54:06