2

So since I've started using Firebase Web Transactions within the Firebase Cloud Functions environment I haven't been able to have the third parameter 'applyLocally' work correctly. I've been setting it to false to suppress getting the initial null value incase the data that is gotten from the transaction is actually null itself. When I run the code after deploying it, it passes null to the update function, but when I run the code again without a new deploy to Cloud Functions, THEN it suppresses all local events and I only see my data.

Q: Is the transaction parameter 'applyLocally' set to false supposed to suppress locally cached values every time when applyLocally is set to false, or is it built so that after the initial code deploy you will get null as a value passed to the update function?

The reason why I'm asking this question is because if the locally cached values aren't suppressed every time, then I would have to account for that initial intermediate state, whereas then I don't understand why I should use applyLocally at all. Also because I didn't see anything elaborating within the docs.

The docs I've read through are: https://firebase.google.com/docs/reference/js/firebase.database.Reference#transaction

https://firebase.google.com/docs/database/web/read-and-write#save_data_as_transactions

I've also come across a similar question with a seemingly accurate answer, but the question and answer are over 4 years old (not sure if outdated), and the link to old docs doesn't provide anything the current docs don't.

Firebase transactions in NodeJS always running 3 times?

Here is my code that duplicates this error in my environment:

workerDataRef.transaction(firstTransactionUpdater,firstTransactionOnComplete,false);
  function firstTransactionUpdater(workerData){
    console.log(`Transaction checker Function Running. Worker Data is `,workerData);
    if(workerData === null){ //data should not be null
      console.log("100 Data was null");
      return workerData;
    }
    else{ //workerData is not null
      //DO WORK
      console.log("200 Data was not null");
      return workerData;
    }
  }
  function firstTransactionOnComplete(error, committed, snapshot) {
    if (error) { //error thrown
      throw new Error(error);
    } else if (!committed) { //transaction aborted
      console.log(`Transaction checker aborted!`);
    } else { //transaction completed successfully
      console.log("Continuing on with next process");
    }
  }

And here are the logs that this code block reproduces from being run twice after one deploy to Cloud Functions:

1:35:40.495 PM Function execution took 410 ms, finished with status: ok 
1:35:40.494 PM Continuing on with next process  
1:35:40.489 PM 200 Data was not null  
1:35:40.489 PM Transaction checker 2 Function Running. Worker Data is {    
//... } 
1:35:40.400 PM 100 Data was null  
1:35:40.399 PM Transaction checker 2 Function Running. Worker Data is null  
1:35:40.190 PM Function execution started  

1:34:35.386 PM Continuing on with next process  
1:34:33.985 PM 200 Data was not null  
1:34:33.885 PM Transaction checker 1 Function Running. Worker Data is { //.. 
}
1:34:25.698 PM Function execution took 623 ms, finished with status: ok 
1:34:25.504 PM 100 Data was null  
1:34:25.499 PM Transaction checker 1 Function Running. Worker Data is null  
1:34:25.076 PM Function execution started  

UPDATE

*In running and trying to duplicate the event data being null only after the initial deploy I found I was wrong. The locally cached data is just being passed as null at inconsistent times. Running the transaction several times after a single deploy with the 'applyLocally' set to false still passes null, and sometimes doesn't.

Devin Carpenter
  • 907
  • 8
  • 21

1 Answers1

1

Is the transaction parameter 'applyLocally' set to false supposed to suppress locally cached values...?

No, that is not the purpose of the applyLocally parameter. The reference documentation says it best:

By default, events are raised each time the transaction update function runs. So if it is run multiple times, you may see intermediate states. You can set this to false to suppress these intermediate states and instead wait until the transaction has completed before events are raised.

Setting applyLocally to false doesn't fundamentally change how transactions behave on the wire. It only changes when the client/SDK raises events.

By default the client raises events as soon as it can, and then raises reconciliatory events if needed. When you set applyLocally to false, the client will only raise events after it receives acknowledgement from the server that its data was committed.

Also see my answer here for an explanation of how transactions run. The default behavior is that the Firebase client will raise events after step 2, which can happen multiple times. Setting applyLocally to false causes events to only be raised after step 4, after the server acknowledges that the value was written.

Frank van Puffelen
  • 565,676
  • 79
  • 828
  • 807
  • So in essence when I am writing the update function I should always expect the possibility of null to be passed through the update function? I was expecting applyLocally to not let the first callback be called until it has the actual data and not a guess, but it's sounding like the applyLocally effects the second callback being ran, and prevents it from running until the transaction was aborted or the data was written. Is that right? – Devin Carpenter Oct 08 '17 at 23:32
  • You should always be prepared to receive `null` as the current value in a transaction handler. The value of `applyLocally` has no effect on that requirement. – Frank van Puffelen Oct 08 '17 at 23:42
  • @FrankvanPuffelen ... this is just another example where you have gone further than the documentation to clear up the text even further. Thank you for "Setting applyLocally to false doesn't fundamentally change how transactions.... " - that line did it for me, even though I read the documentation multiple times. – user3833732 Dec 17 '21 at 20:56