外の API を呼ぶとき、タイムアウトと再試行をどう決めますか

タイムアウトは必ず自分で置き、相手のふだんの速さと、自分が利用者に返すまでの持ち時間から決める、再試行は一時的な失敗で、何度送っても困らない呼び出しだけに、間隔を倍々に広げて回数の上限を付けて行う、と答える問い。再試行が重なって相手を倒す危険と、どの層で再試行するかまで話します。

まず 4 択で考える

2 問

答えに入れるべき考えを選びます。選ぶと、すぐに正誤と解説が出ます。そのあと下の欄で、自分の言葉で答えてみます。

  1. 問 1外の API を呼ぶときのタイムアウトの決め方として、正しいものはどれか

  2. 問 2再試行してよい場面として、正しいものはどれか

2 問中 0 問に答えました

答えてみる

面接官に「外の API を呼ぶとき、タイムアウトと再試行をどう決めますか」と聞かれたつもりで、答えを見る前に自分の言葉で、声に出して答えてみます。

解説を読む

聞かれ方から関連するコースまで、この問いの解説を全部ここに置いています。

こう聞かれます

  • 「外の API を呼ぶとき、タイムアウトと再試行をどう決めますか」
  • 「タイムアウトは何秒にしますか。その数字の根拠は」
  • 「失敗したら、とりあえず 3 回やり直す、で十分ですか」(裏から聞く形)

面接官が見ている点

  • タイムアウトを置かないと、相手が遅いときに自分まで止まると分かっているか
  • タイムアウトの数字を、相手の速さと自分の持ち時間から根拠を持って決めるか
  • 再試行してよい失敗と、してはいけない失敗を分けられるか
  • 再試行が重なると相手をさらに追い込む、と分かっているか

短い答え

  • タイムアウトは必ず自分で置きます。相手のふだんの遅いほうの応答時間に少し余裕を足し、自分が利用者に返すまでの持ち時間に収まるように決めます。
  • 再試行するのは、通信の切れや混み合いのような一時的な失敗で、しかも何度送っても困らない呼び出しだけです。入力の誤りのような失敗は、やり直しても同じなので再試行しません。
  • 再試行の間隔は倍々に広げてばらつきを足し、回数と合計の時間に上限を付けます。再試行は 1 つの層でだけ行います。

この問いを含む分野をレッスンで練習する

レッスンを開く

少し詳しい答え

タイムアウトを置かないと何が起きるか

ライブラリによっては、既定の待ち時間がとても長かったり、無かったりします。相手が応答を返さなくなると、自分のサーバーはリクエストを抱えたまま待ち続け、接続やスレッドが埋まって、外の API と関係の無い画面まで遅くなります。外を呼ぶ所には、必ず自分でタイムアウトを置きます。ブラウザや Node.js の fetch なら、AbortSignal.timeout() で打ち切れます。

何秒にするか

根拠は 2 つです。

  • 相手の速さ 監視で、相手の応答時間の遅いほう (上から 1% の人が待つ時間など) を見て、それに少し余裕を足します。平均を基準にすると、ふつうの遅い応答まで切ってしまいます
  • 自分の持ち時間利用者に 2 秒で返すと決めているなら、外の呼び出しと再試行の合計を、その中に収めます。上の層の持ち時間より、下の層のタイムアウトを長くしても意味がありません

つなぐまでの時間と、つないでから応答を待つ時間を分けて置ける道具なら、つなぐまでは短めにします。つながらない相手を長く待つ理由は無いからです。

再試行してよい失敗

再試行してよいのは、次の両方を満たすときです。

  • 一時的な失敗である通信の切れ、タイムアウト、混み合いを表す 429、502、503、504 など。400 や 404 のような入力や権限の誤りは、何度送っても同じなので再試行しません
  • 何度送っても困らない呼び出しである読むだけの呼び出しや、冪等キーを付けた呼び出しです。決済のような呼び出しを鍵なしで再試行すると、二重に処理されることがあります

相手が Retry-After のヘッダーで「これだけ待ってから」と返してきたら、それに従います。

間隔を倍々に広げる

すぐにやり直すと、混んでいる相手をさらに混ませます。間隔を倍々に広げ (指数バックオフ)、上限で止めます。下の例では、待ち時間を 200 ミリ秒から倍にし、2 秒で頭打ちにしています。

JavaScript

function backoffDelays(base, cap, retries) {
  const delays = [];
  for (let i = 0; i < retries; i++) {
    delays.push(Math.min(cap, base * 2 ** i));
  }
  return delays;
}

function shouldRetry(status, idempotent) {
  if (!idempotent) {
    return false;
  }
  return [429, 502, 503, 504].includes(status);
}

console.log(backoffDelays(200, 2000, 5).join(" "));
console.log(shouldRetry(503, true));
console.log(shouldRetry(400, true));
console.log(shouldRetry(503, false));
200 400 800 1600 2000
true
false
false

実際には、この待ち時間にばらつき (ジッター) を足します。多くの呼び出し元が同じ時刻に失敗すると、同じ間隔で一斉にやり直し、また一斉に相手を叩くからです。

再試行は 1 つの層でだけ

画面、自分の API、その先の API、と 3 つの層がそれぞれ 3 回ずつ試すと、いちばん下には最大で 3 × 3 × 3 = 27 回の呼び出しが届きます。相手が弱っているときほど呼び出しが増え、立ち直れなくなります。どの層で再試行するかを 1 つに決め、ほかの層は失敗をそのまま上へ返します。

トレードオフ

タイムアウトを短くすると、自分は早く諦められますが、相手が少し遅いだけの成功も失敗にしてしまいます。再試行を増やすと成功の割合は上がりますが、利用者を待たせ、相手の負担も増えます。外の API が止まっても自分のサービスを動かしたいなら、再試行で粘るより、早く諦めて代わりの表示に切り替えるほうがよいこともあります。

話す例は架空の例で構いません。「外の住所の検索の API が時々遅く、タイムアウトが無くて画面ごと固まったので、応答時間の記録から 1.5 秒に決め、再試行は 1 回だけにした」のように、数字の根拠を添えて話せると伝わります。

よくある間違い

  • タイムアウトを置かない、既定のままにする相手が止まると、自分のサーバーも待ち続けて止まります
  • どんな失敗でも同じ回数やり直す 400 のような失敗はやり直しても同じで、決済のような呼び出しは二重に処理されます
  • 間を空けずに、すぐやり直す混んでいる相手をさらに混ませます。間隔を広げ、ばらつきを足します

深掘りの問い

  • 「タイムアウトした呼び出しは、相手の側で処理されていますか」分かりません。届いて処理されたが応答だけ遅れたのかもしれないので、何度送っても困らない形にしておく、と答えます
  • 「相手が長く止まっているときも、再試行し続けますか」失敗が続いたら呼び出しそのものを一時止める仕組み (サーキットブレーカー) で、相手を休ませる、と答えます
  • 「非同期で後から送ってよい呼び出しなら」キューに入れ、時間をかけて再試行し、諦めた物は別の置き場に移して人が見る、と答えます

確認日 2026年10月30日書き手 小尾 知希 (講師)

技術面接問題の一覧へ戻る