简体   繁体   中英

How to prevent Transaction write conflict and runTransaction asynchronously?

So I am currently running a few scripts to reset user balances within one of our apps. I am using firestore to get all the tasks and then tally balances based on those for each of our users. Sounds simple right? It should be and its not. The problem is that the transactions are aborting because of write conflicts to the same document. This is happening because all tasks are being passed to the transaction at the same time.

I have tried to use the forEach method which is causing them to abort because it keeps trying to update the same user record each time. And i have tried for(let task of tasks) which gives me an error 'cannot itterate 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);
  }
}

Currently the conflicts between document writes cause the transactions to abort half way through.

I would really appreciate if someone can assist me in being able to pass each task to the transaction, in an asynchronous manner, waiting for the transaction to complete and then pass the next task through. Or another way which will get the same results....

Thanks in advance you kind kind kind soul(s)!

Your updatestuff function is kicking off as many asynchronous items of work, but it's not returning a promise that becomes resolves when all of them are complete. You would use that promise in you main trigger function to let Cloud Functions know that all the work is complete and it's safe to clean up everything. If you don't do that, then your function risks being shut down before the work is complete.

Bear in mind that using async/await with forEach isn't really a good idea. The forEach function isn't going to wait until the async work is complete before moving on to the next item. It's going to finish with a lot of transactions in flight, and no way to know when they're all done. Instead, you might not want to use async/await, and instead push all the transaction promises into an array, then use Promise.all() on that array to create a new promise that resolves when all the work is complete. Return that from your function, then use that in your main trigger function.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM