private ExecutionResult retry() {
    if (retryCount >= MAX_RETRY_COUNT) {
      LOGGER.warn("[ReachMaxRetryCount]");
      stateMachine.transitionToFailed();
      return getStatus();
    }
    LOGGER.warn("error when executing query. {}", stateMachine.getFailureMessage());
    // stop and clean up resources the QueryExecution used
    this.stopAndCleanup(stateMachine.getFailureException());
    LOGGER.info("[WaitBeforeRetry] wait {}ms.", RETRY_INTERVAL_IN_MS);
    try {
      Thread.sleep(RETRY_INTERVAL_IN_MS);
    } catch (InterruptedException e) {
      LOGGER.warn("interrupted when waiting retry");
      Thread.currentThread().interrupt();
    }
    retryCount++;
    LOGGER.info("[Retry] retry count is: {}", retryCount);
    stateMachine.transitionToQueued();
    // force invalid PartitionCache
    planner.invalidatePartitionCache();
    // clear runtime variables in MPPQueryContext
    context.prepareForRetry();
    // re-stop
    this.stopped.compareAndSet(true, false);
    this.resultHandleCleanUp.compareAndSet(true, false);
    // re-analyze the query
    this.analysis = analyze(context);
    // re-start the QueryExecution
    this.start();
    return getStatus();
  }
QueryExecution stores all the status of a query which is being prepared or running inside the MPP
frame. It takes three main responsibilities: 1. Prepare a query. Transform a query from statement
to DistributedQueryPlan with fragment instances. 2. Dispatch all the fragment instances to
corresponding physical nodes. 3. Collect and monitor the progress/states of this query.
iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/QueryExecution.java