在MongoDB中使用nonce进行并发编辑

时间:2014-12-20 21:38:46

标签: javascript node.js mongodb concurrency mongoose

我正在使用Expressjs,Socket.io和Mongoose制作多人游戏。玩家加入游戏,并在有足够的玩家玩游戏时开始游戏。我在游戏启动的同时警告游戏中的所有玩家,所以我遇到了将游戏状态改为“玩”并初始化角色的并发问题。有时候角色会被分配两次,这会让游戏变得混乱。

我尝试按照推荐的herehere添加一个nonce,但我无法让它工作,我真的不明白它。 MongoDB是否将其视为具有特殊属性的特殊值?或者是nonce取决于MongoDB的一些现有属性?

以下是游戏模型的相关部分:

GameSchema = mongoose.Schema
  nonce:
    type: mongoose.Schema.Types.ObjectId
    required: true
    default: mongoose.Types.ObjectId
  name:
    type: String
    required: true
    unique: true
  state:
    type: String
    enum: ["setup", "unstarted", "playing", "assassinating", "good_won", "bad_won", "discontinued"]
    default: "setup"
    required: true
  characters: [String]
  players: [{ type: mongoose.Schema.Types.ObjectId, ref: "Player" }]

GameSchema.statics.findByIdAndStart = (id, done) ->
  Game = this

  Game.findById id, (err, game) ->
    return done err, game if err or game.state is "playing"

    changes =
      state: "playing"
      nonce: mongoose.Types.ObjectId()
    Game.update { id: id, nonce: game.nonce }, changes, (err) ->
      return done err if err

      Game.findById(id).populate("players").exec (err, game) ->
        return done err if err

        characterSelection = new CharacterSelection game.characters

        async.eachSeries game.players, ((player, next) ->
          player.character = characterSelection.assignCharacter()
          player.save next
        ), (err) -> done err, game

如何防止同时编辑模型?

1 个答案:

答案 0 :(得分:2)

将模型的属性命名为#34; nonce"没有什么特别之处。它只是一个术语/惯例含义"只使用一次"我们要求它在每次生成时都是唯一的。

您将nonce定义为MongoDB中的ObjectId,以便每次生成它时都应获得唯一值。

这项工作的关键是当你调用update()时,你使用doc的现有nonce值,这样你的更新可以确保在你发出findById()和发出findById()之间没有其他玩家或用户更新了doc。调用更新方法。如果更新成功,您的文档将获得您在更改对象中添加的新生成的nonce值。

查看您的代码我认为它看起来是正确的,除非您没有处理更新没有更改任何记录的情况,只有错误情况。更新回调(in Mongoose 3.8.x)采用第二个参数numberAffected,您可以使用该参数确定是否存在受影响的文档。

Game.update { id: id, nonce: game.nonce }, changes, (err, numberAffected) ->
  return done err if err
  return done "Conflict!" if !numberAffected

您需要决定如何处理冲突案例,在this example中,代码会在错误输出之前多次重试该调用。