【问题标题】:Firestore transaction produces console error: FAILED_PRECONDITION: the stored version does not match the required base versionFirestore 事务产生控制台错误:FAILED_PRECONDITION:存储的版本与所需的基本版本不匹配
【发布时间】:2018-09-30 23:31:24
【问题描述】:

我编写了一些代码,允许用户以类似于 Reddit 的方式对食谱进行投票/否决。

每个单独的投票都存储在名为 votes 的 Firestore 集合中,其结构如下:

{username,recipeId,value}(其中值为 -1 或 1)

配方存储在recipes 集合中,其结构有点像这样: {title,username,ingredients,instructions,score}

每次用户对菜谱投票时,我都需要在投票集合中记录他们的投票,并更新菜谱上的分数。我想将其作为使用事务的原子操作来执行,因此这两个值不可能不同步。

以下是我到目前为止的代码。我正在使用 Angular 6,但是我找不到任何 Typescript 示例来展示如何在单个事务中处理多个gets(),所以我最终调整了我找到的一些基于 Promise 的 JavaScript 代码。

代码似乎可以工作,但发生了一些令人担忧的事情。当我快速连续单击upvote/downvote 按钮时,偶尔会出现一些控制台错误。这些阅读POST https://firestore.googleapis.com/v1beta1/projects/myprojectname/databases/(default)/documents:commit 400 ()。当我查看来自服务器的实际响应时,我看到了:

{
  "error": {
    "code": 400,
    "message": "the stored version (1534122723779132) does not match the required base version (0)",
    "status": "FAILED_PRECONDITION"
  }
}

请注意,当我缓慢单击按钮时,错误不会出现。

我应该担心这个错误,还是只是事务重试的正常结果? 如 Firestore 文档中所述,“调用事务的函数(事务函数)可能会运行超过如果并发编辑影响事务读取的文档,则执行一次。”

请注意,我已经尝试将 try/catch 块包裹在下面的每个操作中,并且没有抛出任何错误。为了使代码更易于理解,我在发布之前删除了它们。

很想听听任何关于改进我的代码的建议,无论它们是否与 HTTP 400 错误有关。

async vote(username, recipeId, direction) {

  let value;

  if ( direction == 'up' ) {
    value = 1;
  }

  if ( direction == 'down' ) {
    value = -1;
  }

  // assemble vote object to be recorded in votes collection
  const voteObj: Vote = { username: username, recipeId: recipeId , value: value };

  // get references to both vote and recipe documents
  const voteDocRef = this.afs.doc(`votes/${username}_${recipeId}`).ref;
  const recipeDocRef = this.afs.doc('recipes/' + recipeId).ref;

  await this.afs.firestore.runTransaction( async t => {

    const voteDoc = await t.get(voteDocRef);
    const recipeDoc = await t.get(recipeDocRef);
    const currentRecipeScore = await recipeDoc.get('score');

    if (!voteDoc.exists) {

      // This is a new vote, so add it to the votes collection
      // and apply its value to the recipe's score
      t.set(voteDocRef, voteObj);
      t.update(recipeDocRef, { score: (currentRecipeScore + value) });

    } else {

      const voteData = voteDoc.data();

      if ( voteData.value == value ) {

        // existing vote is the same as the button that was pressed, so delete
        // the vote document and revert the vote from the recipe's score
        t.delete(voteDocRef);
        t.update(recipeDocRef, { score: (currentRecipeScore - value) });

      } else {

        // existing vote is the opposite of the one pressed, so update the
        // vote doc, then apply it to the recipe's score by doubling it.
        // For example, if the current score is 1 and the user reverses their
        // +1 vote by pressing -1, we apply -2 so the score will become -1.
        t.set(voteDocRef, voteObj);
        t.update(recipeDocRef, { score: (currentRecipeScore + (value*2))});
      }

    }

    return Promise.resolve(true);

  });

}

【问题讨论】:

    标签: promise transactions google-cloud-firestore angular-promise


    【解决方案1】:

    According to Firebase developer Nicolas Garnier, "您在这里遇到的是事务在 Firestore 中的工作方式:其中一个事务无法写入,因为同时数据已更改,在这种情况下,Firestore 会再次重新运行事务,直到它“ p>

    也就是说,这是事务重试的正常结果。

    我使用 RxJS throttleTime 来防止用户通过快速连续单击 upvote/downvote 按钮向 Firestore 服务器充斥事务,这大大减少了 400 错误的发生。在我的应用程序中,没有正当理由有人需要每秒剪辑数十次赞成/反对票。这不是电子游戏。

    【讨论】:

      猜你喜欢
      • 2020-11-02
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-11-24
      • 2017-08-03
      • 1970-01-01
      • 2012-01-26
      • 1970-01-01
      相关资源
      最近更新 更多