2017-05-25 58 views
0

我有这个功能。不允许接受服务: 仅在可用参数为真时才被采用。mongodb:只更新文件如果没有更新

function takeService(req, res) { 
    var serviceId = req.params.id; 
    var driverId = req.body.driverId; 

    Service.findById(serviceId, (err, service) =>{ 
    if (!err) { 
     if (!service) { 
     res.status(404).send({message: 'Not found'}); 
     } else { 
     if (service.available === false) { 
      res.status(409).send({message: 'The service is taken'}); 
     } else { 
      Service.findByIdAndUpdate(serviceId, { 
      driverId, 
      status: 1, 
      available: false 
      }, (err, serviceUpdated) =>{ 
      if (!err && serviceUpdated) { 
       res.status(200).send({message: "tomado"}); 
      } 
      }); 
     } 
     } 
    } 
    }); 
} 

架构:

var ServiceSchema = Schema({ 
    clientId: { 
    type: String, 
    ref: 'Client' 
    }, 
    available: Boolean, 
    routeId: { 
    type: String, 
    ref: 'Route' 
    }, 
    date: Date, 
    radius: Number, 
    driverId: { 
    type: String, 
    ref: 'Driver' 
    }, 
    status: Number, 
    time: String, 
    createdTime: Number, 
    rateId: { 
    type: String, 
    ref: 'Rate' 
    } 
}); 

var DriverSchema = Schema({ 
    name: String, 
    surname: String, 
    username: String, 
    password: String, 
    status: { type: Number, default: 0 }, 
    oneSignalId: String, 
    plate: String, 
    make: String, 
    year: String, 
    model: String, 
    groupId: [{ 
    type: String, 
    ref: 'DriverGroup' 
    }], 
    unit: String, 
    telephone: String 
}); 

问题是当两个设备调用这个函数,在某些情况下都找到该文件,并检查是否可用,然后更新都在同一个文档。我正在为自动检查此属性的架构中寻找一些验证。

+0

你可以改变'findByIdAndUpdate',或使一个新的'findAvailableByIdAndUpdate'包括' “可用”:TRUE'在蒙戈查询。如果它没有更新任何文件,其他的东西就会赢得比赛。 – Joe

回答

0

如果我正确理解问题,主要问题是两个设备可能认为服务仍然可用。

最终的原因是findByIdfindByIdAndUpdate之间存在竞态条件:在这两个调用之间,存在另一个请求可以更改数据库中文档的时间窗口。

要解决这个问题,您可以使用原子findAndModify命令,其中Mongoose作为(其他)公开的Model#findOneAndUpdate

您的代码将成为像这样:

function takeService(req, res) { 
    var serviceId = req.params.id; 
    var driverId = req.body.driverId; 

    Service.findOneAndUpdate({ 
    _id  : serviceId, 
    available : true 
    }, { 
    driverId : driverId, 
    status : 1, 
    available : false, 
    }, (err, service) => { 
    if (err) { 
     return res.status(500); 
    } else if (! service) { 
     return res.status(409).send({message: 'The service is taken'}); 
    } else { 
     return res.status(200).send({message: "tomado"}); 
    } 
    }); 
} 

有与你原来的代码有一些差别,你应该知道的:

  • 你不能服务不区分现有(无效/未知serviceId)以及不再可用的服务;在这两种情况下,更新都不会产生任何结果,并返回409响应;
  • findOneAndUpdate将在更新之前返回旧的文档。如果您希望收到更新的文档,通过new选项查询:

    Service.findOneAndUpdate({ ... }, { ... }, { new : true }, (err, service) => { ... }) 
    
  • 我在里面添加了一个错误处理程序,发送回一个500(“内部服务器错误”)响应。