Firestore事务产生控制台错误:FAILED_PRECONDITION:存储的版本与所需的基本版本不匹配 [英] Firestore transaction produces console error: FAILED_PRECONDITION: the stored version does not match the required base version

查看:105
本文介绍了Firestore事务产生控制台错误:FAILED_PRECONDITION:存储的版本与所需的基本版本不匹配的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我编写了一些代码,允许用户以与Reddit相似的方式对配方进行加注/减注.

I have written a bit of code that allows a user to upvote / downvote recipes in a manner similar to Reddit.

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

Each individual vote is stored in a Firestore collection named votes, with a structure like this:

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

{username,recipeId,value} (where value is either -1 or 1)

配方存储在recipes集合中,其结构如下所示: {title,username,ingredients,instructions,score}

The recipes are stored in the recipes collection, with a structure somewhat like this: {title,username,ingredients,instructions,score}

每次用户对食谱进行投票时,我都需要将他们的投票记录在投票集合中,并更新食谱的分数.我想将此操作作为使用事务的原子操作,因此这两个值永远不会失去同步.

Each time a user votes on a recipe, I need to record their vote in the votes collection, and update the score on the recipe. I want to do this as an atomic operation using a transaction, so there is no chance the two values can ever become out of sync.

以下是我到目前为止的代码.我正在使用Angular 6,但是找不到任何Typescript示例来说明如何在单个事务中处理多个gets(),因此我最终改编了我发现的一些基于Promise的JavaScript代码.

Following is the code I have so far. I am using Angular 6, however I couldn't find any Typescript examples showing how to handle multiple gets() in a single transaction, so I ended up adapting some Promise-based JavaScript code that I found.

该代码似乎可以正常工作,但是有些事情正在引起人们的关注.当我快速连续单击上投票"/下投票"按钮时,偶尔会出现一些控制台错误.这些读取POST https://firestore.googleapis.com/v1beta1/projects/myprojectname/databases/(default)/documents:commit 400 ().当我查看服务器的实际响应时,会看到以下内容:

The code seems to work, but there is something happening that is concerning. When I click the upvote/downvote buttons in rapid succession, some console errors occasionally appear. These read POST https://firestore.googleapis.com/v1beta1/projects/myprojectname/databases/(default)/documents:commit 400 (). When I look at the actual response from the server, I see this:

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

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

Note that the errors do not appear when I click the buttons slowly.

我应该担心此错误,还是只是重试事务的正常结果?如Firestore文档中所述,调用事务的函数(事务函数)可能会比如果并发编辑影响交易读取的文档,则一次."

Should I worry about this error, or is it just a normal result of the transaction retrying? As noted in the Firestore documentation, a "function calling a transaction (transaction function) might run more than once if a concurrent edit affects a document that the transaction reads."

请注意,我已尝试将try/catch块包装在下面的每个操作中,并且不会引发任何错误.为了使代码更易于遵循,我在发布之前将其删除.

Note that I have tried wrapping try/catch blocks around every single operation below, and there are no errors thrown. I removed them before posting for the sake of making the code easier to follow.

非常有兴趣听到任何有关改进我的代码的建议,无论它们是否与HTTP 400错误有关.

Very interested in hearing any suggestions for improving my code, regardless of whether they're related to the HTTP 400 error.

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);

  });

}

推荐答案

根据Firebase开发人员Nicolas Garnier ,您在这里所遇到的是事务在Firestore中的工作方式:其中一个事务未能写入,因为同时数据已更改,在这种情况下,Firestore再次重新运行了该事务,直到如果同时写入多个评论,则由于数据已更改,可能需要在第一次事务后再次运行其中一些评论.这是预期的行为,这些错误应更多地作为警告."

According to Firebase developer Nicolas Garnier, "What you are experiencing here is how Transactions work in Firestore: one of the transactions failed to write because the data has changed in the mean time, in this case Firestore re-runs the transaction again, until it succeeds. In the case of multiple Reviews being written at the same time some of them might need to be ran again after the first transaction because the data has changed. This is expected behavior and these errors should be taken more as warnings."

换句话说,这是事务重试的正常结果.

In other words, this is a normal result of the transaction retrying.

我使用RxJS油门时间,以防止用户通过快速连续单击upvote/downvote按钮来向事务淹没Firestore服务器,这大大减少了此400错误的发生.在我的应用中,没有正当的理由,有人需要每秒对upvote/downvote进行数十次剪辑.这不是电子游戏.

I used RxJS throttleTime to prevent the user from flooding the Firestore server with transactions by clicking the upvote/downvote buttons in rapid succession, and that greatly reduced the occurrences of this 400 error. In my app, there's no legitimate reason someone would need to clip upvote/downvote dozens of times per seconds. It's not a video game.

这篇关于Firestore事务产生控制台错误:FAILED_PRECONDITION:存储的版本与所需的基本版本不匹配的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆