Посмотрев на ваш код, мы увидим несколько его частей, которые могут привести к неверным результатам. Без тщательного тестирования и воспроизведения вашей проблемы невозможно на 100% быть уверенным, что их исправление полностью решит вашу проблему, но, скорее всего, это является причиной проблем.
Облачная функция HTTP:
С forEach()
l oop вы вызываете несколько асинхронных операций (метод update()
), но вы не ждете, что все эти асинхронные операции завершены, прежде чем отправить ответ обратно. Вы должны сделать следующее, используя Promise.all()
, чтобы дождаться завершения всех асинхронных методов перед отправкой ответа:
exports.updateAccounts = functions.https.onRequest((request, response) => {
const promises = [];
admin.firestore().collection('accounts').get()
.then(accounts => {
accounts.forEach((account) => {
promises.push(admin.firestore().collection('accounts').doc(account.id).update({balance: WHATEVER}));
return Promise.all(promises);
})
.then(() => {
response.send("Done");
})
.catch(error => {....});
});
onUpdate
Фоновая функция облачного запуска
Там вам нужно правильно вернуть цепочку Promises, чтобы указывать платформе, когда облачная функция завершена. Следующее должно сделать трюк:
exports.updateAccount = functions.firestore
.document('accounts/{accountID}')
.onUpdate((change, context) => {
const userID = change.after.data().userID;
return admin.firestore().doc("users/"+userID).get() //Note the return here. (Note that in the HTTP Cloud Function we don't need it! see the link to the video series below)
.then(user => {
const new_balance = change.after.data().balance;
const old_balance = change.before.data().balance;
var user_balance = user.data().balance + new_balance - old_balance;
return admin.firestore().doc("users/"+userID).update({balance: user_balance}); //Note the return here.
});
});
Я бы посоветовал вам посмотреть 3 видео о "JavaScript Promises" из серии видеороликов Firebase: https://firebase.google.com/docs/functions/video-series/ , Они объясняют все ключевые моменты, которые были исправлены выше.
На первый взгляд кажется, что если вы измените в updateAccounts
облачной функции несколько account
документов, которые имеют один и тот же user
вам действительно потребуется реализовать обновление баланса пользователя в транзакции, поскольку несколько экземпляров updateAccount
Cloud Function могут запускаться параллельно. Do c для Транзакций - здесь .
Обновление: Вы можете реализовать транзакцию следующим образом в updateAccounts
Облачной функции (не проверено):
exports.updateAccount = functions.firestore
.document('accounts/{accountID}')
.onUpdate((change, context) => {
const userID = change.after.data().userID;
const userRef = admin.firestore().doc("users/" + userID);
return admin.firestore().runTransaction(transaction => {
// This code may get re-run multiple times if there are conflicts.
return transaction.get(userRef).then(userDoc => {
if (!userDoc.exists) {
throw "Document does not exist!";
}
const new_balance = change.after.data().balance;
const old_balance = change.before.data().balance;
var user_balance = userDoc.data().balance + new_balance - old_balance;
transaction.update(userRef, {balance: user_balance});
});
}).catch(error => {
console.log("Transaction failed: ", error);
return null;
});
});