設問とコンテキスト
管理コンソールでユーザーが複数のタブを開いています。ユーザーが1つのタブでログアウト、テーマ変更、または通知の更新を行った後、他のタブも迅速に同期・収束する必要があります。リロード、スリープ状態のタブ、重複メッセージ、および BroadcastChannel に未対応のブラウザによって最終状態が破損してはなりません。プロトコル、リカバリ、フォールバック、および検証メトリクスを設計してください。
これは、フロントエンド、Web プラットフォーム、およびフロントエンドシステム設計のポジションに向けたブラウザ間協調に関する質問です。タブ数、同期遅延、およびイベントタイプは面接上の前提条件であり、ブラウザの保証ではありません。BroadcastChannel の同一生成元(same-origin)およびストレージパーティションの境界、非永続的メッセージの影響、イベント対状態の選択、そして localStorage、IndexedDB、SharedWorker、または Web Locks と組み合わせるべきタイミングに焦点を当ててください。
面接官がテストしていること
第1に、API の境界を正確に述べられるかです。BroadcastChannel は、同一生成元および通信可能なストレージパーティションを共有するウィンドウ、タブ、フレーム、ワーカー間でメッセージを送信します。クロスオリジンのトランスポート、永続キュー、または分散整合性サービスではありません。
第2に、冪等なプロトコルを設計できるかです。メッセージは重複する可能性があり、送信者は自身のブロードキャストを受信せず、受信側のページは閉じられたりスリープしたりする可能性があります。バージョン情報や再読み込みパスを持たずに単に「テーマが変更されました」と伝えるだけのメッセージは、状態を恒久的に古いままにしてしまいます。
第3に、通知と信頼できる唯一の情報源(Source of Truth)を分離できるかです。ログアウトは無効化シグナルをブロードキャストし、テーマの変更は永続化されてから通知し、通知の更新はメッセージ内の完全なリストを真実として扱うのではなく、受信側が信頼できるキャッシュを再読み込みするように設計すべきです。
最後に、ライフサイクルとフォールバックを処理できるかです。チャンネルのクローズ、メッセージからのトークン除外、機能検出、ストレージイベントやサーバー再読み込みの利用、そして同期処理がループやメモリリークを引き起こさないことの測定などが含まれます。
最初に確認すべき明確化のための質問
- ペイロードは1回限りのイベント、現在の状態、または再生可能な履歴のいずれですか?これによって永続化されたバージョンが必要かどうかが決まります。
- スコープは単一の生成元、同一トップレベルサイト配下の iframe、または異なるサブドメインのいずれですか?生成元が関連しているように見えても、ストレージパーティショニングによって通信が妨げられる場合があります。
- メッセージが1つ失われることは許容されますか?ログアウト、権限の取り消し、および編集の競合には、それぞれ異なるリカバリ保証が必要です。
- 信頼できる情報源(Source of Truth)はどこにありますか:サーバー、IndexedDB、localStorage、インメモリキャッシュ、または Service Worker ですか?
- 更新、マイグレーション、またはバッチ処理において、単一のタブのみがライター(書き込み役)である必要がありますか?BroadcastChannel はロック機能を提供しません。
- プライベートモード、バックグラウンドでのフリーズ、想定されるタブ数など、ブラウザマトリクスはどうなっていますか?
- メッセージにプロファイルデータ、権限、またはトークンが含まれる可能性はありますか?その場合、機密性のない無効化シグナルに置き換えてください。
30秒での回答
「私は BroadcastChannel を永続的な状態ソースとしてではなく、低遅延の通知バスとして使用します。各メッセージにはプロトコルバージョン、イベントタイプ、単調増加シーケンスまたは状態バージョン、およびトレース ID を含めます。受信側は構造を検証し、イベントを冪等に適用し、バージョンのギャップが発生した場合は localStorage、IndexedDB、またはサーバーから再読み込みします。ログアウト時は無効化を送信し、テーマ変更時は永続設定に書き込み、通知変更時は再読み込みをトリガーします。API が利用できない場合はストレージイベントまたはポーリングを使用し、単一のライターが必要な場合は Web Locks またはサーバー協調を使用します。破棄時にはすべてのチャンネルをクローズし、遅延、失われたメッセージのリカバリ、および重複処理を測定します。」
ステップごとの回答
ステップ1: メッセージと信頼できる状態を定義する
機能ごとに信頼できる情報源(Source of Truth)を割り当てます。テーマと言語は、永続設定に保持できる環境設定です。通知と権限は、サーバーまたはローカルキャッシュから再読み込みする必要があります。ログアウトはセッション無効化シグナルです。アクセストークンをメッセージに含めてはなりません。機密性のない type、version、entityKey、updatedAt、または traceId のみをブロードキャストしてください。
MDN では、BroadcastChannel は同一生成元のブラウジングコンテキストおよびワーカー間の双方向通信をサポートする一方、メッセージプロトコルはアプリケーション側で定義され、プラットフォームはネゴシエーションを提供しないと説明されています。バージョニング、未知のイベントの処理、およびフィールドのバリデーションはアプリケーションの責任です。
ステップ2: 重複、順序、および欠落を処理する
状態ドメインごとに最後に処理されたバージョンを保持します。バージョンが古いか等しいメッセージは無視します。連続した新しいバージョンであれば1回の再読み込みをトリガーし、数値のジャンプがあればギャップとみなしてスナップショット同期を開始します。ビジネス要件によりバージョンなしのイベントを公開する場合は、重複排除 ID と有界な処理済みセットを使用しますが、これにより中間のイベントが失われていないことを証明できるわけではない点に留意してください。
配信が保証されていると想定してはなりません。送信者は自身のメッセージを受信せず、新しく開かれたタブやスリープ中のタブはメッセージを見逃す可能性があります。起動時には、購読する前に永続状態またはサーバースナップショットを読み込みます。ブロードキャストは鮮度の遅延を減らすためだけのものです。
ステップ3: 永続化と協調制御を併せて選択する
localStorage は小さな環境設定に適しており、storage イベントをトリガーできますが、高スループットのデータベースやトークンの保存場所には適していません。IndexedDB は、より大きなローカルキャッシュやバージョン管理されたスナップショットに適しています。Service Worker はバックグラウンド同期に参加できますが、結果をリカバリ可能なストレージに書き込む必要があります。
BroadcastChannel は複数のコンテキストへの通知を解決しますが、単一ライターの協調制御は解決しません。単一のタブのみがキャッシュの更新、マイグレーションの実行、またはバッチ送信を行う必要がある場合は、Web Locks を使用してください。ロックがない場合は、バージョン条件を含めてサーバーに調停させます。SharedWorker は共有接続や中央集中型の状態に適していますが、ライフサイクルと互換性の複雑さが増します。
ステップ4: 安全な受信とフォールバックを構築する
ホワイトリストに登録されたイベントタイプのみを受け入れ、メッセージサイズを制限し、未知のバージョンや無効なスキーマを拒絶します。アクセストークン、完全なプロファイル、または実行可能な HTML をメッセージに含めてはなりません。ログアウト時は、機密性の高いローカルキャッシュをクリアしてサインイン画面に遷移します。テーマイベントでは、UI を更新しつつ永続化された設定を信頼します。
機能検出に失敗した場合は、小さな状態に対して storage イベントを使用します。それが利用できない場合やパーティション化されている場合は、フォーカス時の再読み込み、短いポーリング、またはサーバープッシュを使用します。フォールバックは繰り返し実行しても安全である必要があります。ブロードキャストの欠如が永続的な状態の不一致につながってはなりません。
ステップ5: リソースを管理し、挙動を検証する
コンポーネントやページが破棄されるときは、リスナーを削除して close() を呼び出します。レンダーごとに新しいチャンネルを作成してはなりません。無関係なアプリケーションが同じ名前を購読しないように名前空間を使用します。受信側が同じメッセージをループして再ブロードキャストしないように、ソースラベルとトレース ID を含めます。
マルチタブのエンドツーエンド遅延、重複と並べ替え、スリープ後の復帰、リロード後の初期スナップショット、ストレージフォールバック、ストレージパーティションの分離、チャンネルのクローズ、過大なメッセージ、ループ、およびユーザー切り替えをテストします。ハンドラーの成功率、バージョンのギャップ、再読み込み回数、破棄された重複、リカバリ時間、および開いたままのリソース(チャンネル)を追跡します。
質の高い模範解答
「私は BroadcastChannel をキューではなく通知バスとして定義します。ログアウトにはセッション無効化タイプとバージョンのみを含めます。テーマの更新では localStorage に書き込み、新しい設定バージョンをブロードキャストします。通知の更新ではエンティティキーとバージョンをブロードキャストし、各受信側がサーバーまたは IndexedDB から再読み込みできるようにします。メッセージにはトークンや完全なユーザーデータを含めません。
各メッセージにはプロトコルバージョン、単調増加する状態バージョン、およびトレース ID が含まれます。受信側は未知の構造を拒否し、処理済みまたは古いバージョンを破棄し、ギャップを検知したときにスナップショットを取得します。新規またはスリープ中のタブはブロードキャストを見逃す可能性があり、送信者は自身のメッセージを受信しないため、起動時は購読前にスナップショットを読み込みます。
キャッシュ更新に単一のライターが必要な場合は、Web Locks を追加します。BroadcastChannel 単体では2つのタブが同時に書き込むのを防ぐことはできません。未対応のブラウザでは、小さな環境設定には storage イベントを使用し、その他のデータにはフォーカス時の再読み込みや短いポーリングを使用します。終了処理ではリスナーを削除し、チャンネルを閉じます。メトリクスとしては、遅延、ギャップ、重複、リカバリ時間、リークしたリソースをカバーします。これにより、低遅延の通知、リカバリ可能な状態、およびブラウザの互換性を分離して管理できます。」
よくある失敗パターン
- ブロードキャストを永続キューとして扱う → 新規またはスリープ中のタブがイベントを見逃す → 最初にスナップショットを読み込み、メッセージはヒントとして使用する。
- メッセージにトークンや完全な状態を含める → 機密データの漏洩リスクとバージョンの競合が拡大する → 機密性のないイベント、キー、およびバージョンのみを送信する。
- バージョンや重複排除 ID がない → 重複や並べ替えによって UI の状態が後退する → 単調増加バージョン、冪等な処理、およびギャップ時の再読み込みを使用する。
- 同一生成元であれば接続が保証されると思い込む → ストレージパーティショニングによってコンテキストが分離される場合がある → 実際の境界をテストし、フォールバックを提供する。
- BroadcastChannel をロックとして使用する → 2つのタブが依然として同時に書き込みを行う可能性がある → Web Locks または条件付きサーバー書き込みを使用する。
- レンダーごとにチャンネルを作成する → リスナーとリソースがリークする → 安定したインスタンスを維持し、リスナーを削除して適切にクローズする。
- 受信したすべてのメッセージを再ブロードキャストする → ループが発生する → ソース/トレース ID を保持し、状態変更時のみ送信する。
- storage イベントを完全な代替手段として扱う → 一部の書き込みしかカバーせず、同一ウィンドウには発火しない → 違いを文書化し、再読み込みを追加する。
フォローアップの質問
フォローアップ 1: 2つのタブが同じフォームを編集しています。上書きを防ぐにはどうすればよいですか?
リソースのバージョンが変更されたことのみをブロードキャストし、ローカルの下書きを上書きしないようにします。ベースバージョンを送信し、サーバー側で古い条件付き書き込みを拒否させ、ユーザーがマージできるように競合を表示します。ローカルの環境設定であれば、Web Locks を使用して書き込みをシリアライズできます。
フォローアップ 2: タブが20分間スリープした後に復帰しました。どのように追いつきますか?
復帰時にサーバーまたは IndexedDB のスナップショットを再読み込みし、状態バージョンを比較した上で、差分シグナルを処理します。最後のブロードキャストに依存してはなりません。スナップショットが利用できない場合は、状態を stale(古い)としてマークし、再検証を要求するか、明確な期限切れ状態を表示します。
フォローアップ 3: 異なるサブドメイン上のページ同士が通信する必要があります。BroadcastChannel で解決できますか?
解決できると想定してはなりません。BroadcastChannel はオリジンおよびストレージパーティションによって制約されます。クロスオリジン通信には、明示的な postMessage によるウィンドウ関係またはサーバーを介した協調が必要であり、オリジン、スキーマ、および権限のチェックを伴います。単にチャンネルを共有したいという理由だけでセキュリティ境界を弱めてはなりません。
フォローアップ 4: 1つのタブのみが WebSocket 接続を維持する必要があります。どのように設計しますか?
BroadcastChannel は接続状態やデータを共有できますが、リーダーを選出することはできません。接続保持者には Web Locks を優先的に使用し、他のタブはそのブロードキャストを購読するようにします。保持者が閉じるかロックを失った後に再選出を行います。利用できない場合は、サーバーリースを使用するか、サーバー側の重複排除と明確なコストのトレードオフを伴う複数接続を許可します。