AWS SDK for JavaScript v3のリトライ設定はmaxAttemptsとretryModeで足りる

  • AWS
  • SDK
  • JavaScript

この記事は、構成と文章の整理にAIを使っています。
あくまで読みやすくするための補助で、内容そのものをAIにすべて任せて書いているわけではありませんが、ところどころ相談しながら整えています。

SDKを使うときに「リトライは指数バックオフ+ジッターにすべき」という話を思い出して、v3で自分で設定する必要があるのか確認した。

結論

  • 指数バックオフとジッターはデフォルトで入っている。自分で書く必要はない
  • 普段触るのは maxAttempts(試行回数)と retryMode(モード)くらい
  • maxAttempts は初回を含む回数。v2の maxRetries より1つ大きい値になる
const client = new DynamoDBClient({
maxAttempts: 3, // デフォルト 3
retryMode: 'standard', // デフォルト。'adaptive' もある
});

standardモードの中身

AWSのSDK共通のリトライ仕様では、standardモードは失敗したリクエストを指数バックオフ+ジッターでリトライする。

  • ネットワークタイムアウトのような一時的なエラーは待ち時間を短めにする
  • ThrottlingException のようなスロットリング系は待ち時間を長めにする
  • リトライクォータ(トークンバケット)があり、リトライのたびにトークンが減って、成功すると戻る
flowchart TD
  A[リクエスト送信] --> B{成功?}
  B -- Yes --> Z[完了]
  B -- No --> C{リトライ可能なエラー?}
  C -- No --> E[エラーを返す]
  C -- Yes --> D{試行回数が maxAttempts 未満?}
  D -- No --> E
  D -- Yes --> F{リトライクォータに余裕がある?}
  F -- No --> E
  F -- Yes --> G[指数バックオフ+ジッターで待つ]
  G --> A

リトライクォータは、障害時にクライアント全体がリトライを連発して状況を悪化させるのを防ぐためのもの。試行回数が残っていても、クォータが尽きればリトライせずにエラーになる。

ジッターは待ち時間にランダムな揺らぎを入れて、複数のクライアントが同じタイミングで一斉にリトライするのを避けるためのもの。v2の頃からフルジッター付きの指数バックオフだった。

maxAttemptsとv2のmaxRetriesの違い

v2は maxRetries(リトライ回数)、v3は maxAttempts(初回込みの試行回数)になった。

v2 v3
maxRetries: 2 maxAttempts: 3

公式の移行ガイドにも maxAttempts は maxRetries + 1 にすること、と書かれている。移行時にそのままの数字を入れると、リトライが1回減る。

retryModeの選択肢

  • standard:デフォルト。基本はこれ
  • adaptive:standardに加えて、クライアント側でスロットリングの状況を見ながら送信レートを調整する
  • legacy:古いリトライロジック。新規では使わない

adaptive は、DynamoDBへの大量書き込みのようにスロットリングが頻発する処理で候補になる。ただしクライアント側でリクエストを待たせることがあるので、レイテンシが読みづらくなる。何となく選ぶものではなさそう。

コード以外に、環境変数や共有設定ファイルでも指定できる。

  • 環境変数:AWS_MAX_ATTEMPTS / AWS_RETRY_MODE
  • ~/.aws/config:max_attempts / retry_mode

待ち時間の計算を変えたいとき

v2の retryDelayOptions はv3では廃止されていて、代わりに retryStrategy を使う。試行回数と待ち時間の計算だけ変えたいなら ConfiguredRetryStrategy がある。

import { S3Client } from '@aws-sdk/client-s3';
import { ConfiguredRetryStrategy } from '@aws-sdk/util-retry';
const client = new S3Client({
retryStrategy: new ConfiguredRetryStrategy(
4, // 最大試行回数
(attempt: number) => 100 + attempt * 1000, // 待ち時間(ms)
),
});

注意点が2つある。

  • maxAttempts と retryStrategy を両方指定すると、retryStrategy が優先される(maxAttempts は無視される)
  • 待ち時間の関数を自分で書くと、ジッターも自分で入れないといけない。上の例は固定の線形なので、ジッターは効いていない

特に理由がなければ、retryStrategy には手を出さずにデフォルトのままにしておくのがいいと思う。

タイムアウトとの関係

リトライとタイムアウトは掛け算で効く。requestTimeout を設定していても、maxAttempts: 3 なら最悪3回分待つ。Lambdaなど実行時間に上限がある環境では、両方をセットで決めておく。