PaPoo
cover
technews
Author
technews
世界の技術ニュースをリアルタイムでキャッチし、日本語でわかりやすく発信。AI・半導体・スタートアップから規制動向まで、グローバルテックシーンの「今」をお届けします。

Retry StormsでAPIが落ちる理由と、その防ぎ方をわかりやすく解説

この記事のキーポイント

まず結論:APIは「守ろうとしすぎる」と逆に壊れる

今回の記事のテーマは、​Retry Storms(再試行の嵐)​ です。
名前は少し物騒ですが、やっていることは単純で、「失敗したらもう一回投げる」をみんなが同時にやりすぎて、システム全体を押しつぶしてしまう、という話です。

これ、かなり“あるある”だと思います。
人間の感覚だと「1回失敗したなら、もう1回試せばいいじゃない」と思いがちです。実際、それで助かるケースは多い。でも、​遅くなっている時に再試行を積み増すと、救済策が加害者に変わるんですよね。ここが本当に面白くて、怖いところです。

元記事では、API-led architecture(APIを中心にした構成)を前提に、次のような機能がそれぞれ“善意”で入っていると説明します。

どれも単体なら正しい。でも、​ストレス下では同時に暴走しうる
この記事の主張はそこに尽きます。

Retry Storms:1回の失敗が3回分、9回分の負荷になる

Retryは「一時的なタイムアウトなら、もう一度試す」という仕組みです。
たとえば、あるサービスが一時的に遅くなっただけなら、再試行でうまくいくことがあります。これは確かに便利です。

ただし問題は、​失敗したリクエストが、失敗したままでは終わらないこと。
1回のリクエストが3回までRetryされると、単純に考えて負荷は3倍です。しかも、それが複数レイヤーで起きると掛け算になります。

元記事の例では、こんな流れが描かれています。

この構成で各層が独立にRetryすると、1つの遅延が上流まで増幅します。
たとえば下流のDBが少し遅くなっただけで、上流のAPIが「まだ待てる、もう1回」と再試行し、その再試行がさらに下流を圧迫する。まさに悪循環です。

個人的には、ここは分散システムの“性格の悪さ”がよく出る部分だと思います。
単体では良い挙動が、組み合わせると最悪になる。ソフトウェア設計って、こういうところが本当に油断できません。

どう防ぐのか

記事では、Retryは次のようにbounded(上限付き)​にすべきだとしています。

特にJitterは地味ですが重要です。
全員が同じタイミングで再試行すると、再び一斉攻撃みたいになるからです。
「ちょっと待ってから、しかも人それぞれズラして再挑戦する」という発想は、かなり賢いと思います。

Replicationは安心材料だが、同期しすぎると詰まる

Replicationは、データを複数の場所に複製することで、壊れにくくする仕組みです。
これも一見すると万能に見えます。データが飛んでも大丈夫そうですし、冗長性も上がる。いいことづくめに見えます。

でも元記事は、​同期的なReplicationにはコストがあると指摘します。
書き込みのたびに複数レプリカへ同時反映するなら、そのぶん待ち時間も増えるし、調整の手間も増える。

つまり、耐久性を上げるつもりが、​書き込みのボトルネックになるわけです。

記事の例では、書き込みが3つのReplicaにファンアウトし、トラフィックが増えるとレプリカ遅延が起き、クライアントがRetryし、結果として書き込み負荷がさらに増える、という流れが示されています。

これは、業務システムではかなり厄介です。
注文処理、請求、突合(reconciliation)みたいな領域では、「データが消えないこと」は大事ですが、同時に「処理が止まらないこと」も大事です。
どちらか片方だけを最大化すると、もう片方が死ぬことがある。ここは設計者泣かせですね。

どう考えるべきか

記事は、​全部のデータに同じレベルの耐久性を求めないことを勧めています。

これはかなり現実的です。
「全部を最高品質で」ではなく、「大事なものにだけ重い保証をかける」。
個人的には、こういう優先順位ベースの設計が、実運用では一番強いと思います。

Autoscalingは救世主にも爆弾にもなる

Autoscalingは、アクセスが増えたらインスタンスを増やす仕組みです。
クラウド時代の希望みたいな機能ですが、元記事はここにも落とし穴があると言います。

問題は、​Retryで増えた負荷を、本物の需要だと誤認することです。
たとえば本当は100件のアクセスしかないのに、Retryで300件に見えていたら、システムは「うわ、需要が急増してる!」と判断してスケールアウトするかもしれません。

でも新しいインスタンスを立ち上げると、今度はそれ自体がDBやキャッシュに負荷をかける。
その結果、さらに遅くなる。さらにRetryが増える。
つまり、​スケーリングが不安定さを加速するわけです。

これ、なかなか残酷です。
“助けようとした仕組み”が“混乱を広げる仕組み”になる。
分散システムは、こういうフィードバックの連鎖が本当に怖い。

何を見てスケールすべきか

元記事では、スケール判断を次のような信号に寄せるべきだとしています。

要するに、​見せかけのトラフィックではなく、本物のトラフィックを見るということです。
これ、当たり前のようで難しいんですよね。観測値はいつもノイズだらけなので。

image_0003.svg

本当の問題は「相互作用」だと記事は言う

この部分がこの記事の核心です。
Retry、Replication、Autoscaling、Circuit breakerは、それぞれ別の問題に反応しています。

でも実際の障害時には、これらが同じ原因から同時に揺れ始めることがあります。
すると、個別には正しい反応でも、全体としては不安定なループになる。

記事では、分散システムは「フィードバックシステム」だと表現しています。
これはかなり本質的だと思います。
ソフトウェアは、ただの箱の集まりではなく、互いの出力が互いの入力になる“生き物”みたいなものなんですよね。

例:支払い突合APIが小さな遅延から大事故に変わる

元記事では、payment reconciliation API のシナリオが紹介されています。

流れはこんな感じです。

ここでERPが少し遅くなるとします。
すると:

  1. ERP latency が 700ms に上がる
  2. Billing が 500ms で timeout
  3. Billing が 3回 retry
  4. Process API も orchestration を retry
  5. Gateway も client request を retry
  6. Autoscaling がスパイクに反応
  7. DB replication lag が増える
  8. DLQ(Dead Letter Queue:処理失敗したメッセージの逃がし先)が増える

結果、小さな遅延がプラットフォーム全体の障害に変わる。
これ、かなりリアルです。単なる「遅い」から始まって、全体が勝手に大騒ぎする感じ。運用担当からすると悪夢だと思います。

では、どう守るのか:Bounded Reliabilityの考え方

元記事は、信頼性を“最大化”するのではなく、​bounded reliability(上限付きの信頼性)​として設計すべきだと述べます。
要するに、「どこまでも頑張る」のではなく、「ここまでは守る、ここから先はあきらめる」を決めることです。

1. Retry Budgetを決める

Retry Budgetは、Retryに使っていい“予算”のようなものです。
たとえば incoming RPS が 1,000、Retry count が 3 なら、effective load は 3,000 になります。
つまりRetryは、ただの保険ではなく負荷の増幅装置なんです。

だから、サービス全体で何回までRetryしてよいかを制限する必要があります。

2. エラーを見分ける

元記事は、​全部のエラーをRetryしてはいけないと強調しています。

これはかなり実務的です。
ValidationエラーをRetryしても、入力内容が間違っているだけなので直りません。
Authエラーも、再試行したところで認可が通るわけではない。
この当たり前を徹底するのが、意外と難しいんですよね。

3. Idempotencyを守る

Idempotency(冪等性)は、「何回やっても同じ結果になる」性質です。
Retryするなら、これがないと危険です。

たとえば決済や注文登録で、同じリクエストを2回送ったら2重処理になると困ります。
そのため、記事では transaction_id や correlation-id のような識別子を使う例が示されています。

ここは本当に重要です。
Retryは“同じことを何度もやる”仕組みなので、​同じことを何度やっても安全である必要があります。
この原則がないと、リトライは保険ではなく事故の原因になります。

4. DLQとObservabilityをセットで持つ

DLQは、失敗したメッセージを一旦避難させる場所です。
ただ置くだけでは足りず、観測が必要だと記事は言います。

見るべき指標はたとえば:

P95 latency は、ざっくり言うと「遅い側から数えて5%の手前の値」です。
平均値よりも、体感に近い“遅さ”を捉えやすいので、運用でよく使われます。

まとめ:完璧を目指すより、壊れ方を設計する

この記事を読んで強く感じたのは、​信頼性の仕組みは、足し算すると安心ではなくなるということです。
RetryもReplicationもAutoscalingも、ひとつひとつは正義です。
でも、上限なく組み合わせると、システムは“守られる”どころか“追い込まれる”。

だから大事なのは、こんな発想だと思います。

個人的には、この文章のメッセージはかなり刺さりました。
「止まらない仕組みを作る」より、「止まり方を穏やかにする」ほうが、分散システムではずっと重要ではないかと思います。
理想論より現実論。派手さはないけれど、運用ではそっちが勝つ。そんな記事でした。


参考: How Retry Storms Crash API-Led Systems

同じ著者の記事