private LockState executeRollback(long rootProcId, RootProcedureState<TEnvironment> procStack) {
    Procedure<TEnvironment> rootProc = procedures.get(rootProcId);
    RemoteProcedureException exception = rootProc.getException();
    // TODO: This needs doc. The root proc doesn't have an exception. Maybe we are
    // rolling back because the subprocedure does. Clarify.
    if (exception == null) {
      exception = procStack.getException();
      rootProc.setFailure(exception);
      store.update(rootProc);
    }

    if (procStack.isRollbackSupported()) {
      LockState lockState = executeNormalRollback(rootProc, procStack);
      if (lockState != LockState.LOCK_ACQUIRED) {
        return lockState;
      }
    } else {
      // the procedure does not support rollback, so typically we should not reach here, this
      // usually means there are code bugs, let's just wait all the subprocedures to finish and then
      // mark the root procedure as failure.
      LOG.error(HBaseMarkers.FATAL,
        "Root Procedure {} does not support rollback but the execution failed"
          + " and try to rollback, code bug?",
        rootProc, exception);
      executeUnexpectedRollback(rootProc, procStack);
    }

    IdLock.Entry lockEntry = getLockEntryForRollback(rootProc.getProcId());
    try {
      // Finalize the procedure state
      LOG.info("Rolled back {} exec-time={}", rootProc,
        StringUtils.humanTimeDiff(rootProc.elapsedTime()));
      rootProcedureFinished(rootProc);
    } finally {
      if (lockEntry != null) {
        procExecutionLock.releaseLockEntry(lockEntry);
      }
    }

    return LockState.LOCK_ACQUIRED;
  }
Execute the rollback of the full procedure stack. Once the procedure is rolledback, the
root-procedure will be visible as finished to user, and the result will be the fatal exception.
hbase-procedure/src/main/java/org/apache/hadoop/hbase/procedure2/ProcedureExecutor.java