2015-02-07 90 views
1

我正在使用MongoDB和NodeJS。所以我使用貓鼬。如何用mongoDB/mongoose並行執行讀取/寫入文檔

我正在開發一款多人實時遊戲。所以我在同一時間收到許多玩家的許多請求。

我可以說,我有一所房子集合簡化它,看起來像這樣:

{ 
    "_id" : 1, 
    "items": [item1, item2, item3] 
} 

我有一個靜態函數,每個接收請求後調用:

house.statics.addItem = function(id, item, callback){ 
    var HouseModel = this; 
    HouseModel.findById(id, function(err, house){ 
     if (err) throw err; 
     //make some calculations such as: 
     if (house.items.length < 4){ 
      HouseModel.findByIdAndUpdate(id, {$push: {items: item}}, cb); 
     } 
    }); 
} 

在這個例子,我編碼,以便house文件不能有超過4個項目。但是會發生的是,當我同時收到幾個請求時,這個函數會被兩個請求執行兩次,並且由於它是異步的,它們都會將新項目推送到項目字段,然後我的房子有5個項目。

我做錯了什麼?我怎樣才能避免將來的這種行爲?

回答

-2

此函數從不執行兩次,因爲更新操作在單個文檔的級別上是原子級的。 更多的信息在官方手冊:http://docs.mongodb.org/manual/core/write-operations-atomicity/#atomicity-and-transactions

+1

更新本身可能是原子性的,但調用它和在它之前的測試不是。在find和它的回調之間以及find-and-update和它的回調之間,可以運行任意數量的其他調用並查看過時的未更新數據。請參閱下面的答案。 – Andras 2015-02-07 20:57:48

2

是的,你需要更好地鎖定houseModel,以表明addItem 正在進行中。

的問題是,多個請求可以調用findById,看到了同樣的 house.items.length,然後根據該(過時)快照 ,這是確定要增加一個項目各自確定。原子性的nodejs邊界是 回調;在異步調用和其回調之間,其他請求可以運行。

一個簡單的解決方法是跟蹤房子中的物品數量,但也要跟蹤預期的addItems的數量。在進入addItem時,碰撞「想要 添加更多」計數,並測試。

+0

感謝您的回答。如果我得到它,你的意思是變量「預期添加項目」將被存儲在RAM中? – alexislg 2015-02-07 21:46:18

+0

是的,由house _id索引,也許作爲house.statistics的一個屬性。這應該工作,除非你運行多個服務器和/或節點集羣。對於多進程,你必須在memcache或mongodb本身的外部共享存儲中實現互斥計數器。 – Andras 2015-02-07 23:04:05

+0

謝謝!這肯定會起作用。你知道這是否是處理這種情況的常用方法?我的應用程序將運行在多個進程中,所以感謝您的額外解釋 – alexislg 2015-02-08 21:08:22

0

自從Mongoose 4.10.8發佈以來,一種可能的方法是編寫一個插件,使得save()在您加載它之後修改文件時失敗。部分示例在#4004引用:

@vkarpov15說:

8b4870c應該給你的人會怎麼寫這個

插件由於貓鼬4.10.8大方向,插件現在可以訪問this.$where。對於從數據庫中加載的文檔(即不是this.isNew),插件可以添加條件,這些條件將在更新期間由MongoDB進行評估,這可以防止更新實際發生。此外,如果架構的saveErrorIfNotFound選項已啓用,則save()將返回錯誤而不是成功,如果文檔保存失敗。

通過編寫這樣的插件並更改文檔的每個更新的某些屬性(例如版本號),可以實現「樂觀併發」(如標題爲#4004)。即,你可以編寫大致做findOne()的代碼,做一些修改邏輯,save()if (ex) retry()。如果你所關心的是一個文件保持自我一致,並確保Mongoose的驗證器運行你的文檔沒有高度爭議,這可以讓你編寫簡單的代碼(無需使用繞過Mongoose驗證器的東西,如.update()),而無需犧牲安全性(即,如果在此期間修改了文檔並避免覆蓋已提交的更改,則可以拒絕save())。

對不起,我還沒有一個代碼示例,也不知道是否有一個npm上的包,它將此模式實現爲插件。

相關問題