Is There an Firebase Realtime Database Equivalent to The Increment Method Available in Firestore

Most efficient way to increment a value of everything in Firebase

This is one way to loop over all items and increase their priority:

var estimatesRef = firebase.child('Estimates');
estimatesRef.once('value', function(estimatesSnapshot) {
estimatesSnapshot.forEach(function(estimateSnapshot) {
estimateSnapshot.ref().update({
estimateSnapshot.val().priority + 1
});
});
});

It loops over all children of Estimates and increases the priority of each.

You can also combine the calls into a single update() call:

var estimatesRef = firebase.child('Estimates');
estimatesRef.once('value', function(estimatesSnapshot) {
var updates = {};
estimatesSnapshot.forEach(function(estimateSnapshot) {
updates[estimateSnapshot.key+'/priority'] = estimateSnapshot.val().priority + 1;
});
estimatesRef.update(updates);
});

The performance will be similar to the first solution (Firebase is very efficient when it comes to handling multiple requests). But in the second case it will be sent a single command to the server, so it will either fail or succeed completely.

Is there any method like onDisconnect() in firestore like there is in realtime database?

According to this onDisconnect:

The onDisconnect class is most commonly used to manage presence in applications where it is useful to detect how many clients are connected and when other clients disconnect.

To be able to use presence in firestore, you need to connect firestore with realtime firebase(no other way).

Please check this for more info:

https://firebase.google.com/docs/firestore/solutions/presence

Easiest way to increment a data point in Firebase?

Why not (v2.x Firebase code but you get the idea)

counterRef.observeSingleEventOfType(.Value, withBlock: { snapshot in
let valString = snapshot.value
var value = valString.intValue
value = value + 1
counterRef.setValue("\(value)")
})

If this is going to be updated frequently by multiple users, leverage a transaction block as well.

How quickly can you atomically increment a value on the Firebase Realtime Database?

TL;DR

I tested these cases:

  1. Increment a value with a transaction call:

    ref.transaction(function(value) {
    return (value || 0) + 1;
    });
  2. Increment a value with the new increment operator:

    ref.set(admin.database.ServerValue.increment(1));

The fact that increment is faster won't be a surprise, but... by how much?

Results:

  • With transactions I was able to increment a value about 60-70 times per second.
  • With the increment operator, I was able to increment a value about 200-300 times per second.

How I performed the test and got these numbers

I've run the test on my 2016 model macBook pro, and wrapping the above in a simple Node.js script that uses the client-side Node SDK. The wrapping script for the operations was really basic as well:

timer = setInterval(function() {
... the increment or transaction from above ...
}, 100);

setTimeout(function() {
clearInterval(timer);
process.exit(1);
}, 60000)

So: increment the value 10 times per second, and stop doing that after 1 minute. I then spawned instances of this process with this script:

for instance in {1..10}
do
node increment.js &
done

So this would run 10 parallel processes with the increment operator, each increasing the value 10 times per second, for a total of 100 increments per second. I then changed the number of instances until the "increments per second" reached a peak.

I then wrote a small script on jsbin to listen for the value, and determine the number of increments per second by a simple low pass, moving average filter. I had some trouble here, so am not sure if the calculations are completely correct. Given my test results they were close close enough, but if anyone feels like writing a better observer: be my guest. :)

Things to note about the tests:

  1. I kept increasing the number of processes, until the "increments per second" seemed to max out, but I noticed that this coincided with my laptop fans going full-speed. So it's likely that I didn't find the true maximum throughput of the server-side operation, but a combination of my test environment and the server. So it is quite possible (and in fact likely) you may get different results when you try to reproduce this test, although of course the increment throughput should always be significantly higher than the transaction. No matter what results you get: please share them. :)

  2. I've used the client-side Node.js SDK, as it was easiest to get working. Using different SDKs may give slightly different results, although I expect the primary SDKs (iOS, Android, and Web) to be quite close to what I got.

  3. Two different team mates immediately asked whether I'd run this on a single node, or if I was incrementing multiple values in parallel. Incrementing multiple values in parallel might show if there's a system-wide throughput bottleneck in or if it is node-specific (which I expect).

  4. As said already: my test harness is nothing special, but my jsbin observer code is especially suspect. Kudos if anyone feels like coding up a better observer on the same data.


How the transaction and increment operator work under the hood

To understand the performance difference between transaction and increment it really helps to know how these operations work under the hood. For the Firebase Realtime Database "under the hood" means, the commands and responses that are sent between the clients and server over the Web Socket connection.

Transactions in Firebase use a compare-and-set approach. Whenever we start transaction like above, the client takes a guess at the current value of the node. If it's never see the node before that guess is null. It calls our transaction handler with that guess, and our code then returns the new value. The client send the guess and the new value to the server, which performs a compare-and-set operation: if the guess is correct, set the new value. If the guess is wrong, the server rejects the operation and returns the actual current value to the client.

In a perfect scenario, the initial guess is correct, and the value is immediately written to disk on the server (and after that, sent out to all listeners). In a flow chart that'd look like this:

            Client            Server

+ +
transaction() | |
| |
null | |
+---<-----+ |
| | |
+--->-----+ |
1 | (null, 1) |
+--------->---------+
| |
+---------<---------+
| (ack, 3) |
| |
v v

But if the node already has a value on the server, it rejects the write, sends back the actual value, and the client tries again:

            Client            Server

+ +
transaction() | |
| |
null | |
+---<-----+ |
| | |
+--->-----+ |
1 | |
| (null, 1) |
+--------->---------+
| |
+---------<---------+
| (nack, 2) |
| |
2 | |
+---<-----+ |
| | |
+--->-----+ |
3 | (2, 3) |
+--------->---------+
| |
+---------<---------+
| (ack, 3) |
| |
| |
v v

This isn't too bad, one extra roundtrip. Even if Firebase would've used pessimistic locking, it would have needed that roundtrip, so we didn't lose anything.

The problem starts if multiple clients are modifying the same value concurrently. This introduces so-called contention on the node, which looks like this:

            Client            Server                Client
+ + +
transaction() | | |
| | | transaction()
null | | |
+---<-----+ | | null
| | | +--->----+
+--->-----+ | | |
1 | | +---<----+
| (null, 1) | | 1
+--------->---------+ (null, 1) |
| |---------<---------+
+---------<---------+ |
| (nack, 2) |--------->---------+
| | (nack, 2) |
2 | | |
+---<-----+ | | 2
| | | |--->----+
+--->-----+ | | |
3 | (2, 3) | |---<----+
+--------->---------+ | 3
| | |
+---------<---------+ |
| (ack, 3) | (2, 3) |
| |---------<---------+
| | |
| |--------->---------+
| | (nack, 3) |
| | | 3
| | |--->----+
| | | |
| | |---<----+
| | | 4
| | (3, 4) |
| |---------<---------+
| | |
| |--------->---------+
| | (ack, 4) |
| | |
v v v

TODO: Update the above chart so that the operations on the server don't overlap.

The second client had to do another retry for its operation, because the server-side value had been modified between its first and second try. The more clients we have writing to this location, the more likely it is that you'll see retries. And the Firebase client performs those retries automatically, but after a number of retries it will give up and raise an Error: maxretry exception to the application.

This is the reason I could only increment a counter about 60-70 times per second: with more writes than that, there was too much contention on the node.

An increment operation is atomic by nature. You're telling the database: whatever the current value is, make it x higher. This means that the client never has to know the current value of the node, and so it also can't guess wrong. It simply tells the server what to do.

Our flow chart with multiple clients looks like this when using increment:

            Client            Server                Client

+ + +
increment(1) | | |
| | | increment(1)
| (increment, 1) | |
+--------->---------+ (increment, 1) |
| |---------<---------+
+---------<---------+ |
| (ack, 2) |--------->---------+
| | (ack, 3) |
| | |
v v v

The length of these last two flow charts alone already goes a long way to explain why increment is so much faster in this scenario: the increment operation is made for this, so the wire protocol much more closely represents what we're trying to accomplish. And that simplicity leads to a 3x-4x performance difference in my simple test alone, and probably even more in production scenarios.

Of course transactions are still useful, as there are many more atomic operations than just increments/decrements.

Does Firestore REST API support equivalent values to the SDK's FieldValue?

The REST API is not necessarily easy to use, since you're essentially seeing the wire protocol of gRPS calls. For that reason the documentation says:

If you are using a gRPC-supported language, consider using the RPC API rather than the REST API.

That said, operation such as FieldValue.increment are available in the REST API in the form of so-called transforms, which are additional operations that are performed on the server after the actual setting of the value(s) (still as part of the same write operation). In this case you're looking for a FieldTransform operation, as shown here.

I highly recommend studying the example given here of using server-side timestamps (which is quite similar), and liberally using the Firestore API explorer.

Android studio value increment in Firebase

Edit: 20222608

Actually, there is a really simple solution nowadays in which we can increment a field in the Realtime Database which is:

scoreRef.setValue(ServerValue.increment(1));

And to decrement a value, simply pass a negative number:

scoreRef.setValue(ServerValue.increment(-1));

In order to increment a value in a Firebase database, first of all, you need to retrieve that value. There is no way to increment a value without knowing it. To achieve this, I definitely recommend you to use Firebase Transaction.

Let's take an example. Let's assume we want to increment a counter. In order to achieve this, please use the following code to set the default value of the counter.

DatabaseReference rootRef = FirebaseDatabase.getInstance().getReference();
rootRef.child("score").setValue(1);

Assuming that the score field is of type Integer, to use transactions, please use the following method:

public static void setScore(String operation) {
DatabaseReference rootRef = FirebaseDatabase.getInstance().getReference();
DatabaseReference scoreRef = rootRef.child("score");
scoreRef.runTransaction(new Transaction.Handler() {
@Override
public Transaction.Result doTransaction(MutableData mutableData) {
Integer score = mutableData.getValue(Integer.class);
if (score == null) {
return Transaction.success(mutableData);
}

if (operation.equals("increaseScore")) {
mutableData.setValue(score + 1);
} else if (operation.equals("decreaseScore")){
mutableData.setValue(score - 1);
}

return Transaction.success(mutableData);
}

@Override
public void onComplete(DatabaseError databaseError, boolean b, DataSnapshot dataSnapshot) {}
});
}

Using transactions, you will avoid inconsistent results if users are trying to increase/decrease the score at the same time. So as a conclusion, call this method accordingly to your increase/decrease operation.

If you want to read the score, please use the following code:

DatabaseReference rootRef = FirebaseDatabase.getInstance().getReference();
DatabaseReference scoreRef = rootRef.child("score");
ValueEventListener eventListener = new ValueEventListener() {
@Override
public void onDataChange(DataSnapshot dataSnapshot) {
Integer score = dataSnapshot.getValue(Integer.class);
Log.d("TAG", "score: " + score);
}

@Override
public void onCancelled(DatabaseError databaseError) {}
};
scoreRef.addListenerForSingleValueEvent(eventListener);

How can I do an offline batch in Firebase RTDB?

You can do a multi-path update to perform both writes transactionally:

var id = dbRef.push().key;
Map<String, dynamic> updates = {
"inventory/$foodId/quantity": ServerValue.increment(-quantity),
"output/$id": salidaAlimento.toJson()
}
dbRef.update(updates);

With the above, either both writes are completed, or neither of them is.

While you're offline, the client will fire local events based on its best guess for the current value of the server (which is gonna be 0 if it never read the value), and it will then send all pending changes to the server when it reconnects. For a quick test, see https://jsbin.com/wuhuyih/2/edit?js,console

Firebase How to increment counter while offline?

Since a transaction requires access to the current value of a field to determine the new value of a field, there is no way to run transactions while you're not connected to the database server.

The Firebase clients don't persist transactions across app restarts for the same reasons: the concept of transactions doesn't work well when a user is not connected.

If you want to record user actions while they are not connected, you should literally store that in your database: the user actions.

So instead of trying to increase the likeCount with a transaction, you could keep a list of likedPosts for the user:

likedPosts
uidOfTooFoo
post1: true
post3: true
uidOfTooPuf
post2: true
post3: true

With this structure you don't need a transaction to increase a counter, because each user is essentially isolated from everyone else.

Alternative, you could keep a queue of like actions:

likesQueue
-K234782387432
uid: "uidOfPoofoo"
post: post1
-K234782387433
uid: "uidOfPuf"
post: post2
-K234782387434
uid: "uidOfPuf"
post: post3
-K234782387434
uid: "uidOfPoofoo"
post: post3

With this last structure, you'd then spin up a small back-end service that consumes this queue (by listening for child_added events or preferably by using firebase-queue) and then increases the shared counter.



Related Topics



Leave a reply



Submit