这个错误是什么意思?
特别是,他们是什么意思:* * 请重试**
这是否意味着交易失败,我必须手动重新运行交易?从我对文件的理解来看,
事务读取在事务外部修改的文档。在这种情况下,事务自动再次运行。事务被重试有限次数。
如果是,哪些文件?该错误并没有指出它正在谈论的是哪个文件。我得到这个堆栈:
{错误:10中止:关于这些文件的争论太多了。请重试.在Object。出口。错误状态Errornode_modules\grpc\src\common。js:八十七:15)在ClientReadableStream。_emitStatusIfDone\node_modules\grpc\src\client。js:二百三十五:26)在ClientReadableStream。_receiveStatus\node_modules\grpc\src\client。js:二百一十三:(8)对象。onReceiveStatus\node_modules\grpc\src\client_interceptors。js:1256:(15)拦截。_callNext node_modules\grpc\src\client_interceptors。js:第五百六十四章:42)在拦截。onReceiveStatus\node_modules\grpc\src\client_interceptors。js:第六百一十四章:8)在C:会话信息:SESSION_ID = tlgggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggjs:1019:24码:10、元数据:元数据{_internal_repr:{}},详情:“对这些文件的争论太多了。请重试。
要重新创建此错误,只需按照documentation上的指示,在db. runTransaction方法上运行for循环
7条答案
按热度按时间i5desfxk1#
我们在Firebase Firestore数据库中遇到了同样的问题。即使是少于30个项目的小柜台也会遇到这个问题。
我们的解决方案不是分发计数器,而是增加事务的尝试次数,并为这些重试添加延迟时间。
第一步是保存transaction作为const,可以传递给另一个函数。
第二步是创建两个helper函数。一个用于等待特定的时间,另一个用于运行事务并捕获错误。如果发生代码为10的中止错误,我们只需再次运行事务并重试特定次数。
现在我们已经拥有了我们需要的一切,我们可以使用
await
调用我们的助手函数,我们的事务调用将比默认的运行时间更长,并且它将延迟时间。我喜欢这个解决方案的原因是,它并不意味着更多或复杂的代码,而且大多数已经编写的代码可以保持原样。只有当计数器达到必须计数更多项的程度时,它才会使用更多的时间和资源。确保时间和资源与默认事务相同。
对于大量的项目,我们可以增加重试次数或等待时间。这两个因素也影响了Firebase的成本。对于等待部分,我们还需要增加函数的超时。
**我还没有用成千上万或更多的项目对这段代码进行压力测试。在我们的具体情况下,问题开始与20+项目,我们需要多达50个项目的任务。我用200个项目测试了一下,问题没有再出现。
toe950272#
如果需要,事务确实会运行几次,但是如果在写入之前继续更新读取的值,它最终会失败,因此记录该事务的文档会重试有限次。如果你有一个像计数器一样经常更新的值,可以考虑其他的解决方案,比如distributed counters。如果你想要更具体的建议,我建议你在问题中包括你的交易代码和一些关于你试图实现的信息。
toe950273#
Firestore只重新运行事务有限的次数。截至撰写本文时,此数字已硬编码为5,并且无法更改。当许多用户使用同一文档时,为了避免拥塞/争用,通常我们使用指数退避算法(但这将导致事务需要更长的时间才能完成,这在某些用例中可能是可以接受的)。
然而,在撰写本文时,这还没有在Firebase SDK中实现-事务会立即重试。幸运的是,我们可以在事务中实现自己的指数退避算法:
可以这样使用:
**注意:**上述示例可能会导致您的交易需要1.5分钟才能完成。这对我的用例来说很好。您可能需要根据您的用例调整回退算法。
k2arahey4#
我已经实现了一个简单的回退解决方案来分享:维护一个全局变量,为每个失败的连接分配一个不同的“重试槽”。例如,如果5个连接同时出现,其中4个出现争用错误,则每个连接将获得500 ms、1000 ms、1500 ms、2000 ms的延迟,直到再次尝试。因此,它可能会同时解决,而不会再有任何争用。
我的事务是调用Firebase Functions的响应。每个Functions计算机示例都可以有一个全局变量
nextRetrySlot
,该变量将一直保留到关闭。因此,如果error.code === 10
因争用问题而被捕获,则延迟时间可以是(nextRetrySlot + 1) * 500
,然后您可以例如nextRetrySlot = (nextRetrySlot + 1) % 10
,因此下一个连接将获得500 ms ~ 5000 ms范围内的不同时间轮询。以下是一些基准:
我的情况是,我希望每个新的Firebase Auth注册都能从唯一的Firebase UID获得一个更短的ID,因此它有冲突的风险。
我的解决方案是简单地检查所有注册的短ID,如果查询返回了什么,就生成另一个,直到它不是。然后我们将这个新的短ID注册到数据库。因此,该算法不能只依赖于Firebase UID,但它能够以确定性的方式“移动到下一个UID”。(不只是随机再次)。
这是我的事务,它首先读取所有使用过的短ID的数据库,然后原子地写入一个新的,以防止发生极不可能的事件,即2个新寄存器同时出现,具有不同的Firebase UID,派生为相同的短ID,并且都看到短ID同时为空。
我运行了一个测试,故意注册了20个不同的Firebase UID,它们都派生为相同的短ID。(极不可能的情况)所有这些都在同一时间爆发。首先,我尝试在下次重试时使用相同的延迟,所以我希望它在慢慢解决某些连接时会一次又一次地相互冲突。
然后,利用时隙中的分布式延迟时间:
说明不同的延迟时间肯定有帮助。
b1payxdu5#
在runTransaction code中找到
maxAttempts
,它应该修改5个默认尝试(但尚未测试)。无论如何,我认为随机等待(最终加上队列)仍然是更好的选择。
mspsb9vt6#
Firestore现在支持服务器端
increment()
和decrement()
原子操作。您可以增加或减少任何数量。请参阅blog post以了解详细信息。在许多情况下,这将消除对客户端事务的需要。
范例:
这仍然限于每个文档1 QPS的持续写入限制,因此如果您需要更高的吞吐量,请考虑使用distributed counters。这将增加读取成本(因为您需要读取所有分片文档并计算总数),但允许您通过增加分片数量来扩展吞吐量。现在,如果您确实需要将计数器作为事务的一部分进行递增,那么由于更新争用而导致失败的可能性要小得多。
ulmd4ohb7#
解析以下函数:
这将在遇到争用错误时自动无限重试事务,并具有回退+抖动延迟
要使用它,只需将现有的
await db.runTransaction(async (transaction) => {
与
await retryingTransaction(async (transaction) => {