2012-03-21 88 views
1

我有一臺服務器和一臺客戶機。zodb:數據庫衝突失敗

客戶端發送請求。該請求具有與其相關的特定密鑰,例如, a-1,a-2b-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 

這解決了我的問題,但我不把那作爲一個答案,因爲我還是想知道:爲什麼它給衝突錯誤,如果我不之前沒有開始新的交易?

+1

請考慮將此發佈到ZODB-dev郵件列表。如果您得到答案,請將其發回。 – sdupton 2012-03-21 21:10:12

+0

@sdupton:謝謝,會做! – Claudiu 2012-03-21 22:54:06

回答

3

從交易開始的那一刻起,ZODB提供了一致的視圖連接。這意味着最重要的是一個線程不會看到其他線程所做的數據庫更改,直到一個新的事務開始!這是數據庫的基本特徵,稱爲Multiversion Concurrency Control。換句話說,如果您希望您的應用程序線程不會因使用鎖定而發生衝突,那麼您也需要在鎖定可用時啓動新的事務。

在我看來,鎖定是你想要避免的性能瓶頸。持有鎖的時間越長,遇到不一致數據的機會就越大(起始狀態與數據庫狀態)。

在Zope中,使用了一種樂觀的方法:當發生衝突錯誤時,事務將被中止,並重新從頭重新嘗試。如果您的事務是短而快的事務,那麼您可以避免大多數鎖定問題,而只要持久更改發生更改而導致衝突時,您就可以將更改重新計算到數據庫。

+0

啊,這很有道理。感謝您的解釋!回覆:鎖定,這可能確實沒有必要。如果沒有它,我會看看我能做些什麼。 – Claudiu 2012-03-23 14:45:04