過負荷になったシステムはなぜ回復しないのか

赤い光の軌跡がサーバーラックに繰り返し戻り、リトライストームを表すイラスト

アクセスのピークを過ぎても、APIのタイムアウトが減らないことがある。新規リクエストが減った後も、失敗したリクエストの再試行が、詰まったDBに仕事を送り続けるからだ。

リトライが過負荷を維持する

短時間のアクセス急増でDBの応答が遅れ、APIがタイムアウトし始める。呼び出し元が再試行するとDBに仕事が増える。タイムアウトした元の処理がDB側でまだ走っていれば、元の処理と再試行が重なる。追加の仕事で応答がさらに遅れ、次のタイムアウトと再試行を招く。

再試行が集中して負荷を増幅する現象をリトライストームと呼ぶ。この例では、アクセス急増が引き金で、リトライストームが障害を維持する仕組みだ。引き金が消えた後も、この循環で過負荷が続く状態を準安定障害(metastable failure)という。

負荷制御の手法はどこに効くか

手法どこに効くか
負荷分散余力のあるレプリカに仕事を振り分ける。全レプリカが同じDBを使うなら、振り分け先を変えてもDBに届く仕事の総量は減らない。
バックプレッシャー下流の処理能力に合わせて上流を減速させる。例えば、同時実行枠が空くまでワーカーが次のジョブを取り出さない。
ロードシェディング処理に入る前に一部のリクエストを断る。例えば、同時実行数が上限に達したら、低優先度の処理を早めに拒否する。
トークンバケット一定の速度でトークンを補充し、処理を許可するたびに消費する。補充速度で継続的な許可レートを、バケット容量で短時間のバーストを制御する。再試行専用にも使える。
指数バックオフ+ジッター再試行するたびに待ち時間を伸ばし、ランダムなばらつきを入れて再試行が集中するのを防ぐ。再試行の回数や総量には上限も設ける。
サーキットブレーカー失敗が続く依存先への呼び出しを一時的に遮断し、時間を置いて少数の試行を通し、回復を確かめる。

APIゲートウェイとサービスクライアントがそれぞれ最大3回試行する構成では、元のリクエスト1件から下流への呼び出しが最大9回発生する。リトライを担当する層を決めておくと、試行回数が層ごとに掛け合わさるのを防げる。

回復するまで流入を絞る

平常時にさばける負荷と、障害中に回復できる負荷は違う。GoogleのSRE本には、平常時は毎秒1万件を処理できるサービスでも、正常なサーバーが1割まで減ると、復旧のために毎秒1,000件程度まで流入を下げる必要があるという例がある。

DBが詰まった例なら、まず再試行や低優先度の処理を絞り、DBに届く仕事を減らす。新規リクエスト数と再試行数を別々に数えると、どこから仕事が増えているかが見える。成功処理数とレイテンシが持ち直し、キューの滞留が減り始めたら、再び詰まらないかを見ながら流入を段階的に戻す。

参考資料

データ指向アプリケーションデザイン 第2版
Metastable Failures in the Wild
Google SRE — Addressing Cascading Failures
AWS Well-Architected Framework — Control and limit retry calls

おすすめの書籍

書籍 · アフィリエイトリンク
データ指向アプリケーションデザイン 第2版 ―信頼性、拡張性、保守性の高い分散システム設計の原理
この記事のきっかけになった本です。過負荷への対処を、分散システムの信頼性や拡張性の設計とあわせて学べます。
Amazonで書籍を見る

コメントを残す

メールアドレスが公開されることはありません。 ※ が付いている欄は必須項目です