private void pushProceduresAfterLoad(List<Procedure<TEnvironment>> runnableList,
    List<Procedure<TEnvironment>> failedList) {
    failedList.forEach(scheduler::addBack);
    // Put the procedures which have been executed first
    // For table procedures, to prevent concurrent modifications, we only allow one procedure to run
    // for a single table at the same time, this is done via inserting a waiting queue before
    // actually add the procedure to run queue. So when loading here, we should add the procedures
    // which have been executed first, otherwise another procedure which was in the waiting queue
    // before restarting may be added to run queue first and still cause concurrent modifications.
    // See HBASE-28263 for the reason why we need this
    runnableList.sort((p1, p2) -> {
      if (p1.wasExecuted()) {
        if (p2.wasExecuted()) {
          return Long.compare(p1.getProcId(), p2.getProcId());
        } else {
          return -1;
        }
      } else {
        if (p2.wasExecuted()) {
          return 1;
        } else {
          return Long.compare(p1.getProcId(), p2.getProcId());
        }
      }
    });
    runnableList.forEach(p -> {
      p.afterReplay(getEnvironment());
      if (!p.hasParent()) {
        sendProcedureLoadedNotification(p.getProcId());
      }
      scheduler.addBack(p);
    });
  }
Scheduler/Queue that contains runnable procedures.
hbase-procedure/src/main/java/org/apache/hadoop/hbase/procedure2/ProcedureExecutor.java