代表的な面接トピック

リースベースのリーダー選出をどのように設計しますか?

システム設計難しい
Offer.cc 編集チーム公開日 更新日

質問

複数のレプリカ間で、スケジュールされた決済ジョブを一度に1つのインスタンスのみが実行できるようにする必要があります。リースベースのリーダー選出を設計し、障害、パーティション、復旧時における安全性(Safety)と可用性(Availability)について説明してください。

課題とコンテキスト

複数のレプリカが協調ストレージを共有し、スケジュールされた決済ジョブを一度に1つのインスタンスのみが実行できるようにする必要があります。リースのレコード、立候補(campaign)、更新(renewal)、ハンドオフ、およびオブザーバビリティを設計してください。ネットワーク分断(パーティション)、プロセスの一時停止、クロックドリフト、ストレージ障害への対応を含めます。リースは誰が作業を開始できるかを制御するものであり、ビジネスロジックの書き込みには依然として冪等性と条件付きチェックが必要です。

面接官が評価しているポイント

  • リースの安全性とビジネス上の副作用を明確に分離できているか。
  • アトミックな compare-and-set、タームフェンシングトークン、およびクォーラムを理解しているか。
  • 一時停止、分断、再起動した古いリーダーによって有効な二重書き込み(dual writers)が発生しない理由を説明できるか。
  • 測定可能なシグナル、フォールトインジェクション、および復旧動作を定義できるか。

回答前の確認質問

許容される一時停止時間枠、許容される重複実行、協調ストレージの一貫性モデル、リージョン間レイテンシ、レプリカ数、およびクロック同期の品質を確認します。リージョン間での強い一貫性が必要な場合は、可用性と選出レイテンシのトレードオフを明確にします。

30秒の回答フレームワーク

線形化可能な読み取り(linearizable reads)と条件付き書き込みを提供する協調ストレージを使用します。各候補者は一意のIDと単調増加するターム(term)で競合し、アトミックな作成または更新に成功したインスタンスのみがリーダーになります。リーダーは有効期限が切れる前に更新を行います。すべてのビジネス書き込みにはタームトークンが付与され、ダウンストリームシステムは古いトークンを拒否します。候補者は新しい状態を確認した後にのみ処理を引き継ぎます。パーティションや長い一時停止の間、インスタンスは副作用を伴う処理を停止します。一時的な停止は、二重書き込みが発生するよりも安全です。

ステップごとの詳細解説

1. データモデルとアトミック操作

リースのレコードには、保持者のID、有効期限、タームトークン、バージョン、および最終更新時刻が含まれます。競合には compare-and-set を使用します。レコードが存在しない場合は作成し、バージョンが変更されておらずリースが期限切れの場合にのみ更新します。Kubernetes はリースを保持者と更新情報を含む協調オブジェクトとして表現します。結果整合性のキャッシュを信頼するのではなく、ストレージの一貫性モデルを検証して実装する必要があります。

2. 更新と自己降格

ネットワークジッターやスケジューラの一時停止に備えて十分な猶予を持たせ、リース期間内に余裕をもって更新します。更新が失敗した場合、確認応答が読み取れない場合、またはプロセスの停止が安全時間枠を超えた場合は、直ちに副作用を伴う処理を停止し、復旧後に再度立候補します。ローカルのウォールクロックのみに基づいて他の保持者の期限切れを判断してはなりません。

3. フェンシングとビジネスの冪等性

立候補が成功するたびに、単調増加するトークンが生成されます。ワーカー、データベースの条件付き更新、またはダウンストリームサービスは、古いトークンを持つリクエストを拒否します。これにより、一時停止から復帰した古いリーダーが新しいリーダーの書き込みを上書きすることを防ぎます。決済処理には、依然として冪等性キー、トランザクション境界、およびリトライ処理が必要です。

質の高い模範解答

私は安全性を「2つの有効なリーダーが同時に同一リソースに書き込むことを防ぐこと」と定義し、可用性においては「リース期限切れ後の短時間の停止を許容すること」とします。協調ストレージは線形化可能な CAS を提供します。候補者は自身のIDと増加するタームを記録し、リーダーはハートビートによって更新を行います。すべての副作用にはタームトークンが付与され、ダウンストリームの条件付き書き込みによって比較検証されます。パーティション、GCの一時停止、または更新確認の消失が発生した場合、インスタンスは停止し、状態を再確認した後にのみ立候補します。単に数秒スリープすることは安全性の証明にはなりません。Raft はログのリーダーシップにタームと多数決(クォーラム)を使用しますが、Kubernetes Lease はより軽量な協調レコードです。どちらも明示的な一貫性モデル、タイムアウトバジェット、および復旧計画を必要とします。私であれば、リーダーのクラッシュ、パーティション、クロックオフセット、長時間の停止、ストレージ利用不可などの障害を注入し、二重書き込み、古いトークンによる書き込み、目標復旧時間の違反がないかを検証します。

よくある間違い

  • 二重リーダーは発生しないと主張しながら、プロセスメモリ内のみでロックを保持したり、結果整合性の Redis 読み取りに依存したりすること。
  • 単調増加するタームやフェンシングトークンを使用せずにタイムスタンプのみを比較すること。
  • 更新失敗後も現在のバッチ処理を継続し、古いリーダーに書き込みの隙を与えてしまうこと。
  • 「一度に1つのリーダー」が存在することを「重複実行が絶対に発生しないこと」と同義と捉えること。
  • 通常の選出のみをテストし、一時停止、パーティション、ストレージレイテンシ、ストレージ障害をテストしないこと。

フォローアップ質問と回答

リース期限が切れると、古いリーダーが停止したことが保証されますか?

いいえ。プロセスが実行されたまま一時停止したり、隔離されたりする可能性があります。ビジネス境界でフェンシングトークンを検証する必要があります。期限切れは、協調レイヤーがその保持者を認識しなくなったことを意味するに過ぎません。

リース間隔と更新間隔はどのように選択しますか?

障害検知目標時間、リージョン間の p99 レイテンシ、スケジューラ一時停止バジェット、ストレージジッターから導き出し、複数回の更新サイクル分のマージンを設けます。固定のミリ秒値をそのまま流用するのではなく、更新失敗や選出の頻発(churn)を監視します。

協調ストレージが利用できなくなった場合はどうなりますか?

新しい副作用の発生を停止し、読み取り専用または安全にコミットされた結果のみを保持します。復旧後、タームを再読み込みして立候補します。継続的な運用が必須である場合は、シャーディング、マルチアクティブな所有権モデル、または手動介入へ明示的に縮退(デグレード)させ、安全性の証明を再定義します。

公開情報ソース

関連する質問

関連面接ツール

システム設計の回答には「回答する」を使用

まず要件を明確にし、スケール、アーキテクチャ、コンポーネント選定、トレードオフの順に進めます。

ツールを見る