Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Firebase transactions in NodeJS always running 3 times?

Whenever I define a Firebase transaction in NodeJS I notice it always runs three times - the first two times with null data, then finally a third time with actually data. Is this normal/intended?

For example this code:

firebaseOOO.child('ref').transaction(function(data) {
    console.log(data);
    return data;
});

outputs the following:

null
null
i1: { a1: true }

I would have expected that it only print the last item.

To answer a question in the comments, here is the same with a callback:

firebaseOOO.child('ref').transaction(function(data) {
    console.log(data);
    return data;
}, function(error, committed, snapshot) {
    if (error) 
        console.log('failed');
    else if (!committed)
        console.log('aborted');
    else 
        console.log('committed');
    console.log('fin');
});

Which yields the following output:

null
null
i1: { a1: true }
committed
fin

I had read the details of how transactions work before posting the question, so I had tried setting applyLocally to false like this:

firebaseOOO.child('ref').transaction(function(data) {
    console.log('hit'); 
    return data; 
}, function(){}, false);

But it still hits 3 times (just double-checked) so I thought it was something different. Getting the 'value' before transacting does "work" as expected, in that it only hits once, and that's regardless of what applyLocally is set to, so I'm not sure what applyLocally does? This is what I mean by getting the value before transacting:

firebaseOOO.child('ref').once('value', function(data) {
    console.log('1');
    firebaseOOO.child('ref').transaction(function(data) {
        console.log('2');
        return data;
    });
});

Outputs:

1
2

@Michael: How can one make use of this behavior? Transactions are primarily for having data use itself to modify itself - the prototypical increment++ scenario. So if I need to add 1 to the existing value of 10, and continue working with the result of 11, the first two times the function hits I will have an erroneous result of 1 that I need to handle, and finally the correct result of 11 on the third hit. How can I make use of those two initial 1's? Another scenario (and maybe I shouldn't be using transactions for this, but if it worked like I expected it makes for cleaner code) is to insert a value if it does not yet exist. If transactions only hit once, a null value would mean the value does not exist, and so you could, for example, init the counter to 1 in that case, otherwise add 1 to whatever the value is. With the noisy nulls, this is not possible.

It seems the takeaway from all this is to simply use the 'once' pattern more often than not?

ONCE TRANSACTION PATTERN:

firebaseOOO.child('ref').once('value', function(data) {
    console.log('1');
    firebaseOOO.child('ref').transaction(function(data) {
        console.log('2');
        return data;
    });
});
like image 389
Baz Avatar asked Nov 03 '22 23:11

Baz


1 Answers

The behavior you're seeing here is related to how Firebase fires local events and then eventually synchronizes with the Firebase servers. In this specific example, the "running three times" will only happen the very first time you run the code—after that, the state has been completely synchronized and it'll just trigger once from then on out. This behavior is detailed here: https://www.firebase.com/docs/transactions.html (See the "When a Transaction is run, the following occurs" section.)

If, for example, you have an outstanding on() at the same location and then, at some later time, run this same transaction code, you'll see that it'll just run once. This is because everything is in sync prior to the transaction running (in the ideal case; barring any normal conflicts, etc).

like image 99
Vikrum Avatar answered Nov 09 '22 07:11

Vikrum