1 つのサービスの遅れが全体に広がるのを、どう防ぎますか

遅い相手を待ち続けて自分の接続や処理の枠が埋まるのが広がる原因だと押さえ、タイムアウトで待ちを切る、失敗が続く相手は呼ぶのを一時止める (サーキットブレーカー)、相手ごとに枠を分ける、溢れた分は早めに断る、代わりの表示で動き続ける、と答える問い。何を止めて何を守るかを先に決めておく所まで話します。

まず 4 択で考える

2 問

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

  1. 問 1おすすめのサービスが遅くなっただけで、購入やログインまで遅くなった。広がった仕組みとして正しいものはどれか

  2. 問 2サーキットブレーカーの動きとして、正しいものはどれか

2 問中 0 問に答えました

答えてみる

面接官に「1 つのサービスの遅れが全体に広がるのを、どう防ぎますか」と聞かれたつもりで、答えを見る前に自分の言葉で、声に出して答えてみます。

解説を読む

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

こう聞かれます

  • 「1 つのサービスの遅れが全体に広がるのを、どう防ぎますか」
  • 「おすすめの表示のサービスが遅くなっただけで、サイト全体が開かなくなりました。なぜですか」
  • 「サーキットブレーカーとは何ですか。どんなときに使いますか」

面接官が見ている点

  • 遅い相手を待つ間に、自分の接続や処理の枠が埋まって広がる、という仕組みを説明できるか
  • タイムアウト、サーキットブレーカー、枠を分ける、早めに断る、代わりの表示、の複数の手を持っているか
  • 止まってもよい機能と、守るべき機能を分けて考えているか
  • ヘルスチェックや再試行が、かえって広げる側に回ることを知っているか

短い答え

  • 遅い相手を待つ間、自分の接続や処理の枠を握ったままになり、それが埋まると関係の無いリクエストまで止まる、というのが広がる仕組みです。
  • だから、タイムアウトで待ちを切り、失敗が続く相手は呼ぶのを一時止め (サーキットブレーカー)、相手ごとに使える枠を分けて、1 つの遅れがほかを巻き込まないようにします。
  • 溢れたリクエストは早めに断り、止まってもよい機能は代わりの表示に切り替えて、守るべき機能 (ログインや購入など) を動かし続けます。

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

レッスンを開く

少し詳しい答え

どう広がるか

商品の画面が、おすすめのサービスを呼んでいるとします。おすすめのサービスが 10 秒かかるようになると、商品の画面のサーバーは、1 つのリクエストのために 10 秒ずつ接続と処理の枠を握ります。枠が埋まると、おすすめと関係の無い購入やログインのリクエストまで待たされ、やがて商品の画面のサーバーも遅くなります。それを呼んでいるほかのサービスも同じように詰まり、遅れが上へ上へと広がります。

さらに、呼び出し元が失敗を見て再試行すると、弱っている相手への呼び出しが増え、立ち直りを遅らせます。

待ちを切る、呼ぶのを止める

まず、外を呼ぶ所すべてにタイムアウトを置き、待ちの長さに上限を付けます。それでも、失敗するとわかっている相手を毎回タイムアウトまで待つのは無駄です。

サーキットブレーカーは、失敗が続いた相手を「開」の状態にして、しばらく呼ばずにすぐ代わりの答えを返す仕組みです。時間がたったら「半開」で 1 回だけ試し、成功すれば「閉」に戻して呼び出しを再開します。失敗が 3 回続いたら開き、3 単位の時間たったら試す、という動きを小さく書くと次の通りです。

JavaScript

function createBreaker(limit, coolDown) {
  let state = "閉";
  let failures = 0;
  let openedAt = 0;

  return function call(time, ok) {
    if (state === "開") {
      if (time - openedAt < coolDown) {
        return time + " 開 呼ばずに代わりの表示";
      }
      state = "半開";
    }
    if (ok) {
      state = "閉";
      failures = 0;
      return time + " 呼んで成功 → 閉";
    }
    failures += 1;
    if (state === "半開" || failures >= limit) {
      state = "開";
      openedAt = time;
      failures = 0;
      return time + " 呼んで失敗 → 開";
    }
    return time + " 呼んで失敗 (" + failures + " 回目)";
  };
}

const call = createBreaker(3, 3);
const results = [false, false, false, true, true, false, true, true, true, true];
results.forEach((ok, time) => console.log(call(time, ok)));
0 呼んで失敗 (1 回目)
1 呼んで失敗 (2 回目)
2 呼んで失敗 → 開
3 開 呼ばずに代わりの表示
4 開 呼ばずに代わりの表示
5 呼んで失敗 → 開
6 開 呼ばずに代わりの表示
7 開 呼ばずに代わりの表示
8 呼んで成功 → 閉
9 呼んで成功 → 閉

開いている間は相手を呼ばないので、自分は待たずに済み、相手は休んで立ち直る時間を得ます。

枠を分ける、早めに断る

  • 相手ごとに枠を分ける接続の数や同時に走る処理の数を、呼ぶ相手ごとに分けておきます。おすすめのサービス向けの枠が埋まっても、決済向けの枠は残ります。船の区画を仕切って、1 つの穴で全体が沈まないようにする考えで、バルクヘッドと呼ばれます
  • 溢れた分は早めに断る自分が受けきれないほどのリクエストが来たら、待たせて全部を遅くするより、一部をすぐに 503 で断るほうが、残りを速く返せます。待ち行列にも長さの上限を付けます

代わりの表示で動き続ける

機能ごとに、止まったときの振る舞いを先に決めておきます。おすすめが出せなければ人気順の固定の一覧を出す、レビューが読めなければ欄を隠す、のようにします。守るべき機能 (ログイン、購入) と、止まってもよい機能を分けておけば、障害のときに何を切り捨てるかで迷いません。

ヘルスチェックにも気をつけます。ヘルスチェックの中で外の相手まで調べると、相手が止まっただけで自分の全台が「不健康」と判定され、ロードバランサから外されてしまいます。自分が動けるかだけを見るようにします。

トレードオフ

サーキットブレーカーは、開く条件が敏感すぎると、少しの揺れで本当は使える相手を止めてしまい、鈍すぎると広がりを防げません。枠を分けると、空いている枠を融通できず、全体の使い方は少し無駄になります。代わりの表示も、作って確かめる手間がかかります。全部の呼び出しに入れるのではなく、止まると全体を巻き込む相手、止まっても代わりがある機能から入れます。

話す例は架空の例で構いません。「通知のサービスが遅くなった日に、投稿の保存まで遅くなったので、通知の呼び出しにタイムアウトとサーキットブレーカーを入れ、通知は後でキューから送る形にした」のように話せると伝わります。

よくある間違い

  • すべての外の呼び出しが、同じ 1 つの枠を使う 1 つの相手の遅れで、枠が全部埋まります。相手ごとに分けます
  • ヘルスチェックで外の相手まで調べる相手が止まると自分の全台が外され、全体が止まります
  • 弱っている相手に、すぐ何度も再試行する呼び出しが増え、立ち直りを遅らせます

深掘りの問い

  • 「同期で呼ばず、非同期にすれば解決しますか」結果をすぐ返さなくてよい処理ならキューに逃がすのは有効ですが、キューが溜まり続けるので、長さの上限と監視が要る、と答えます
  • 「サーキットブレーカーが開いたことに、どう気づきますか」開いた、閉じたを記録と監視に出し、開いている時間が長いときは人を呼ぶ、と答えます
  • 「どの機能を守り、どれを切るかは誰が決めますか」事業側と一緒に、売上や利用者への影響で順番を決め、障害の前に文書にしておく、と答えます

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

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