aws-sdk-javaのClientConfigurationのリトライポリシーはDynamoDBだけ異なる

タイトルの通りですが、aws-sdk-javaで利用するClientConfigurationのリトライポリシーのデフォルトは、DynamoDBだけ異なるようです。

DynamoDBを利用していて、500エラーに遭遇する機会が増えてきており、その際に調べていて知りました。

はじめに

AWS の利用全般における、エラー再試行と、再試行におけるエクスポネンシャルバックオフとジッターについては、下記の公式ドキュメントにまとめられています。

docs.aws.amazon.com

また、その中でも DynamoDB については、下記のブログ記事にまとめられています。
この記事、かなり詳しく書かれてますので、DynamoDB を使っている場合、一読しておくとよさそうです。

aws.amazon.com

リトライポリシーの違いについて

先に結論ですが、デフォルトのリトライポリシーの設定値を以下の表にまとめました。違いは「エラー時のリトライ試行数」と「ベース遅延(非スロットル系エラー)」ですね。

DynamoDB それ以外
エラー時のリトライ試行数 10回 3回
ベース遅延(非スロットル系エラー) 25ミリ秒 100ミリ秒
ベース遅延(スロットル系エラー) 500ミリ秒 500ミリ秒
最大遅延 20秒 20秒

(コードを読んで確認しただけなので、見逃しはあるかもしれません)

ClientConfigurationとリトライポリシー

aws-sdk-java において、各AWSサービスへのアクセスを行う各クライアントは、 ClientConfiguration に指定された設定が適用されます。
リトライポリシーについてもそれらの設定の一部となっています。

デフォルト値については、GitHub を見るのが確実だと思います。

https://github.com/aws/aws-sdk-java/blob/1.11.974/aws-java-sdk-core/src/main/java/com/amazonaws/ClientConfiguration.java

リトライポリシーについては、 PredefinedRetryPolicies.DEFAULT がデフォルトなようですね。

public static final RetryPolicy DEFAULT_RETRY_POLICY = PredefinedRetryPolicies.DEFAULT;

ところで、クライアントの作成は、各サービスごとに用意されたビルダーを使って生成することが多いと思います。

DynamoDB であれば次のように AmazonDynamoDBAsyncClientBuilder を使う形ですね(実際にはメソッドチェーンを使った流れるようなインタフェースで書くことが多いですが、説明のために分けて書いています)

AmazonDynamoDBAsyncClientBuilder builder = AmazonDynamoDBAsyncClientBuilder.standard();

// builder に対して Credentials, Region, ClientConfiguration 等をセットしていく

AmazonDynamoDB client = builder.build();

こちらのビルダー内部では、ClientConfiguration を作成するファクトリを使っており、DynamoDB の場合は AmazonDynamoDBClientConfigurationFactory が該当します。

https://github.com/aws/aws-sdk-java/blob/1.11.974/aws-java-sdk-dynamodb/src/main/java/com/amazonaws/services/dynamodbv2/AmazonDynamoDBClientConfigurationFactory.java

上記クラスを見るとわかりますが、リトライポリシーだけ、 PredefinedRetryPolicies.DEFAULT ではなく PredefinedRetryPolicies.DYNAMODB_DEFAULT という専用のデフォルト値を使うように指定されています。

    @Override
    protected ClientConfiguration getDefaultConfig() {
        return super.getDefaultConfig().withRetryPolicy(PredefinedRetryPolicies.DYNAMODB_DEFAULT);
    }

こういった実装になっていることで、エラー時のリトライ試行数、ベース遅延(非スロットル系エラー)に関して、DynamoDBとそれ以外でデフォルト値が異なっています。

なぜデフォルト値がDynamoDBだけ異なるか?

これは、公開されている情報だけではっきりとした理由はわかりませんでした。
特性上、短期間でのリトライを繰り返すことで、自己修復によってリクエストが成功する可能性の高いサービスだから、ということでしょうか…。

実際に使っていく場合は、デフォルトの設定だけでなく、運用していくワークロードに応じて適切な設定値を決めていくことが大事だとは思いますが、こういった違いがあることを知っておくと設定値を決める参考にもなるかしれません。

ClientConfigurationの上書き設定時は注意

リトライポリシー以外にもタイムアウトの設定など ClientConfiguration は様々な設定を定義できます。
そのため、リトライポリシーはデフォルト値を使っていても、無意識のうちにリトライポリシーが PredefinedRetryPolicies.DYNAMODB_DEFAULT から PredefinedRetryPolicies.DEFAULT に入れ替わっていないか、気をつけるとよさそうです。

例えば、以下のコードでは、独自に new した ClientConfiguration をセットするため、リトライポリシーは PredefinedRetryPolicies.DEFAULT になります。

ClientConfiguration config = new ClientConfiguration()
        .withConnectionTimeout(30 * 1000);

AmazonDynamoDB client = AmazonDynamoDBAsyncClientBuilder.standard()
        .withClientConfiguration(config)
        .build();

回避する場合は、下記のようにリトライポリシーも明示的に再設定してあげましょう。

ClientConfiguration config = new ClientConfiguration()
        .withRetryPolicy(PredefinedRetryPolicies.DYNAMODB_DEFAULT)
        .withConnectionTimeout(30 * 1000);

AmazonDynamoDB client = AmazonDynamoDBAsyncClientBuilder.standard()
        .withClientConfiguration(config)
        .build();