@Override
    public Action handleAttemptFailure(Event event) {
      // Ratis guarantees that event.getCause() is instance of IOException.
      // We should allow RaftException or IOException(StatusRuntimeException, thrown by gRPC) to be
      // retried.
      Optional<Throwable> unexpectedCause =
          Optional.ofNullable(event.getCause())
              .filter(RaftException.class::isInstance)
              .map(Throwable::getCause)
              .filter(StatusRuntimeException.class::isInstance);

      if (unexpectedCause.isPresent()) {
        logger.info(
            "{}: raft client request failed and caught exception: ", this, unexpectedCause.get());
        return NO_RETRY_ACTION;
      }

      return defaultPolicy.handleAttemptFailure(event);
    }
RatisRetryPolicy is similar to ExceptionDependentRetry 1. By default, use
ExponentialBackoffRetry to handle request failure 2. If unexpected IOException is caught,
immediately fail the request and let application choose retry action.

<p>potential IOException can be categorized into expected and unexpected 1. expected, instance
of RaftException, like LeaderNotReady / GroupMisMatch etc. 2. unexpected, IOException which is
not an instance of RaftException
iotdb-core/consensus/src/main/java/org/apache/iotdb/consensus/ratis/RatisClient.java