private List<FailedFragmentInstanceWithStatus> dispatchRetryWrite(
      List<FragmentInstance> retriedInstances, int maxRetryAttempts) throws InterruptedException {
    Preconditions.checkArgument(maxRetryAttempts > 0);

    final long retryStartTime = System.nanoTime();
    int retryAttempt = 0;
    List<FragmentInstance> nextDispatch = new ArrayList<>(retriedInstances);
    List<FailedFragmentInstanceWithStatus> failedFragmentInstanceWithStatuses =
        Collections.emptyList();

    while (retryAttempt < maxRetryAttempts) {
      // 1. let's retry on next replica location
      nextDispatch.forEach(FragmentInstance::getNextRetriedHostDataNode);

      // 2. dispatch the instances
      failedFragmentInstanceWithStatuses = dispatchWriteOnce(nextDispatch);

      // 3. decide if to continue the retry
      final long waitMillis = getRetrySleepTime(retryAttempt);
      if (failedFragmentInstanceWithStatuses.isEmpty()
          || waitMillis + System.nanoTime() >= retryStartTime + maxRetryDurationInNs) {
        break;
      }

      // 4. sleep and do the next retry
      Thread.sleep(waitMillis);
      PERFORMANCE_OVERVIEW_METRICS.recordRemoteRetrySleepCost(waitMillis * 1_000_000L);
      retryAttempt++;
      nextDispatch =
          failedFragmentInstanceWithStatuses.stream()
              .map(FailedFragmentInstanceWithStatus::getInstance)
              .collect(Collectors.toList());
    }

    return failedFragmentInstanceWithStatuses;
  }
Dispatch the given write instances once. It will dispatch the given instances locally or
remotely, give the host datanode.
iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/scheduler/FragmentInstanceDispatcherImpl.java