【ITニュース解説】FastAPI Live Poll Host Reassignment: Deterministic Leases for Split-Brain Clients
2026年10月24日に「Dev.to」が公開したITニュース「FastAPI Live Poll Host Reassignment: Deterministic Leases for Split-Brain Clients」について初心者にもわかりやすく解説しています。
ITニュース概要
複数クライアントが同時にホストと誤解し操作が競合する問題を解決する。サーバーがホスト権限を一元管理し、リース期間と世代番号(エポック)を付与する。クライアントの操作時はサーバーでその世代番号を検証、常に唯一のホストが確実に機能する。
ITニュース解説
オンラインで複数人が同時に参加するアプリケーション、例えばオンライン会議システムでの投票機能や共同作業ツールでは、「ホスト」と呼ばれる特別な権限を持つ参加者がいる。ホストは投票の開始・終了、参加者の管理など、重要な操作を行う役割を担う。しかし、このホストの権限管理が意外と難しい問題を引き起こすことがある。
特に「WebRTC」のようなピアツーピア通信技術を使ったアプリケーションでは、参加者同士が直接データをやり取りするため、ホストがネットワークから切断された場合に「誰が次のホストになるべきか」という問題が複雑になる。最も厄介なのは、「スプリットブレイン」と呼ばれる状況が発生することだ。これは、複数の参加者(クライアント)が、それぞれ自分こそが現在のホストであると誤解してしまう状態を指す。例えば、クライアントAがホストの役割を担っていたが、一時的にネットワーク接続が失われたとする。クライアントAは自分のメモリ内に「is_host = true」という状態を保持し続ける。その間、サーバはAの接続断を検知し、別のクライアントBを新しいホストに任命する。しかし、Aが再接続した際、Aは依然として自分をホストだと思い込み、以前の認証情報やキャッシュされた状態に基づいてホストとしての操作を試みようとする。このとき、Bもサーバによって正しく任命されたホストとして振る舞うため、アプリケーション上では「二人のホスト」が存在し、どちらも投票を開始したり終了したりできるような、矛盾した状況が生まれてしまうのだ。
このような状況は、ネットワークの遅延、メッセージの到達順序の乱れ、サーバやクライアントの処理のタイミングなど、様々な要因が絡み合うため、再現が非常に困難で、デバッグも難しい。WebRTCはピア間のデータ伝送を保証するが、アプリケーションの役割(ホストなど)の管理までは行わないため、この問題はアプリケーション側のプロトコルで解決する必要がある。クライアントが自身のローカルな状態だけでホストであると判断することは、この問題を引き起こす根本原因となる。
このスプリットブレイン問題を解決するための核心的な原則は、「ホストの権限は、クライアントの好みではなく、サーバ側で管理される事実である」という点だ。具体的には、サーバが「ルームID、ホストID、エポック(世代番号)、有効期限」という形でホストの割り当てを管理する。ここで言う「エポック」とは、ホストの権限が更新されるたびに増加する一意の番号のことである。ホストの権限が別のクライアントに移るたび、あるいは明示的に同じホストに再割り当てされるたびに、このエポックが一つ増える。これにより、古いエポックの情報を持つクライアントからの操作は、サーバによって拒否されるようになる。
ホストの権限を管理する上で重要なのは、「権限に関わる通信経路」と「一般的なピア間通信経路」を明確に分けることである。クライアントはアプリケーションサーバに接続し、ルームに参加する。サーバは、接続中のメンバーの中から、あらかじめ決められた順序に基づいて次のホスト候補を決定する。例えば、ルームへの参加順序やメンバーIDなどを考慮し、誰が見ても同じ結果になるように順序付けられたリストから選ぶ。そして、サーバが新しいホストを決定すると、新しいエポックを割り当ててその情報をデータベースなどにコミット(確定)し、すべてのクライアントに新しいホストの情報をブロードキャストする。WebRTCは投票の更新や参加者の状態通知といったピア間のデータ伝送に引き続き使われるが、ホストの変更や投票の開始・終了といった権限に関わる重要なコマンドは、必ずサーバを経由させる。クライアントはサーバから受け取ったブロードキャスト情報を「現在の確定した状態の表示」として扱い、自分自身で権限を拡張するようなことはしない。
このホストの選出プロセスとエポックのコミットは、アトミック(不可分)に行われる必要がある。複数のサーバプロセスが同時にホストの再割り当てを試みても、データベースのトランザクション機能や比較交換(Compare-and-Swap)操作を使って、必ず一つの処理だけが成功し、エポックが一度だけ更新されるようにする。これにより、複数のサーバが同時に異なるホストを任命してしまうような事態を防ぐことができる。
サーバが新しいホストを決定し、エポックをコミットした後、そのホストに対して「能力トークン」を発行する。これは、JWT(JSON Web Token)のような形式の短い有効期限を持つトークンで、そのトークンがどのルームの、どの参加者に、どの役割(ホスト)として、どのエポックで、どのような操作(例:投票開始、投票終了)を許可するのかが明記されている。ホストが何らかの操作(例えば投票を閉じる)をサーバに要求する際、この能力トークンを添付して送信する。サーバは、トークンの署名が正当であるか、有効期限が切れていないか、そして何よりもトークンに含まれるエポックが現在の最新のエポックと一致しているかを検証する。たとえ有効な署名を持つ古いエポックのトークンであっても、現在のエポックと一致しなければそのコマンドは拒否される。これにより、再接続した古いホストが、自身が持っていた古いエポックのトークンを使って不正な操作を試みても、それが無効化される。参加者向けのトークンは、ホストのそれよりも狭い範囲の権限(例えば投票の提出のみ)に限定し、ホストの権限を継承できないようにする。
リース期間の設定も重要だ。ホストが活動を停止してから次のホストに切り替わるまでの時間は、リースの有効期限によって決まる。この期間が短すぎると、ネットワークの一時的な遅延でも頻繁にホストの切り替えが発生し、サーバへの負荷も増える可能性がある。逆に長すぎると、ホストが実際に機能停止している間、誰もホスト操作を行えない時間が増えてしまう。そのため、ネットワークのレイテンシやサーバの応答速度を計測し、アプリケーションが許容できる回復時間を考慮して、適切な期間を設定することが重要である。
このシステムを導入する際には、競合状態やネットワーク障害時にも正しく機能するかを徹底的にテストする必要がある。例えば、同時に複数の再割り当て要求があった場合に、常に一貫した結果(同じホスト、同じエポック)が返されるか、古いエポックのブロードキャストが新しいエポックの再接続よりも早く届いた場合にどうなるか、といったシナリオを検証する。重要なのは、「どんな状況でも、あるエポックにおいてホスト操作を実行できるのは最大で一人のメンバーであること」、「エポックは必ず増加し、決して後退しないこと」、「クライアントは自身が観測した最新のエポックよりも古い割り当てを無視すること」、「古いエポックを持つコマンドは状態を変更しないこと」といった基本的な保証が維持されることだ。
また、運用の側面では、ホストの権限変更に関するイベントを詳細にログに記録し、監視することが不可欠である。どのルームで、どのホストからどのホストに、なぜ切り替わり、エポックがどう変化したか、といった情報を記録することで、問題発生時に原因を特定しやすくなる。データベースのスキーマも、エポックや冪等性をサポートするように適切に設計し、古いバージョンのシステムが新しいスキーマを壊さないように注意深いリリース計画が必要となる。
最終的に、この仕組みはブラウザ間で「合意」を形成するものではない。サーバがバージョン管理されたホストの「リース」をコミットし、クライアントはそれを一時的な「ビュー」としてキャッシュするだけであり、すべての重要なコマンドはサーバで現在のバージョンに対する検証を受ける。この厳格な契約によって、二つのクライアントがそれぞれホストであると主張し、矛盾した状態を引き起こす「スプリットブレイン」の問題を効果的に防ぎ、アプリケーションの安定性と信頼性を高めることができる。