プロンプトとコンテキスト
この質問は、「毎分Nリクエスト」という要件を運用可能なコントロールプレーンとデータプレーンに落とし込めるかをテストするものです。リミッターは公平性とビジネスの優先順位を維持しながらキャパシティを保護しますが、分散ノードと共有状態は近似誤差をもたらします。ポリシー、アルゴリズム、ストレージ、レスポンス、フォールバック、ロールアウト、およびメトリクスを網羅して回答してください。
面接官がテストしていること
優れた回答では、トークンバケット、リーキーバケット、固定ウィンドウ、スライディングウィンドウを比較する前に、ディメンション、ウィンドウ、バースト、および障害セマンティクスを明確にします。ポリシーの配布とリクエストごとの判定を分離し、アトミックカウンター、ホットキー、マルチリージョンでの動作、Redisの障害について説明し、実用的なRetry-Afterシグナルを返します。また、シャドウロールアウト、エラーバジェット、監査されたバイパスについても議論します。
明確にすべき質問
- キーはAPI、テナント、ユーザー、IP、またはそれらの組み合わせですか?プレミアムカスタマーは優先されますか?
- 制御するのは持続的なレート、バーストサイズ、並行性、または複数のリソースですか?
- 上限を超えたリクエストは失敗、キューイング、デグレード、あるいは少量の超過許容のいずれになりますか?グローバルな整合性は必要ですか?
- リミッターが利用できない場合、フェイルオープンとフェイルクローズのどちらにすべきですか?どのルートが高リスクですか?
- ポリシーの変更頻度はどのくらいですか?段階的なロールアウト、監査、ロールバック、および即時反映は必要ですか?
30秒の回答フレームワーク
「ポリシー制御とリクエスト時の判定を分離します。テナント、ユーザー、ルートを正規化してキーを作成し、データプレーンはRedis Luaでアトミックなトークンバケットを実行して、残りのクォータと再試行時間を返します。バージョン管理されたポリシーはキャッシュされます。ローカルキャッシュで高速な拒否判定を行いつつ、共有状態が最終決定を下します。短い共有ストレージの障害中、低リスクのルートはテナントの緊急バジェットを使用してフェイルオープンし、高リスクのルートはフェイルクローズしてページャーを発報します。シャドウモード、バーンレート、ホットキーのメトリクス、リージョンごとのエラー境界を基にロールアウトを進めます。」
ステップバイステップの詳細な回答
ステップ 1: キャパシティと公平性の定義
持続的なレート、バーストサイズ、並行性を分離します。テナントクォータは1つの顧客がグローバルプールを消費し尽くすのを防ぎ、ユーザークォータはテナント内のノイズを封じ込めます。重要なルートには専用のバジェットを割り当てることができます。公平性とはビジネス上のポリシーであり、アルゴリズムが偶発的にもたらすデフォルトではありません。
ステップ 2: アルゴリズムの選択
トークンバケットは制御されたバーストを許容し、APIに適しています。リーキーバケットは出力を平滑化します。スライディングウィンドウは直感的ですが、より多くの状態コストがかかります。Redisで状態を共有する場合、アトミックなスクリプト内でRedisのTIMEを使用してすべてのノードが同じクロックを使用するようにし、経過時間を0でクランプし、状態のクリーンアップと精度の境界を定義します。
ステップ 3: キーとポリシーのモデリング
メソッド、ルート、テナント、プリンシパル、リージョンを正規化し、ノードが同じキーを導出できるようにします。ポリシーには、制限値、バースト、スコープ、優先度、バージョン、有効時間、オーナーが含まれます。不明なポリシーは安全なベースラインにフォールバックし、クライアントが独自のクォータを指定することはできません。
ステップ 4: 判定のアトミック化
単一の共有操作で、状態の読み取り、トークンの補充、コストの差し引き、TTLの更新を行います。Redis Lua、アトミックなデータベース操作、またはサイドカーが機能します。鍵となるのは、読み取り後の書き込み(read-then-write)による競合を避けることです。残りのトークン数、リセット時間、およびポリシーバージョンを返します。
ステップ 5: ホットキーとマルチリージョンの処理
人気のあるテナントは、単一のキーをホットスポットに変えてしまう可能性があります。そのバジェットをシャード化し、許容可能な超過境界の範囲内である場合にのみコーディネーターでマージします。マルチリージョンシステムでは、リージョンごとのクォータと非同期のグローバル集約を使用できます。厳格なグローバル整合性は可用性とレイテンシの犠牲を伴います。
ステップ 6: 障害とフォールバックの設計
ルートのリスクに応じてフェイルオープンまたはフェイルクローズを選択し、ローカルの緊急バジェットを設定します。ポリシーストレージが利用できない場合は、短いTTLを持つ最新バージョンを使用します。復旧時に蓄積されたすべての需要を一度に解放してはなりません。すべてのバイパスおよびデグレードされた決定を監査します。
ステップ 7: 変更のロールアウト
まず新しいポリシーをシャドウモードで実行し、予測される拒否数を比較した上で、テナント単位またはパーセンテージで有効化します。ログにポリシーバージョンを含め、ロールバックの準備を整えておきます。すべての一時的な除外措置には、オーナーと有効期限が必要です。
ステップ 8: 結果の測定
許可/拒否レート、残りのクォータ、p95の判定レイテンシ、ホットキー、ストレージエラー、ポリシーバージョン、誤拒否の申し立て、バックエンドの過負荷を監視します。これらを5xxエラー、キューの深さ、テールレイテンシと関連付けます。制限が緩すぎると保護に失敗し、厳しすぎるとビジネスを損ないます。
アトミックなトークンバケットの疑似コード
now = redis_time()
state = load(key) or {tokens: burst, at: now}
elapsed = max(0, now - state.at)
state.tokens = min(burst, state.tokens + elapsed * rate)
allowed = state.tokens >= cost
if allowed:
state.tokens -= cost
state.at = now
save_atomically(key, state, ttl)
return allowed, state.tokens, retry_after(state)トレードオフと境界条件
| 選択肢 | 適している用途 | 主なコスト |
|---|---|---|
| トークンバケット | バースト性のあるAPI | 共有アトミック状態 |
| ローカルリミッター | 最も低レイテンシな保護 | 複数ノード間での不正確なクォータ |
| リージョンクォータ | マルチリージョンの可用性 | グローバルな公平性の誤差 |
| フェイルクローズ | 決済および認証 | リミッターの障害が可用性に影響 |
レート制限はキューではなく、キャパシティプランニング、サーキットブレーカー、認証の代わりになるものでもありません。キューは待機可能なリクエストに適しており、リミッターは過負荷をレイテンシとして隠すのではなく、リソースの境界で迅速に拒否します。
ロールアウト計画と根拠
単一の高トラフィックAPI向けにポリシーモデルとトークンバケットデータプレーンを構築し、シャドウモードを1日実行してから、少数のテナントで有効化します。Microsoft Well-Architectedではスロットリングをアクティブな過負荷制御として扱っており、DataInterviewやSystem Design Schoolの資料ではアルゴリズムの選択、共有状態、およびRetry-Afterが重視されています。
パイロット運用の終了基準
トラフィック急増、共有ストレージの障害、ロールバック、および高負荷テナントの訓練後もレイテンシ目標が維持されていること、すべての誤拒否に理由があること、フォールバックバジェットが機能していること、ポリシーにオーナー、バージョン、監査記録が存在すること。
成果が本物であることを証明する方法
テナント、ルート、リージョンごとにセグメント化し、導入前後のバックエンド過負荷、5xx、p99レイテンシ、拒否率、誤拒否率、ストレージコストを比較します。自然な低トラフィック期間を成功と誤認しないよう、キャパシティに合わせて正規化します。
よくある間違いとフォローアップ
すべてのノードでローカルカウンターを使用すれば十分である
各ノードが独自のクォータを解放するため、クライアントはノードを移動することで制限を超過できてしまいます。近似誤差を定量化するか、重要なルートには共有状態/リージョンバジェットを使用してください。
固定ウィンドウのみを使用する
ウィンドウの境界で短いダブルバーストが許可されてしまいます。トークンウィンドウまたはスライディングウィンドウを使用し、状態コストと精度について説明してください。
リミッターの障害時にすべてのリクエストを通過させる
高リスクのルートが最後の保護手段を失います。リスクに応じて緊急バジェットを設定し、ページャー発報を伴うフェイルオープン/フェイルクローズを選択してください。
プレミアムテナントへの悪影響をどのように回避しますか?
テナントクォータと優先度を設定し、共有プールと保証プールを分離し、すべての除外設定に有効期限と監査証跡を付与します。
クライアントはどのように再試行すべきですか?
Retry-Afterを遵守し、ジッター付きバックオフを使用し、429に対して無期限に再試行しないようにします。自動再試行には冪等なリクエストが必要です。
新しいポリシーをどのようにカナリアリリースしますか?
シャドウモードで計算を行い、テナントまたはパーセンテージで有効化し、拒否数、コンバージョン、バックエンド負荷を比較します。また、ロールバックに備えてポリシーをバージョン管理します。