Home > Software design >  How could I update and GET a ServerValue.increment operation via setValue in Firebase - Realtime?
How could I update and GET a ServerValue.increment operation via setValue in Firebase - Realtime?

Time:12-27

I can see the .runTransaction option has a completion listener which returns a snapshot, so I will assume this snapshot is the updated snapshot ("DataSnapshot currentData") that the client just performed.

@NonNull
public Result doTransaction(@NonNull MutableData currentData);

/**
 * This method will be called once with the results of the transaction.
 *
 * @param error null if no errors occurred, otherwise it contains a description of the error
 * @param committed True if the transaction successfully completed, false if it was aborted or
 *     an error occurred
 * @param currentData The current data at the location or null if an error occurred
 */
public void onComplete(
    @Nullable DatabaseError error, boolean committed, @Nullable DataSnapshot currentData);

My guess is that using a setValue(ServerValue.increment) is an order of magnitude faster than runTransaction.

The problem is, it seems the retrieval of the result is not possible, since there does not seem to be any listener that reliably returns the updated value by the client.

  /**
   * ...
   * @param value The value to set at this location or null to delete the existing data
   * @param listener A listener that will be triggered with the results of the operation
   */
  public void setValue(@Nullable Object value, @Nullable CompletionListener listener) {
    setValueInternal(value, PriorityUtilities.parsePriority(this.path, null), listener);
  }

Here the documentation makes the promise that the listener will give me "the result of THE (MY??) operation", but when going to the listener it does not say that:

  /**
   * This interface is used as a method of being notified when an operation has been acknowledged by
   * the Database servers and can be considered complete
   *
   * @since 1.1
   */
  public interface CompletionListener {

    /**
     * This method will be triggered when the operation has either succeeded or failed. If it has
     * failed, an error will be given. If it has succeeded, the error will be null
     *
     * @param error A description of any errors that occurred or null on success
     * @param ref A reference to the specified Firebase Database location
     */
    public void onComplete(
        @Nullable final DatabaseError error, @NonNull final DatabaseReference ref);
  }

Notice that the DatabaseReference is not a "snapshot" of the branch at the moment of value update, instead is just a live reference...

Because a plain reference is returned I assume we are forced to connect a plain singleValueEventListener to this reference.... which means this read will NOT be atomic...

So There are a couple methods that may work but Im not sure:

Object db_config_ver = ServerValue.increment(1);

db_ver_ref.setValue(db_config_ver,
                            new DatabaseReference.CompletionListener() {
                                @Override
                                public void onComplete(@Nullable DatabaseError error, @NonNull DatabaseReference ref) {
                                    if (error == null) {

                                       //Should I connect a listener?? But this is NOT atomic and will bring ERRORS under heavy contention!!
                                        ref.addListenerForSingleValueEvent(
                                                new ValueEventListener() {
                                                    @Override
                                                    public void onDataChange(@NonNull DataSnapshot snapshot) {

                                                    }

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

                                                    }
                                                }
                                        );

                                        //Maybe a simple proactive read will suffice?
                                        long value = ref.get().getResult().getValue(Long.class);

                                       //What If I read the ServerValue.increment() object here:
                                       System.out.println(db_config_ver)

                                    }
                                }
                            }
                    );

Or maybe there is NO reliable way to do this atomically.

So maybe setValue and updateChildren may work properly with the ServerValue.increment operator... BUT the way to properly do an updateAndGet can only be achieved with runTransaction()??

CodePudding user response:

You can use ServerValue.increment() to increment a value of a field when you are not interested in what already exists on the server. If you need to perform some operations according to the value that already exists, then indeed you need to perform a transaction, which reads the data first and performs the update right after that.

The first solution is a simplified version of a transaction, which doesn't need round-trip communications with Firebase servers.

When you want to read the value of a field that is incremented by multiple users, you should attach a persistence listener, so you can be notified in real-time.

  • Related