こう 聞かれます
- 「外の API を
呼ぶとき、 タイムアウトと 再試行を どう 決めますか」 - 「タイムアウトは
何秒にしますか。 その 数字の根拠は」 - 「失敗したら、
とりあえず 3 回やり直す、 で十分ですか」 (裏から聞く形)
面接官が 見ている点
- タイムアウトを
置かないと、 相手が 遅い ときに 自分まで 止まると 分かっているか - タイムアウトの
数字を、 相手の 速さと 自分の 持ち時間から 根拠を 持って決めるか - 再試行して
よい 失敗と、 してはいけない 失敗を 分けられるか - 再試行が
重なると 相手を さらに 追い 込む、 と分かっているか
短い答え
- タイムアウトは
必ず 自分で 置きます。 相手の ふだんの 遅い ほうの 応答時間に 少し 余裕を 足し、 自分が 利用者に 返すまでの 持ち時間に 収まるように 決めます。 - 再試行するのは、
通信の 切れや 混み合いのような 一時的な 失敗で、 しかも 何度送っても 困らない 呼び出しだけです。 入力の 誤りのような 失敗は、 やり直しても 同じなので 再試行しません。 - 再試行の
間隔は 倍々に 広げてばらつきを 足し、 回数と 合計の 時間に 上限を 付けます。 再試行は 1 つの 層でだけ 行います。
この
少し 詳しい答え
タイムアウトを 置かないと 何が起きるか
ライブラリにAbortSignal.timeout() で
何秒にするか
根拠は 2 つです。
- 相手の速さ 監視で、
相手の 応答時間の 遅い ほう (上から 1% の 人が 待つ時間など ) を 見て、 それに 少し 余裕を 足します。 平均を 基準に すると、 ふつうの 遅い 応答まで 切ってしまいます - 自分の持ち時間利用者に 2 秒で
返すと 決めているなら、 外の 呼び出しと 再試行の 合計を、 その 中に 収めます。 上の 層の 持ち時間より、 下の 層の タイムアウトを 長くしても 意味が ありません
つなぐまでの
再試行してよい失敗
再試行して
- 一時的な
失敗である 通信の 切れ、 タイムアウト、 混み合いを 表す 429、 502、 503、 504 など。 400 や 404 のような 入力や 権限の 誤りは、 何度送っても 同じなので 再試行しません - 何度送っても
困らない 呼び出しである 読むだけの 呼び出しや、 冪等キーを 付けた 呼び出しです。 決済のような 呼び出しを 鍵なしで 再試行すると、 二重に 処理される ことがあります
相手が Retry-After の
間隔を 倍々に広げる
すぐに
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 つの層でだけ
画面、
トレードオフ
タイムアウトを
話す例は
よく ある間違い
- タイムアウトを
置かない、 既定のままにする 相手が 止まると、 自分の サーバーも 待ち続けて 止まります - どんな
失敗でも 同じ 回数やり直す 400 のような 失敗は やり直しても 同じで、 決済のような 呼び出しは 二重に 処理されます - 間を
空けずに、 すぐやり直す 混んでいる 相手を さらに 混ませます。 間隔を 広げ、 ばらつきを足します
深掘りの問い
- 「タイムアウトした
呼び出しは、 相手の 側で 処理されていますか」 分かりません。 届いて 処理されたが 応答だけ 遅れたのかもしれないので、 何度送っても 困らない 形に しておく、と答えます - 「相手が
長く 止まっている ときも、 再試行し続けますか」 失敗が 続いたら 呼び出し その ものを 一時止める 仕組み (サーキットブレーカー) で、 相手を 休ませる、と答えます - 「非同期で
後から 送って よい 呼び出しなら」キューに 入れ、 時間を かけて 再試行し、 諦めた物は 別の 置き場に 移して 人が 見る、と答えます
