2013-09-20 47 views
1

這可能是一個不同的壞主意,或者我們必須解決的一個數據庫併發問題。MongoDB數據庫信號量和Node.js Process.NextTick()

我們有一個被調用來更新mongo記錄的方法。我們看到一些併發問題 - 進程A讀取記錄,進程B讀取記錄,進程A製作mods並保存記錄,進程製作B mods並保存記錄。因爲B在A之後讀取,所以在A寫入之前,它不知道A所做的更改,並且我們丟失了來自A的數據。我想知道我們是否不能使用數據庫信號量,集合,這是一個布爾值。如果我們在方法開始時讀取記錄,並且該字段爲真,則正在編輯它。此時,使用process.nexttick()重新調用該方法,並使用相同的數據。否則,設置信號量,並繼續。

讀取和保存之間仍然會有一段時間,但它應該比我們現在要做的更快。

是這樣的。任何想法,任何人都做過這樣的事情?它會工作嗎?

function remove_source(service_id,session, next) 
{ 
    var User = Mongoose.model("User"); 

    /* get the user, based on the session user id */ 
    User.findById(session.me,function(err,user_info) 
    { 
     if (user_info.semaphore === true) 
     { 
       process.nextTick(remove_source(service_id,session,next)); 
     } 
     else 
     { 
       user_info.semaphore = true; 
       user_info.save(function(err,user_new) 
       { 
        if (err) next(err,user_new); 
        else continue_on(null,user_new); 
       }); 
     } 

     function continue_on(user_new) 
     { 
      etc....... 
     } 

編輯:新代碼:

功能現在看起來如下。我正在對陣列進行單獨更新。這當然意味着如果第一次和第二次事務之間的事務失敗,我現在有可能使數據不同步。我在想,我可以簡單地重新保存在進入函數時檢索到的用戶對象,覆蓋我的更改。我不知道,如果我沒有改變這個對象,Mongoose/Mongo是不會做這個保存的,我們不得不去試試看。還有什麼想法?

var User = Mongoose.model("User"); 

/* get the user, based on the session user id */ 
User.findById(session.me,function(err,user_info) 
{ 
     if (err) 
     { 
      next(err,user_info,null); 
      return; 
     } 

     if (!user_info || user_info.length === 0) 
     { 
      next(_e("ACCOUNT_NOT_FOUND"),"user_id: " + session.me); 
      return; 
     } 

     var source_service_info = _.where(user_info.credentials, {"source_service_id": service_id}); 
     var source_service = source_service_info.source_service; 

     User.findByIdAndUpdate(session.me,{$pull: {"credentials": {"source_service_id": service_id}}},{},function(err,user_credential_removed) 
     { 
      if (err) 
      { 
       next(err,user_info,null); 
       return; 
      } 

      User.findByIdAndUpdate(session.me,{$pull: {"criteria": {"source_service": source_service}}},{},function(err,user_criteria_removed) 
      { 
       if (err) 
       { 
        next(err,user_info,null); 
        return; 
       } 

       else 
       { 
        next(null,user_criteria_removed); 
       } 
      }); 
     }); 
}); 

};

+0

您可能只是想爲您的文檔添加版本,並根據讀取後版本未更改的版本進行有條件更新。如果它未能更新,您會知道自上次讀取以來進行了修改。 – WiredPrairie

回答

1

您的方法存在的問題是,它只是縮短了數據可以被第二個進程讀取的時間,但並不能消除問題。

解決方法是將您的信號設置爲與讀取操作相同的操作。我沒有使用Mongoose,但是在MongoDB中,如果信號量爲false,則可以使用findAndModify返回用戶記錄;如果爲false,則在一個原子操作中,將信號量設置爲true。

如果您不想使用findAndModify,只有在未設置信號量的情況下,纔可以首先進行更新,將信號量設置爲true(或指定某個特定的ID值,以便知道它是您的信號量)。然後,如果該過程成功,您可以執行查找(可能會將您的信號量標識作爲查找條件)。但是,findAndModify(如果它在Mongoose中可用)將一步完成此操作。

此處描述了一種變體:http://docs.mongodb.org/manual/tutorial/isolate-sequence-of-operations/其中您執行一種樂觀鎖定形式,在將這些舊值更改爲新值之前檢查舊值是否保持不變。

存在使用一個單獨的表來模擬兩相在這個變化承諾:http://docs.mongodb.org/manual/tutorial/perform-two-phase-commits/

+0

這是我的想法,在我看後。這並沒有讓我們買得太多。這是我們與芒戈的第一個大項目。儘量保證它儘可能安全地進行多項交易似乎是設計中最醜陋的部分。我喜歡你這樣做的方式,這是有道理的。我想我們必須從查找和修改中測試結果。我很確定它在貓鼬那裏。 – CargoMeister

+0

我喜歡這種簡單。有一件事仍然困擾着我,如果某件事失敗了,記錄就會被鎖定。可能能夠通過try/catch塊來處理,或者只是在下面的錯誤檢查中。 – CargoMeister

+0

如果事務安全性至關重要,MongoDB將會是一些額外的工作。正如Kyle Banker在Mongo In Action http://www.manning.com/banker2/中所說的,沒有人會在MongoDB中建立銀行的後端。 – nachbar

1

編輯:在下面的交匯處,這似乎是一個模式和更新的問題。問題可能會變成這樣:我在數組中有一些條目,並且這些條目的序號索引也與其他一些數組相關。如何在不失配的情況下執行刪除操作?

取決於現實世界中的頻率與QA測試場景中出現的三種可能性。

  1. 考慮添加已刪除的標誌,但保持記錄的順序相同。如果有人切換,重複使用相同的記錄,但無論您想要如何修復。
  2. 對每個元素使用關聯數組(JS對象)(而不是關係世界的特徵)。如果您需要訂單,請添加按順序列​​出鍵的數組。兩者都有更新的語法,而不用觸及任何其他變化的內容,也不會覆蓋對不同字段的更改。
  3. 使用鍵是數字的關聯數組。實際刪除不會影響檢索。

    東西= {} 東西[1] = {一些: '細節'} 東西[2] = {一些: 'details2'}

當時 1)你在修改同一個領域?將其作爲一個數組,並推送更改,並彈出最新的值以讀取當前值。

2)你是否在改變不同的領域,但數據正在遭受挫折?然後有更好的語法用於更新。你可以逐場更新。

$set: { 'fielda': 'valuea' } 

不會失去以前的領域

3)編輯可以改變你的架構

4)改變的過程的時間,使他們不重疊。或者他們可以在較小的子集中這樣做,以防止重疊。

我想知道,出於興趣,需要多個進程來更新相同的記錄?我不會用任何看起來像這樣的東西。

+0

客戶端是一個ipad客戶端,它發送一個領帶的多個更改。我們已經將客戶所擁有的物品構建到用戶集合中,因爲我們經常更改關於用戶的信息以及他們同時創建的物品。該文件有許多子文件。它使更新更安全,然後將其分開。特別是在這種情況下,它是來自各種外部服務的證書 - 他們可能決定放棄登錄。客戶同時發送它們,而不是一個接一個地發送出去。我們可能會將它們作爲單個數組發送。 – CargoMeister

+0

刪除現有憑證的用例不需要存在交叉更新衝突 - 它們在用戶記錄中的某個時刻是不同的。這聽起來像是你正在樹中對該字段進行更新。向我們展示更新,這是一個快速解決方案。 –

+0

嗨,吉姆,是的,我們正在保存整個用戶對象。憑證位於數組中,然後與它們關聯的對象位於另一個數組中。我們正在操作這些數組,然後更新用戶對象,然後重新保存整個用戶對象。從你所說的話看,這可能不是最好的策略。但是,由於它們是數組,我們正在刪除成員,我可以刪除數組中的單個成員嗎?我們也喜歡在完成時壓縮數組,以便我們有連續的編號,而不是留下空白。所以,編號可以在保存之間改變。 – CargoMeister