在使用一个 transaction.get 后,在交易中阅读额外的文档
Read an additional document in a transaction after using one transaction.get already
我对 Firebase 和 NoSQL 数据库还很陌生,我正在开发服务器端。
我的目标是在一个事务中读取两个集合(并锁定包含的文档以保持一致性)。一方面,我想从集合 A 中读取最多 499 个文档,另一方面,我想从集合 B 中读取一个文档,如以下代码示例所示。
export const transactionTest = function(nextFunction: () => void) {
const collection_A_reference = admin.firestore().collection("A").limit(499);
const collection_B_doc_B1_reference = admin.firestore().collection("B").doc("B1");
try {
admin.firestore().runTransaction((t) => {
return t.get(collection_A_reference)
.then((coll_A_snapshot) => {
if (!(coll_A_snapshot.empty)) {
t.get(collection_B_doc_B1_reference)
.then((coll_B_doc_B1_snapshot) => {
if (coll_B_doc_B1_snapshot.exists) {
let counter = coll_B_doc_B1_snapshot.get("COUNTER");
for (let i = 0; i < coll_A_snapshot.docs.length; i++) {
counter++;
t.update(coll_A_snapshot.docs[i].ref, {COUNTER: counter});
}
t.update(coll_B_doc_B1_snapshot.ref, {COUNTER: counter});
} else {
console.log("coll_B_doc_B1_snapshot does not exist");
}
});
} else {
console.log("coll_A_snapshot is empty");
}
});
});
} catch (e) {
console.log("Transaction failure:", e);
nextFunction();
}
nextFunction();
};
但是,第二个 t.get 似乎是不允许的 并抛出以下错误:
(node:13460) UnhandledPromiseRejectionWarning: Error: 10 ABORTED: The referenced transaction has expired or is no longer valid
有人知道如何实现这个(尤其是语法上)吗?我在谷歌上搜索了很多,但没有找到我想要的东西。也许我在这里推理如何在 firebase 中使用事务时也有错误。一种解决方法可能是在事务之前创建一个 DocumentReference
数组,然后使用它 transaction.getAll()
但这看起来不是很优雅。
如有任何帮助,我将不胜感激:)
此致
问题不在于进行了多次 Transaction#get()
调用,而是您没有正确链接承诺。这会导致事务完成(不执行任何操作)并且您会收到“引用的事务已过期”错误消息。
admin.firestore().runTransaction((t) => {
return t.get(collection_A_reference)
.then((coll_A_snapshot) => {
if (!(coll_A_snapshot.empty)) {
return t.get(collection_B_doc_B1_reference) // <-- was missing this return
.then((coll_B_doc_B1_snapshot) => {
if (coll_B_doc_B1_snapshot.exists) {
let counter = coll_B_doc_B1_snapshot.get("COUNTER");
for (let i = 0; i < coll_A_snapshot.docs.length; i++) {
counter++;
t.update(coll_A_snapshot.docs[i].ref, {COUNTER: counter});
}
t.update(coll_B_doc_B1_snapshot.ref, {COUNTER: counter});
} else {
console.log("coll_B_doc_B1_snapshot does not exist");
}
});
} else {
console.log("coll_A_snapshot is empty");
}
});
});
备注:
- 交易主体(
(t) => { /* ... */ }
内的任何内容)可能会被重试多次。所以在做日志记录之类的事情时要小心,因为你最终可能会得到很多包含被忽略数据的日志。相反,您应该 return 有关结果的信息。如果需要重试事务,则丢弃该对象并替换为下一次尝试的结果。当交易成功时,最新的对象被传递回调用者,然后基于该对象进行日志记录。交易中唯一有用的日志是跟踪尝试以及他们花费的时间。
- 除非您将
await
与 Promise 一起使用,否则用 try
/catch
块包围它是没有意义的。
try {
await admin.firestore().runTransaction((t) => { /* ... */ });
} catch (e) {
console.log("Transaction failure:", e);
nextFunction();
}
就重构而言,您可以选择:
const transactionResult = await admin.firestore()
.runTransaction(async (t) => {
const coll_B_doc_B1_snapshot = await t.get(collection_B_doc_B1_reference); // check B1 first (only reading 1 document instead of up to 499)
if (!coll_B_doc_B1_snapshot.exists)
return { aborted: true, type: "missing-doc" };
const coll_A_snapshot = await t.get(collection_A_reference);
if (coll_A_snapshot.empty)
return { aborted: true, type: "empty" };
// queue changes
let counter = coll_B_doc_B1_snapshot.get("COUNTER");
for (let i = 0; i < coll_A_snapshot.docs.length; i++) {
counter++; // <- is this meant to increment on every loop?
t.update(coll_A_snapshot.docs[i].ref, {COUNTER: counter});
}
t.update(coll_B_doc_B1_snapshot.ref, {COUNTER: counter});
return { aborted: false, counter };
})
.catch(error => ({ aborted: true, type: "error", error }));
if (transactionResult.aborted) {
if (transactionResult.type === "empty") {
console.error(`Update failed, because coll_A is empty`);
} else if (transactionResult.type === "missing-doc") {
console.error(`Update failed, because coll_B_doc_B1 is missing`);
} else {
console.error("Update failed, because of ${transactionResult.type}", transactionResult.error);
}
nextFunction();
return;
}
console.log(`Counter was successfully updated. B1 now has a COUNTER of ${transactionResult.counter}`);
nextFunction();
我对 Firebase 和 NoSQL 数据库还很陌生,我正在开发服务器端。
我的目标是在一个事务中读取两个集合(并锁定包含的文档以保持一致性)。一方面,我想从集合 A 中读取最多 499 个文档,另一方面,我想从集合 B 中读取一个文档,如以下代码示例所示。
export const transactionTest = function(nextFunction: () => void) {
const collection_A_reference = admin.firestore().collection("A").limit(499);
const collection_B_doc_B1_reference = admin.firestore().collection("B").doc("B1");
try {
admin.firestore().runTransaction((t) => {
return t.get(collection_A_reference)
.then((coll_A_snapshot) => {
if (!(coll_A_snapshot.empty)) {
t.get(collection_B_doc_B1_reference)
.then((coll_B_doc_B1_snapshot) => {
if (coll_B_doc_B1_snapshot.exists) {
let counter = coll_B_doc_B1_snapshot.get("COUNTER");
for (let i = 0; i < coll_A_snapshot.docs.length; i++) {
counter++;
t.update(coll_A_snapshot.docs[i].ref, {COUNTER: counter});
}
t.update(coll_B_doc_B1_snapshot.ref, {COUNTER: counter});
} else {
console.log("coll_B_doc_B1_snapshot does not exist");
}
});
} else {
console.log("coll_A_snapshot is empty");
}
});
});
} catch (e) {
console.log("Transaction failure:", e);
nextFunction();
}
nextFunction();
};
但是,第二个 t.get 似乎是不允许的 并抛出以下错误:
(node:13460) UnhandledPromiseRejectionWarning: Error: 10 ABORTED: The referenced transaction has expired or is no longer valid
有人知道如何实现这个(尤其是语法上)吗?我在谷歌上搜索了很多,但没有找到我想要的东西。也许我在这里推理如何在 firebase 中使用事务时也有错误。一种解决方法可能是在事务之前创建一个 DocumentReference
数组,然后使用它 transaction.getAll()
但这看起来不是很优雅。
如有任何帮助,我将不胜感激:)
此致
问题不在于进行了多次 Transaction#get()
调用,而是您没有正确链接承诺。这会导致事务完成(不执行任何操作)并且您会收到“引用的事务已过期”错误消息。
admin.firestore().runTransaction((t) => {
return t.get(collection_A_reference)
.then((coll_A_snapshot) => {
if (!(coll_A_snapshot.empty)) {
return t.get(collection_B_doc_B1_reference) // <-- was missing this return
.then((coll_B_doc_B1_snapshot) => {
if (coll_B_doc_B1_snapshot.exists) {
let counter = coll_B_doc_B1_snapshot.get("COUNTER");
for (let i = 0; i < coll_A_snapshot.docs.length; i++) {
counter++;
t.update(coll_A_snapshot.docs[i].ref, {COUNTER: counter});
}
t.update(coll_B_doc_B1_snapshot.ref, {COUNTER: counter});
} else {
console.log("coll_B_doc_B1_snapshot does not exist");
}
});
} else {
console.log("coll_A_snapshot is empty");
}
});
});
备注:
- 交易主体(
(t) => { /* ... */ }
内的任何内容)可能会被重试多次。所以在做日志记录之类的事情时要小心,因为你最终可能会得到很多包含被忽略数据的日志。相反,您应该 return 有关结果的信息。如果需要重试事务,则丢弃该对象并替换为下一次尝试的结果。当交易成功时,最新的对象被传递回调用者,然后基于该对象进行日志记录。交易中唯一有用的日志是跟踪尝试以及他们花费的时间。 - 除非您将
await
与 Promise 一起使用,否则用try
/catch
块包围它是没有意义的。
try {
await admin.firestore().runTransaction((t) => { /* ... */ });
} catch (e) {
console.log("Transaction failure:", e);
nextFunction();
}
就重构而言,您可以选择:
const transactionResult = await admin.firestore()
.runTransaction(async (t) => {
const coll_B_doc_B1_snapshot = await t.get(collection_B_doc_B1_reference); // check B1 first (only reading 1 document instead of up to 499)
if (!coll_B_doc_B1_snapshot.exists)
return { aborted: true, type: "missing-doc" };
const coll_A_snapshot = await t.get(collection_A_reference);
if (coll_A_snapshot.empty)
return { aborted: true, type: "empty" };
// queue changes
let counter = coll_B_doc_B1_snapshot.get("COUNTER");
for (let i = 0; i < coll_A_snapshot.docs.length; i++) {
counter++; // <- is this meant to increment on every loop?
t.update(coll_A_snapshot.docs[i].ref, {COUNTER: counter});
}
t.update(coll_B_doc_B1_snapshot.ref, {COUNTER: counter});
return { aborted: false, counter };
})
.catch(error => ({ aborted: true, type: "error", error }));
if (transactionResult.aborted) {
if (transactionResult.type === "empty") {
console.error(`Update failed, because coll_A is empty`);
} else if (transactionResult.type === "missing-doc") {
console.error(`Update failed, because coll_B_doc_B1 is missing`);
} else {
console.error("Update failed, because of ${transactionResult.type}", transactionResult.error);
}
nextFunction();
return;
}
console.log(`Counter was successfully updated. B1 now has a COUNTER of ${transactionResult.counter}`);
nextFunction();