您如何避免使用 firebase 云功能可能出现的竞争情况?
Posted
技术标签:
【中文标题】您如何避免使用 firebase 云功能可能出现的竞争情况?【英文标题】:How do you avoid a possible race condition with firebase cloud functions? 【发布时间】:2018-03-15 01:58:47 【问题描述】:我有一个由 Firestore 数据库写入触发的云功能。它执行可能需要很长时间的异步操作(从某些 3rd 方 API 获取数据),也可能不会。完成后,它将结果写入“搜索结果”字段。
可能存在竞争条件,即来自较新触发器的结果被稍后完成的较旧操作覆盖。我应该如何在 Firebase 云函数和 Firestore 的上下文中解决这个问题?
【问题讨论】:
如果您知道在第二次(或第三次或第四次)Firestore 文档写入期间当前正在发生对第三方服务的请求,您打算怎么做? 取消它,如果没有忽略并取消订阅。 取消旧请求,我的意思是。不取消新的数据库写入。 函数的两次调用彼此一无所知。它们甚至可能不在同一个服务器实例上运行。如果没有一些相当深入的工作来尝试在两者之间建立一些通信,一个调用不会影响另一个调用,可能使用 Firestore 本身。 通常,您会通过确保操作是幂等的来做到这一点,这意味着多次执行相同的操作是无用的。但是,如何做到这一点取决于您的具体用例。 【参考方案1】:这里一般有两种方法:
-
确保您的操作是幂等的
确保您的操作检测到冲突更新并重试
确保您的操作是幂等的
这通常是最可扩展且架构最简单的。当您对相同的输入执行相同的幂等操作时,它具有相同的结果。这意味着如果多次执行该操作并不重要,因为结果将是相同的。
Firestore documentation on arrays and sets 就是一个很好的例子。想象一下,您正在用类别标记博客文章。一个简单的模型是:
title: "My great post",
categories: [
"technology",
"opinion",
"cats"
]
但现在想象一下,两个用户几乎同时将同一个帖子标记为关于猫的帖子。你最终可能会得到 p>
title: "My great post",
categories: [
"technology",
"opinion",
"cats",
"cats"
]
这显然不是你想要的。但是由于数据结构允许,这可能会发生。这里理想的解决方案是使用一种使这成为不可能的数据结构:添加cat
是幂等操作的数据结构。用数学术语来说,这将是一个集合,在 Firestore 中,您可以将其建模为:
title: "My great post",
categories:
"technology": true,
"opinion": true,
"cats": true
现在在这种结构中,无论您将cats
设置为true
的频率如何,结果都将始终相同。
确保您的操作检测到冲突更新并重试
有时不可能(或不可行)使您的操作具有幂等性。在这种情况下,您还可以考虑使用比较和设置策略。
例如,假设第 3 方 API 以某种方式更改数据,并且您只想在数据库中的原始数据未修改的情况下将结果写回数据库。在这种情况下,您需要在函数中执行以下步骤:
-
读取原始数据
使用数据调用第 3 方 API
等待结果
开始交易
重新加载原始数据
如果数据被修改,返回#2
如果数据未修改,则从第 3 方 API 写入结果
这种类型的比较和设置操作实际上是 Firebase 的实时数据库实现事务的方式,“第 3 方 API”是您的应用程序事务处理程序。
正如您可能看到的,第二种方法比具有幂等操作的方法更复杂。所以如果可能的话,我总是推荐这种方法。
【讨论】:
非常感谢!我必须对此做一些深入的思考。 好主意。上述解决方案的问题是,如果我想使用array-contains
(Firestore 查询运算符),那么我不能使用上述解决方案,因为它只适用于数组而不是对象
Firebase 现在支持用于幂等数组操作的 arrayUnion 和 arrayRemove。见:firebase.googleblog.com/2018/08/…【参考方案2】:
虽然不是直接回答 OP 关于等待第 3 方响应的问题,但对于那些从 Google 搜索到通用 Firestore 竞争条件解决方案的人来说,Firestore 支持事务和批量写入:
Cloud Firestore 支持读取和写入数据的原子操作。在一组原子操作中,要么所有操作都成功,要么一个都不应用。 Cloud Firestore 中有两种原子操作:
事务:事务是对一个或多个文档的一组读写操作。
批处理写入:批处理写入是对一个或多个文档的一组写入操作。
更多详情请见:https://firebase.google.com/docs/firestore/manage-data/transactions
【讨论】:
【参考方案3】:Firebase 云函数提供了 transact() 函数,可以用来将一些数据原子地保存到数据库中。
【讨论】:
以上是关于您如何避免使用 firebase 云功能可能出现的竞争情况?的主要内容,如果未能解决你的问题,请参考以下文章