AWS SDK for JavaScript v3のタイムアウトはrequestHandlerで設定する

  • AWS
  • SDK
  • JavaScript
  • Node.js

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

v2では httpOptions: { timeout, connectTimeout } で設定していたタイムアウトが、v3ではどこに行ったのか確認した。書き方が変わっただけでなく、デフォルトの挙動にも注意点があった。

結論

  • v3ではクライアントの requestHandler に渡す
  • v3.521.0以降はオブジェクトを渡すだけでいい(NodeHttpHandler をimportしなくていい)
  • connectionTimeout / requestTimeout / socketTimeout はどれもデフォルト0で、タイムアウトなし
  • requestTimeout は超えても警告が出るだけ。例外にしたいなら throwOnRequestTimeout: true(3.910.0以降)
const client = new DynamoDBClient({
requestHandler: {
connectionTimeout: 5_000,
requestTimeout: 10_000,
throwOnRequestTimeout: true,
},
});

v2からの対応

v2の httpOptions はv3では丸ごと requestHandler に移った。Node.jsなら NodeHttpHandler、ブラウザなら FetchHttpHandler の設定になる。

v2 (httpOptions) v3 (requestHandler)
connectTimeout connectionTimeout
timeout requestTimeout
agent httpAgent / httpsAgent
proxy agent側でプロキシを設定

ちなみに timeout の移行先は資料によって書き方が違っていた。GitHubのUPGRADING.mdでは requestTimeout、AWS公式の移行ガイドのページでは socketTimeout になっている。現在の socketTimeout は「ソケットがアイドルのまま許される最大時間」という意味なので、v2の「リクエスト全体の時間」に相当するのは requestTimeout のほうだと思う。

3つのタイムアウトの違い

sequenceDiagram
  participant C as SDK Client
  participant S as AWS Service
  Note over C,S: connectionTimeout:接続確立まで
  C->>S: TCP/TLS 接続
  S-->>C: 接続完了
  Note over C,S: requestTimeout:リクエスト送信〜レスポンス受信まで
  C->>S: HTTP リクエスト
  S-->>C: HTTP レスポンス
  Note over C,S: socketTimeout:ソケットが無通信のまま許される時間
  • connectionTimeout:接続フェーズにかけていい最大時間。超えたら接続を諦める
  • requestTimeout:リクエストとレスポンスにかけていい最大時間
  • socketTimeout:ソケットがアイドルのまま保持される最大時間。サーバー側がアイドル接続を切ることには影響しない

長い書き方と短い書き方

v3.521.0より前は、NodeHttpHandler と https.Agent をimportしてインスタンスを渡す必要があった。

import { Agent } from 'node:https';
import { NodeHttpHandler } from '@smithy/node-http-handler';
const client = new DynamoDBClient({
requestHandler: new NodeHttpHandler({
httpsAgent: new Agent({ keepAlive: true, maxSockets: 100 }),
connectionTimeout: 5_000,
requestTimeout: 10_000,
}),
});

v3.521.0以降はオブジェクトを渡すと、それがコンストラクタのパラメータとして解釈される。

const client = new DynamoDBClient({
requestHandler: {
httpsAgent: { maxSockets: 100 },
connectionTimeout: 5_000,
requestTimeout: 10_000,
},
});

普段はこっちで十分。ただ、GitHubのissue(#7076)で、短い書き方で httpsAgent の中に timeout を入れたときに期待通り動かないという報告があった。agentを細かく触りたい場合は長い書き方にしたほうが確実かもしれない(実機未確認)。

requestTimeoutは超えても止まらない

ここが一番ハマりそうなところだった。

requestTimeout を超えても、デフォルトでは警告が出るだけで例外は投げられない。3.910.0で追加された throwOnRequestTimeout: true を指定したときだけ TimeoutError になる。

「タイムアウト設定したのにリクエストが終わらない」「catchに入らない」という場合は、まずSDKのバージョンとこのオプションを確認する。

ブラウザの場合

ブラウザでは FetchHttpHandler が使われる。こちらには connectionTimeout と socketTimeout がなく、使えるのは requestTimeout だけ。

import { FetchHttpHandler } from '@smithy/fetch-http-handler';
const client = new S3Client({
requestHandler: new FetchHttpHandler({ requestTimeout: 30_000 }),
});

Lambdaで使うときに考えること

デフォルトが0(無効)なので、何も設定しないとSDKの呼び出しはLambdaのタイムアウトまで待ち続けることになる。

SDKはリトライもするので、最悪ケースの待ち時間はざっくり「試行回数 × requestTimeout + リトライ間の待ち時間」になる。これがLambdaのタイムアウトより短くなるように決めておくと、SDK側でエラーを拾ってログを出せる。