AWS SDK を使用して Lambda 関数を呼び出す際の再試行とタイムアウトの問題は、どのようにしてトラブルシューティングすればよいですか?
最終更新日: 2020 年 5 月 28 日
AWS SDK を使用して AWS Lambda 関数を呼び出そうとすると、関数がタイムアウトしたり、関数の実行がハングしたり、または API アクションが重複したりします。これらの問題を解決するにはどうすればよいですか?
簡単な説明
こうした問題は次のような条件で発生する場合があります。
- 応答に時間がかかりすぎる、または到達できないリモート API を呼び出した。
- ソケットがタイムアウトするまで、API コールが応答を受け取れなかった。
- Lambda 関数のタイムアウト期間内に、API コールが応答を受け取れなかった。
注意: ネットワーク接続で問題が発生すると、API コールに想定より長い時間がかかることがあります。ネットワークの問題も、再試行や重複した API リクエストを引き起こす可能性があります。こうした状況に対処するためには、常時、Lambda 関数をべき等にする必要があります。
AWS SDK を使用して API を呼び出し、そのコールが失敗した場合、SDK は自動的にコールを再試行します。SDK がどの程度の長さ、またどの程度の回数再試行を行うかは、SDK ごとに異なる設定により決まります。これらの設定のデフォルト値を以下に示します。
注: 特定の AWS サービスによって一部の値が異なる場合があります。
AWS SDK | 最大再試行回数 | 接続タイムアウト | ソケットタイムアウト |
Python (Boto 3) | サービスにより異なる | 60 秒 | 60 秒 |
JavaScript/Node.js | サービスにより異なる | 該当なし | 120 秒 |
Java | 3 | 10 秒 | 50 秒 |
.NET | 4 | 100 秒 | 300 秒 |
Go | 3 | 該当なし | 該当なし |
再試行とタイムアウトの問題を解決するには、API コールのログを確認して、問題を見つけます。その後、各ユースケースの必要性に合わせて SDK の再試行回数とタイムアウトの設定を変更します。API コールが応答するのに十分な時間を確保するには、Lambda 関数のタイムアウト設定で時間を追加します。
解決方法
SDK により行われた API コールをログに記録する
Amazon CloudWatch Logs を使用すると、失敗した接続と再試行回数についての詳細情報を得ることができます。詳細については、「Amazon CloudWatch の AWS Lambda ログへのアクセス」をご覧ください。または、使用している SDK のインストラクションをご確認ください。
- 「Python の AWS Lambda 関数ログ作成」
- 「AWS SDK for JavaScript 呼び出しのログ記録」
- AWS SDK for Java 呼び出しのログ記録
- Logging with the AWS SDK for .NET
- 「Logging Service Calls」(AWS SDK for Go)
このサンプルエラーログでは、API コールが接続の確立に失敗しています (ソケットのタイムアウト)。
START RequestId: b81e56a9-90e0-11e8-bfa8-b9f44c99e76d Version: $LATEST
2018-07-26T14:32:27.393Z b81e56a9-90e0-11e8-bfa8-b9f44c99e76d [AWS ec2 undefined 40.29s 3 retries] describeInstances({})
2018-07-26T14:32:27.393Z b81e56a9-90e0-11e8-bfa8-b9f44c99e76d { TimeoutError: Socket timed out without establishing a connection
...
このサンプルエラーログでは、接続は成功したものの、応答するまでに長い時間がかかっている (接続タイムアウト):
START RequestId: 3c0523f4-9650-11e8-bd98-0df3c5cf9bd8 Version: $LATEST
2018-08-02T12:33:18.958Z 3c0523f4-9650-11e8-bd98-0df3c5cf9bd8 [AWS ec2 undefined 30.596s 3 retries] describeInstances({})
2018-08-02T12:33:18.978Z 3c0523f4-9650-11e8-bd98-0df3c5cf9bd8 { TimeoutError: Connection timed out after 30s
注意: Lambda 関数がタイムアウトするまでに API コールが応答を取得できなかったときは、これらのログは生成されません。関数のタイムアウトが原因で関数の実行が終了する場合は、以下のいずれかを試してください。
- SDK 内で再試行の設定を変更し、タイムアウト時間内にすべての再試行が実行されるようにします。
- Lambda 関数のタイムアウト設定を一時的に増やし、SDK ログが生成されるのに十分な時間を確保します。
SDK の設定を変更する
SDK の再試行回数とタイムアウト設定では API コールが応答を受け取るのに十分な時間が確保されていなければなりません。各設定に適切な値を判別するには、さまざまな設定をテストして、次の情報を取得します。
- 正常な接続が確立されるまでの平均時間
- 完全な API リクエストにかかる (正常に応答が戻るまでの) 平均時間
- SDK またはコードで再試行を生成する必要があるかどうか
これらの設定の変更に関する詳細については、次の SDK クライアント設定ドキュメントを参照してください。
ランタイムごとにこれらの設定を変更する方法の例を、次に示します。
注: 次のいずれかのコマンド例を使用する前に、各設定のサンプル値をご自身のユースケースで適切な値に置き換えてください。
Python (Boto 3) のサンプル:
# max_attempts: retry count / read_timeout: socket timeout / connect_timeout: new connection timeout
from botocore.session import Session
from botocore.config import Config
s = Session()
c = s.create_client('s3', config=Config(connect_timeout=5, read_timeout=60, retries={'max_attempts': 2}))
JavaScript/Node.js のサンプル:
// maxRetries: retry count / timeout: socket timeout / connectTimeout: new connection timeout
var AWS = require('aws-sdk');
AWS.config.update({
maxRetries: 2,
httpOptions: {
timeout: 30000,
connectTimeout: 5000
}
});
Java のサンプル:
// setMaxErrorRetry(): retry count / setSocketTimeout(): socket timeout / setConnectionTimeout(): new connection timeout
ClientConfiguration clientConfig = new ClientConfiguration();
clientConfig.setSocketTimeout(60000);
clientConfig.setConnectionTimeout(5000);
clientConfig.setMaxErrorRetry(2);
AmazonDynamoDBClient ddb = new AmazonDynamoDBClient(credentialsProvider,clientConfig);
.NET のサンプル:
// MaxErrorRetry: retry count / ReadWriteTimeout: socket timeout / Timeout: new connection timeout
var client = new AmazonS3Client(
new AmazonS3Config {
Timeout = TimeSpan.FromSeconds(5),
ReadWriteTimeout = TimeSpan.FromSeconds(60),
MaxErrorRetry = 2
});
再試行時の Go のサンプル:
// Create Session with MaxRetry configuration to be shared by multiple service clients.
sess := session.Must(session.NewSession(&aws.Config{
MaxRetries: aws.Int(3),
}))
// Create S3 service client with a specific Region.
svc := s3.New(sess, &aws.Config{
Region: aws.String("us-west-2"),
})
リクエストがタイムアウトする場合の Go のサンプル:
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
// SQS ReceiveMessage
params := &sqs.ReceiveMessageInput{ ... }
req, resp := s.ReceiveMessageRequest(params)
req.HTTPRequest = req.HTTPRequest.WithContext(ctx)
err := req.Send()
(オプション) Lambda 関数のタイムアウト設定を変更する
Lambda 関数のタイムアウトを低く設定すると、正常の接続が早期に失われる可能性があります。ご自身のユースケースでこのような状況が発生するときは、関数のタイムアウト設定を増やして、API コールが応答を受け取るのに十分な時間を確保できるようにします。この式を使用して、関数のタイムアウトに必要な基礎時間を推定します。
Retries * (Connection timeout + Socket timeout)
たとえば、SDK で試行回数が 3 回、接続タイムアウトが 10 秒、またソケット接続タイムアウトが 30 秒に設定されているとします。その場合、Lambda 関数のタイムアウトには 120 秒以上の時間を確保します。
3 * (10 + 30) = 120 seconds
残りのコードが実行される余裕をもたせるため、追加で (たとえば、20 秒) のマージンを追加します。
120 + 20 = 140 seconds