Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Data in transaction is null

Tags:

firebase

I have a problem with transactions. The data in the transaction is always null and the update handler is called only singe once. The documentation says :

To accomplish this, you pass transaction() an update function which is used to transform the current value into a new value. If another client writes to the location before your new value is successfully written, your update function will be called again with the new current value, and the write will be retried. This will happen repeatedly until your write succeeds without conflict or you abort the transaction by not returning a value from your update function

Now I know that there is no other client accessing the location right now. Secondly if I read the documentation correctly the updateCounters function should be called multiple times should it fail to retrieve and update data.

The other thing - if I take out the condition if (counters === null) the execution will fail as counters is null but on a subsequent attempt the transaction finishes fine - retrieves data and does the update.

simple once - set on this location work just fine but it is not safe.

Please what do I miss?

here is the code

self.myRef.child('counters')
  .transaction(function updateCounters(counters){
    if (counters === null) {
      return;
    }
    else {
      console.log('in transaction counters:', counters);
      counters.comments = counters.comments + 1;
      return counters;
    }
  }, function(error, committed, ss){
    if (error) {
      console.log('transaction aborted');
      // TODO error handling
    } else if (!committed){
      console.log('counters are null - why?');
    } else {
      console.log('counter increased',ss.val());
    }
  }, true);

here is the data in the location

counters:{
  comments: 1,
  alerts: 3,
  ...
}
like image 445
webduvet Avatar asked Mar 02 '15 13:03

webduvet


1 Answers

By returning undefined in your if( ... === null ) block, you are aborting the transaction. Thus it never sends an attempt to the server, never realizes the locally cached value is not the same as remote, and never retries with the updated value (the actual value from the server).

This is confirmed by the fact that committed is false and the error is null in your success function, which occurs if the transaction is aborted.

Transactions work as follows:

  • pass the locally cached value into the processing function, if you have never fetched this data from the server, then the locally cached value is null (the most likely remote value for that path)
  • get the return value from the processing function, if that value is undefined abort the transaction, otherwise, create a hash of the current value (null) and pass that and the new value (returned by processing function) to the server
  • if the local hash matches the server's current hash, the change is applied and the server returns a success result
  • if the server transaction is not applied, server returns the new value, client then calls the processing function again with the updated value from the server until successful
  • when ultimately successful, and unrecoverable error occurs, or the transaction is aborted (by returning undefined from the processing function) then the success method is called with the results.

So to make this work, obviously you can't abort the transaction on the first returned value.

One workaround to accomplish the same result--although it is coupled and not as performant or appropriate as just using the transactions as designed--would be to wrap the transaction in a once('value', ...) callback, which would ensure it's cached locally before running the transaction.

like image 90
Kato Avatar answered Oct 30 '22 21:10

Kato