zu-min.com

Quarkus Redis クライアントのタイムアウトがうまく動かなかった話

quarkusredisreactivemutinyunijava

Quarkus の Redis クライアントを使っているのですが、 quarkus.redis.timeout というタイムアウトの設定がうまく効いていない気がするので調査しました。

ドキュメントを読む

まずは quarkus.redis.timeout のドキュメントを眺めます。 これがデフォルト値 10s になっているので 10 秒でタイムアウトするのかなと思っていたのですが、 説明をよく見ると

The maximum delay to wait before a blocking command to Redis server times out

quarkus redis timeout

と、 blocking 専用であることが書かれています。 つまり non-blocking 、言い換えると Reactive では効果がありません。

私の環境では ReactiveRedisClient を使っていたので道理で効かなかったのですね・・・。

コードも読む

コードを追いかけると、 RedisClientImpl.java では timeout 設定を使っている形跡がありますが、 ReactiveRedisClientImpl.java では使用されていません。

代替案

代替案としては SmallRye Fault Tolerance 拡張の @Timeout アノテーションがあります。 SmallRye Fault Tolerance は Mutiny の Uni に対応している1ので、 ReactiveRedisClient と合わせて使用できます。 @CircuitBreaker も一緒に設定すれば失敗が連続した時(=障害時)に無理してアクセスしようとする回数が減り、 タイムアウトまで待たずに早期失敗とできます。

  @CircuitBreaker(
      requestVolumeThreshold = 5,    // 判定に使う直近のリクエスト数
      failureRatio = 0.5,            // 50% 失敗で OPEN
      delay = 10,                    // OPEN になった後 10 秒待機
      delayUnit = ChronoUnit.SECONDS,
      successThreshold = 3           // HALF-OPEN → CLOSED に必要な連続成功数
  )
  @Timeout(value = 5, unit = ChronoUnit.SECONDS)  // 5秒でタイムアウト
  public Uni<String> get(String key) {
      return commands.get(key);
  }

まとめ

ちゃんとドキュメントを読もうという話でした。

また、最近は Virtual Threads 対応が進んでいますので、 non-blocking API を使い続けるよりも @RunOnVirtualThreads と blocking API に切り替えた方がいいのかもしれません。

Footnotes

  1. SmallRye Fault Tolerance adds support for additional asynchronous types:

    • Mutiny: Uni
    • RxJava 3: Single, Maybe, Completable

    https://smallrye.io/docs/smallrye-fault-tolerance/6.2.1/reference/asynchronous.html#async-types

関連記事