2013-03-11 59 views
0

在我現在正在開展的項目中,我們稱之爲挑戰。挑戰有會員參與者。成員是能夠接受挑戰(可以是單個用戶或用戶組)的每個人,並且參與者跟蹤每個用戶的參與統計。在基於事件的系統中同時處理唯一性

每次添加新的挑戰成員時都會重新計算挑戰參與者。這發生在基於事件的情況下,以便挑戰成員觸發created事件,挑戰參與者偵聽的事件。

當同時創建兩個質詢成員時會出現問題,這意味着該事件也會被觸發兩次,並且兩個執行代碼同時運行。爲了說明:

challenge.getMembers(); 
challenge.getParticipants(); 

// calculations 

foreach member not in participants, create participant 

如所述,問題,當上述代碼被同時運行的,更具體地,當第二執行之前到達所述第一創建了參與者條目有失蹤getParticipants出現。兩次執行都看到一些參與者缺失,並創建它們。這意味着我們現在有一些用戶的重複參賽者條目。

現在我們解決這個問題的方法是在挑戰參與者challenge_id, user_id上有一個唯一索引。它確實感覺有點骯髒,但只要忽略違反約束時的錯誤。這也使得檢查其他SQL錯誤更加困難,因爲所有的錯誤都傳遞給回調函數,然後我們必須檢查錯誤字符串的內容以查看它是否是我們喜歡的錯誤(違反唯一性),或者我們不喜歡的錯誤(如錯誤的語法),應該處理。

這段代碼可以在多臺服務器上運行,所以即使有了這些代碼,我們也不能保證代碼不會同時運行。我們也不介意它同時運行不同的挑戰,只要它不會同時運行相同的挑戰。有沒有人有處理這種類型的併發合併的建議?

+3

的簡單解決方案(但可能不是太有效率)將保持在數據庫中每一個挑戰的鎖,或某種形式的緩存/到所有服務器訪問的共享存儲層中。然而,更大的問題在於你的設計。爲什麼在添加新成員時創建所有參與者?爲什麼不只是在添加成員時創建每個成員作爲參與者?另外,即使你覺得不應該發生這種情況,你也應該對你的db有獨特的約束。這是一個很好的回退,因爲你的代碼不如你想象的那麼好。 – 2013-03-11 17:46:06

+0

你的模式對挑戰,參與者和成員來說是什麼樣的? – arnorhs 2013-04-30 08:45:32

回答

0

既然你說你不介意爲不同的挑戰同時運行代碼,你可以在挑戰表中有一個狀態欄。然後你會發出一個UPDATE challenges SET state = {locked} WHERE id = {id} AND state = {unlocked}。如果此查詢成功並且受影響的行等於1,則可以繼續更新參與者並在之後解鎖挑戰。如果查詢不影響任何行,那麼挑戰可能會被某個其他執行線程更新,因此您可能要跳過此步驟或安排它稍後重新運行。

如果你走這條路,那麼你就需要小心解鎖你的挑戰,並做一些事情,防止它們被卡住,如果一個線程在更新過程中崩潰,不解鎖的挑戰。例如,您可以使用state = {thread id},然後您可以檢測哪些線程已經死亡並解除其挑戰。