【发布时间】:2020-08-31 17:42:21
【问题描述】:
更新:
这是我收到的console.error(err) 消息:
Error: 6 ALREADY_EXISTS: Document already exists: projects/projectOne-e3999/databases/(default)/documents/storage/AAAAAAAAAA1111111111
我明白为什么会出现错误。因为并行运行的事务更快地执行事务的else 部分。因此,返回错误的事务不必创建而是更新文档。如果事务重新运行就足够了。但它并没有声明Transactions are committed once 'updateFunction' resolves and attempted up to five times on failure。
这就是问题的重点。当文档已经存在时,如何防止事务失败,因为它是并行创建的,或者如何重新运行事务?
原问题:
我有一个云功能,可以在创建另一个集合中的文档时更新另一个文档。由于可以并行创建多个文档,所有文档都访问同一个文档,这将作为事务运行,因此保证了幂等性。
函数的结构如下所示。问题是第一次写入(因此创建存储文档)。正如我所说,可以并行创建两个文档。但是现在事务读取了一个不存在的文档并尝试创建它,但是由于另一个事务(并行运行的事务)已经创建了该文档,所以事务失败了。 (首先我不明白,因为我认为事务会在执行时阻止所有访问)
这根本不是问题,但它不会重试(尽管它声明它将自动重试多达 5 次)。我认为这与我的异步函数的 try & catch 块有关。
如果它会重试,它将检测到文档存在并自动更新现有的。
让我们简单地说。此事务绝不能失败。如果没有可能的自动恢复方式,它将导致数据库不一致。
.onCreate(async (snapshot, context) => {
//Handle idempotence
const eventId = context.eventId;
try {
const process = await shouldProcess(eventId)
if (!process) {
return null
}
const storageDoc = admin.firestore().doc(`storage/${snapshot.id}`)
await admin.firestore().runTransaction(async t => {
const storageDbDoc = await storageDoc.get()
const dataDb = storageDbDoc.data()
if (dataDb) {
//For sake of testing just rewrite it
t.update(storageDoc, dataDb )
} else {
//Create new
const storage = createStorageDoc()
t.create(storageDoc, storage )
}
})
return markProcessed(eventId)
} catch (err) {
console.error(`Execution of ${eventId} failed: ${err}`)
throw new Error(`Transaction failed.`);
}
【问题讨论】:
-
请编辑问题以显示有关失败的更多详细信息,而不仅仅是“交易失败”。您应该完整地记录错误对象,而不仅仅是字符串表示。
console.error(err). -
基本点:事务根本不会阻塞任何东西。他们确实检查在事务处理期间读取的任何文档是否已更改。如果读取的任何文档已 更改,则结果未 提交并重新尝试整个事务。最关键的是:没有任何东西被锁定,没有任何东西被阻止。 firebase.google.com/docs/reference/js/…
-
@DougStevenson 我用日志更新了问题,也许现在的问题更清楚了。
-
@LeadDreamer 好的,这就解释了为什么我会收到错误消息。现在的问题是:如何防止这种情况发生。我真的只需要一个交易(有史以来第一个)创建文档。所有其他应该只更新现有文档。 (即使有多个事务并行运行)
标签: javascript node.js google-cloud-firestore transactions