0
votes

Please clarify my doubt, I am working on mobile app development which uses the Firebase realtime database. url is / req/ 12345/ confirmed = ""

Scenario is: when 2 mobiles at a time app (internally using Firebase) read the Firebase node data (12345/confirmed) and check whether the data is null or empty , if it is null or empty, it will update with its id(app user have unique id)

Issue is: at the same time, both are reading data as null and both are updating the value, how to avoid this kind of scenario? Do we have any mechanism to lock that node ?

this is the code using.

String node = "/" + "req" + "/" + requestId;
ValueEventListener postListener = new ValueEventListener() {
    @Override
    public void onDataChange(@NonNull DataSnapshot dataSnapshot) {

        String driverIdd = preference.getLongDetail(Constants.DRIVER_ID);
        Log.e("Test","Log::Going to Confirm Driver:::"+driverIdd);
        String confirmed_walker = dataSnapshot.child("confirmed_walker").getValue(String.class);
        Log.e("Test","Log::Confiremed::"+confirmed_walker);
        if(confirmed_walker.isEmpty()){
            Map<String, Object> updates = new HashMap<>();
            updates.put(node + "/confirmed_walker",driverIdd);
            databaseReference.updateChildren(updates).addOnCompleteListener(task -> {

                Log.e("Test","Log::Confirmed Driver:::"+driverIdd);

            }).addOnFailureListener(e -> {

            });
        }
    }

    @Override
    public void onCancelled(@NonNull DatabaseError databaseError) {

    }
};


databaseReference.child(
        node).
        addListenerForSingleValueEvent(postListener);
1
Id is nothing but it is string @Ashish - Saranya Subramanian
I think Firebase work on FIFS means (First in First Serve). so i think it manager api calls in proper manner and update first call value and then replace with second one. - Ashish
@Ashish edited please check - Saranya Subramanian
There is no built in mechanism in Firebase to lock a node. However, Firebase does have Transactions that can handle situations where concurrent writes are attempted. - Jay

1 Answers

1
votes

You're looking for a Firebase Database transaction here. From the documentation:

When working with data that could be corrupted by concurrent modifications, such as incremental counters, you can use a transaction operation.

A Firebase transaction may be a bit unusual at first, as you don't explicitly place a lock on the data. Instead you write a function that gets called with a guess to the current value of the database node, and your function returns the new value that the server should store in that case.

In your case it'd look something like this:

databaseReference.child(node).runTransaction(new Transaction.Handler() {
    @Override
    public Transaction.Result doTransaction(MutableData data) {
        if (data.getValue() == null) {
            // TODO: call data.setValue(...) to set the initial data for the node
            return Transaction.success(mutableData);
        }

        if (TextUtils.isEmpty(data.child("confirmed_walker").getValue(String.class))) {
            // TODO: call data.setValue(...) to set the data for a confirmed walker
            // Report transaction success, so that the server tries to commit it
            return Transaction.success(mutableData);
        }

        // Abort the transaction, since we have nothing to do
        return Transaction.abort();
    }

    @Override
    public void onComplete(DatabaseError databaseError, boolean b,
                           DataSnapshot dataSnapshot) {
        // Transaction completed
        Log.d(TAG, "runTransaction:onComplete:" + databaseError);
    }
});

*Update**: since you only seem to be reading/writing the confirmed_walker node, you can run the transaction just on that node, both reducing contention, and making the code a bit simpler:

databaseReference.child(node).child("confirmed_walker").runTransaction(new Transaction.Handler() {
    @Override
    public Transaction.Result doTransaction(MutableData data) {
        if (data.getValue() == null) {
            // Set the driver ID for this node
            String driverIdd = preference.getLongDetail(Constants.DRIVER_ID);
            data.setValue(driverIdd);

            // Report transaction success, so that the server tries to commit it
            return Transaction.success(mutableData);
        }

        // Abort the transaction, since we have nothing to do
        return Transaction.abort();
    }

    @Override
    public void onComplete(DatabaseError databaseError, boolean b,
                           DataSnapshot dataSnapshot) {
        // Transaction completed
        Log.d(TAG, "runTransaction:onComplete:" + databaseError);
    }
});