public void startTransaction()

in firebase-database/src/main/java/com/google/firebase/database/core/Repo.java [880:990]


  public void startTransaction(Path path, final Transaction.Handler handler, boolean applyLocally) {
    if (operationLogger.logsDebug()) {
      operationLogger.debug("transaction: " + path);
    }
    if (dataLogger.logsDebug()) {
      operationLogger.debug("transaction: " + path);
    }

    if (this.ctx.isPersistenceEnabled() && !loggedTransactionPersistenceWarning) {
      loggedTransactionPersistenceWarning = true;
      transactionLogger.info(
          "runTransaction() usage detected while persistence is enabled. Please be aware that "
              + "transactions *will not* be persisted across database restarts.  See "
              + "https://www.firebase.com/docs/android/guide/offline-capabilities.html"
              + "#section-handling-transactions-offline for more details.");
    }

    // make sure we're listening on this node
    // Note: we can't do this asynchronously. To preserve event ordering,
    // it has to be done in this block.  This is ok, this block is
    // guaranteed to be our own event loop
    DatabaseReference watchRef = InternalHelpers.createReference(this, path);
    ValueEventListener listener =
        new ValueEventListener() {
          @Override
          public void onDataChange(DataSnapshot snapshot) {
            // No-op. We don't care, this is just to make sure we have a listener outstanding
          }

          @Override
          public void onCancelled(DatabaseError error) {
            // Also a no-op? We'll cancel the transaction in this case
          }
        };
    addEventCallback(new ValueEventRegistration(this, listener, watchRef.getSpec()));

    TransactionData transaction =
        new TransactionData(
            path,
            handler,
            listener,
            TransactionStatus.INITIALIZING,
            applyLocally,
            nextTransactionOrder());

    // Run transaction initially.
    Node currentState = this.getLatestState(path);
    transaction.currentInputSnapshot = currentState;
    MutableData mutableCurrent = InternalHelpers.createMutableData(currentState);

    DatabaseError error = null;
    Transaction.Result result;
    try {
      result = handler.doTransaction(mutableCurrent);
      if (result == null) {
        throw new NullPointerException("Transaction returned null as result");
      }
    } catch (Throwable e) {
      operationLogger.error("Caught Throwable.", e);
      error = DatabaseError.fromException(e);
      result = Transaction.abort();
    }
    if (!result.isSuccess()) {
      // Abort the transaction
      transaction.currentOutputSnapshotRaw = null;
      transaction.currentOutputSnapshotResolved = null;
      final DatabaseError innerClassError = error;
      final DataSnapshot snap =
          InternalHelpers.createDataSnapshot(
              watchRef, IndexedNode.from(transaction.currentInputSnapshot));
      postEvent(
          new Runnable() {
            @Override
            public void run() {
              handler.onComplete(innerClassError, false, snap);
            }
          });
    } else {
      // Mark as run and add to our queue.
      transaction.status = TransactionStatus.RUN;

      Tree<List<TransactionData>> queueNode = transactionQueueTree.subTree(path);
      List<TransactionData> nodeQueue = queueNode.getValue();
      if (nodeQueue == null) {
        nodeQueue = new ArrayList<TransactionData>();
      }
      nodeQueue.add(transaction);
      queueNode.setValue(nodeQueue);

      Map<String, Object> serverValues = ServerValues.generateServerValues(serverClock);
      Node newNodeUnresolved = result.getNode();
      Node newNode =
          ServerValues.resolveDeferredValueSnapshot(
              newNodeUnresolved, transaction.currentInputSnapshot, serverValues);

      transaction.currentOutputSnapshotRaw = newNodeUnresolved;
      transaction.currentOutputSnapshotResolved = newNode;
      transaction.currentWriteId = this.getNextWriteId();

      List<? extends Event> events =
          this.serverSyncTree.applyUserOverwrite(
              path,
              newNodeUnresolved,
              newNode,
              transaction.currentWriteId,
              /*visible=*/ applyLocally,
              /*persist=*/ false);
      this.postEvents(events);
      sendAllReadyTransactions();
    }
  }