同步多个请求和多个数据库调用

时间:2015-07-17 09:55:15

标签: node.js mongodb asynchronous mongoose

我正在处理一个问题,当我在node.js应用程序中使用mongo数据库(使用mongoose)创建文档时,我会遇到重复的键错误。

以下是方案

更新特定用户的HTTP请求。

POST /process { "user": 123 }

服务器检查用户是否存在。如果它不存在,则会创建一个新用户,否则会更新用户并保存。

User.findOne({ "user": 123 }, function (err, doc) {
    if (!doc) {
        doc = new User({ ... });
    }
    doc.updated = Date.now();
    doc.save(...);
}

有两个异步调用findOnesave。我遇到的问题是当一个异步调用(即doc.save在第一个请求完成之前第二个HTTP请求(针对同一个用户)进来时会发生什么。即使node.js是单个的如果在异步I / O期间有一些延迟,这仍然会发生。

R1: POST /process
R1: findOne => async
R2: POST /process
R2: findOne => async
R1: !doc = true
R2: !doc = true

因此,对于这两个请求,应用程序认为用户不存在,因此尝试使用相同的密钥创建文档两次。

如何解决?

  • 首先,我将findOnesave之间的时间缩短了一些。但是,问题仍然在某些情况下发生(只有1/1000)。

  • 我不想使用upsert,因为在创建新用户时我还设置了一些默认值的其他字段。我认为这对于upsert来说会很棘手。

  • 理想情况下,我想确保只有一个请求可以立即进入处理逻辑(有点像函数调用的互斥锁)。但是,我不想阻止请求调用 - 所以也许我可以使用一些很好的异步锁定实用程序?

示例:

/* don't block, call the function when a lock can be acquired */
lock(function (done) {
    /* can only enter here one at a time */
    done(); /* <-- unlocks */
}

或者,我是以错误的方式接近这个。有什么想法吗?

1 个答案:

答案 0 :(得分:0)

简单。 不要直接使用.findOne().save() .update() 数据库中的对象。

MongoDB和&#34;锁定&#34; (以这种方式)只是不会发生,这基本上是设计。你并不打算:

  1. 预订一本书
  2. 从书架上拿书
  3. 将新段落写入书籍
  4. 将书归还书架
  5. 其他人使用的非预约簿
  6. 这是可扩展模式中的一个非常大的 NO

    所以而不是这样做。

      

    &#34;在书架上书写新段落&#34;

    最好是

      

    &#34;在书架上书写新段落,其中当前段落是我期望的那样&#34;

    那应该是一个明确的&#34;类推。

    所以改为:

    User.update(
       { "user": 123 },
       { "$set": { "updated": new Date() } },
       function(err,numAffected) {
           // do something in callback
       }
    );
    

    简单地说&#34;&#34;只是更新当前&#34;更新&#34;在那里提出请求时的日期。

    或更好:

    var updatedDate = new Date();
    
    User.update(
       { "user": 123, "updated": { "$lt": updatedDate } },
       { "$set": { "updated": updatedDate } },
       function(err,numAffected) {
           // do something in callback
       }
    );
    

    未触及对象的是当前值&#34;更新&#34;是&#34;大于&#34;您要将其设置为的值。

    纯粹的逻辑。