Как предотвратить конфликт записи транзакций и асинхронное выполнение транзакций?

#javascript #transactions #async-await #google-cloud-firestore

# #javascript #транзакции #асинхронное ожидание #google-облако-firestore

Вопрос:

Итак, в настоящее время я запускаю несколько сценариев для сброса пользовательских балансов в одном из наших приложений. Я использую firestore для получения всех задач, а затем подсчитываю балансы на основе таковых для каждого из наших пользователей. Звучит просто, не так ли? Это должно быть и его нет. Проблема в том, что транзакции прерываются из-за конфликтов записи в один и тот же документ. Это происходит потому, что все задачи передаются транзакции одновременно.

Я пытался использовать метод forEach, который вызывает их прерывание, потому что он продолжает пытаться каждый раз обновлять одну и ту же запись пользователя. И я попытался (пусть task of tasks), который выдает мне ошибку «не удается выполнить задачи».

 async function updatestuff(){
  console.log("start");
  const tasksRef = db.collection('tasks');
  try {
          const allTasksSnapShot = await tasksRef.get();
          allTasksSnapShot.forEach(async(doc) => {
            //Run transaction for calculations for each task here
            const tskUser = doc.data().taskUser;
            const tuser = db.collection('backupusers').doc(tskUser);
                if(doc.data().taskStatus == 'Not Completed'){
                  console.log('NOT');}
                if(doc.data().taskStatus != 'Not Completed'){
                  console.log('Run transaction here and wait for it to finish?');
                  await db.runTransaction(async t => {
                  const userSnapShot =  await t.get(tuser);
                          if (!userSnapShot) {return console.log('not a thing')}
                          //get new balance calculated
                          const newBalance = userSnapShot.actualbalance   doc.data().taskBalanceAdj;
                          await t.update(tuser, {actualbalance: newBalance});
                  })
            }});
          console.log("end")
  }
  catch (err) {
    console.log('Error getting documents', err);
  }
}
 

В настоящее время конфликты между записями документов приводят к прерыванию транзакций на полпути.

Я был бы очень признателен, если бы кто-нибудь мог помочь мне передать каждую задачу транзакции асинхронным образом, ожидая завершения транзакции, а затем передать следующую задачу. Или другой способ, который даст те же результаты….

Заранее спасибо вам, добрые добрые добрые души!

Комментарии:

1. Вы комбинируете использование async / await с then() и catch() . Это затрудняет понимание того, что делает ваш код, и может даже быть неправильным. Если вы хотите использовать async / await, почему бы не применить его во всем коде для обеспечения согласованности и удобочитаемости?

2. Привет, Даг, веришь или нет, я наблюдал за тобой довольно много дня, и твои фейерверки великолепны! Я обновил свой код, чтобы удалить then catch, однако это заставило меня понять, что мне, возможно, нужно пересмотреть эти видео и несколько других примеров асинхронного ожидания.

3. Итак, в чем именно здесь ошибка?

4. Поэтому, когда я запускаю его, я получаю «Необработанное promiserejectionwarning: Ошибка: 10 ПРЕРВАНО: Срок действия указанной транзакции истек или она больше не действительна «. примерно через минуту функция останавливается. Многие транзакции завершаются без проблем в течение первых 30 секунд. Наконец, я получаю сообщение об ошибке в этих записях слишком много разногласий, пожалуйста, попробуйте еще раз, и функция прервется. Поэтому я думаю, что проблема связана с тем, что задачи передаются транзакции синхронно, и при нескольких записях в один и тот же документ транзакция прерывается.

Ответ №1:

Ваша updatestuff функция запускает столько асинхронных элементов работы, но не возвращает обещание, которое становится разрешенным, когда все они завершены. Вы бы использовали это обещание в своей основной триггерной функции, чтобы сообщить облачным функциям, что вся работа завершена и можно безопасно все очистить. Если вы этого не сделаете, ваша функция рискует быть закрытой до завершения работы.

Имейте в виду, что использование async / await с forEach на самом деле не очень хорошая идея. Функция forEach не будет ждать завершения асинхронной работы, прежде чем перейти к следующему элементу. Это закончится с большим количеством транзакций в полете, и нет способа узнать, когда все они будут выполнены. Вместо этого вы можете не захотеть использовать async / await и вместо этого поместить все обещания транзакции в массив, а затем использовать Promise.all() в этом массиве для создания нового обещания, которое разрешается, когда вся работа завершена. Верните это из своей функции, затем используйте это в своей основной триггерной функции.