Firestore Transaction:如何编写可以作为事务发起方和非发起方的函数?



假设我有一个updateLog()函数,它执行以下操作:

只是一个伪:

const updateLog = (logData) => {
return db.runTransaction( t => {
return t.get(logRef)
.then(log => {
if (!log.exists) {
// blah blah blah
} else {
// blah blah blah
}
}
}
}

但我还有另一个函数,可以是事务的发起者updateLog()函数只能是事务的部分

const createDoc = (docData) => {
return db.runTransaction( t => {
t.set(docRef, docData);
// Here, the updateLog() can only be part of the transaction.
return updateLog(docData, t???)
.then(result => {
if (!result) {
// blah blah blah
} else {
// blah blah blah
}
}
}
}

问题:因此,在我的updateLog()中,我是否可以接受transaction作为可选参数,同时保留其作为独立事务发起方的能力?因此,我如何为updateLog()创建一个可选参数,以使runTransactionOPTIONALLY接受它作为事务的一部分?我之所以要这样做,是因为我想避免复制CCD_;"代理";或";路由器";管理事务的可用性。任何建议都将不胜感激,谢谢!

const updateLog = (logData, transaction = null) => {
// How do I pass transaction as t in this context?
// Possible?
return db.runTransaction( t => {
return t.get(logRef)
.then(log => {
if (!log.exists) {
// blah blah blah
} else {
// blah blah blah
}
}
}
}

在等待了一个多星期的答案后,显然我们不可能有条件地await firestore.runTransaction()如下:

const updateLog = async (logData, transaction = null) => {
if (!transaction) {
transaction = await firestore.runTransaction();
};
// Start manipulating data here.
}

您不能等待事务完成并在runTransaction外部执行数据操作任务——这完全违背了事务的目的

解决方案:预测不可预测的情况

提供独立和部分事务灵活性的最佳方式是在任何直接数据操作函数中不要使用runTransaction,而是使用最外层的调用者,例如Firebase函数:

数据层.js

const updateLog = (logData, transaction) => {
if (!transaction || transaction instanceof admin.firestore.Transaction === false) {
throw new Error(`Invalid argument: transaction. It must be an instance of Firestore Transaction.`);
};
// Assuming logRef has been created here. 
return transaction.get(logRef)
.then(log => {
if (!log.exists) {
// blah blah blah
} else {
// blah blah blah
}
}
}
const createDoc = (docData, transaction) => {
if (!transaction || transaction instanceof admin.firestore.Transaction === false) {
throw new Error(`Invalid argument: transaction. It must be an instance of Firestore Transaction.`);
};
// Assuming docRef has been created here. 
transaction.set(docRef, docData);
// Here, the updateLog() can be part of the transaction.
return updateLog(docData, transaction)
.then(result => {
if (!result) {
// blah blah blah
} else {
// blah blah blah
}
}
}

Firebase函数

"use strict";
const admin = require("firebase-admin");
exports.createDoc = functions.https.onCall((data, context) => {
const firestore = admin.firestore();
// Transaction will only be initiated as needed
// by the caller.
return firestore.runTransaction(transaction => {
return createDoc(data.docData, transaction)
.then(result => {
// Log has also been updated when running createDoc()
return result;
}
}.catch(error => {
throw new functions.https.HttpsError("failed-precondition", `${error.message}`);
};
});
exports.updateLog = functions.https.onCall((data, context) => {
const firestore = admin.firestore();
// The updateLog() now can also be standalone.
return firestore.runTransaction(transaction => {
return updateLog(data.logData, transaction)
.then(result => {
// Log has been updated.
return result;
}
}.catch(error => {
throw new functions.https.HttpsError("failed-precondition", `${error.message}`);
};
});

结论:

让每个较小的数据服务功能接受transaction参数并仅在它们被打包为一个完整任务时启动runTransaction是一种很好的做法。这应该是相对安全的,尤其是当首先验证所需的transaction参数,同时能够在使用数据服务时提供更大的灵活性时。

最新更新