普段動かないフェイルオーバー経路は壊れていても気づかない:YouTube Data API 予備経路
StreamElements が落ちたら YouTube Data API v3 へ切り替える。クォータから逆算した「短時間の保険」という位置付けと、実運用で初めて露見した 3 つの競合。
何に困っていたか
コメント受信は StreamElements のゲートウェイに一本足で依存していた。これが落ちるとコメントが 1 件も届かず、24 時間配信の運用が止まる。
YouTube Data API v3 による予備経路を追加し、主経路が Failed になったら自動で切り替え、復旧したら戻すようにした。あわせて配信メタデータ(videoId・同時視聴者数・高評価数・配信中かどうか)を定期取得し、「配信が終わり、同じチャンネルで次の配信が始まった」枠の切り替えを検知する。
クォータが設計を決めた
Data API のクォータは 1 日 10,000 単位。単価は videos.list が 1、liveChatMessages.list が 5、search.list が 100 である。
liveChatMessages.list を 10 秒間隔で回すと 1 時間 1,800 単位、つまり 1 日で約 5.5 時間分しか持たない。だから予備経路は短時間の障害をしのぐ保険と位置付けた。この割り切りから、次の設計が一貫して決まる。
- 日次使用量を数え、上限(既定 9,000)で予備経路もメタデータも停止して
Failed(理由:クォータ上限)にする。 - 高価な
search.listは「videoId が無い」「配信が終わった」ときだけ、既定 1,800 秒以上空けて呼ぶ。videoId は設定や Remote から与えるのを基本にする。 - 主が
Failedになって 30 秒経過後に副を開始し、主がConnectedへ戻れば副を停止する。副の稼働中はヘルスをDegradedにする。
クォータのリセットは太平洋時間 0 時だが、実装は UTC 08:00 固定にして夏時間の 1 時間は誤差として許容した。
何が起きたか
テストは通っていた。実運用で初めて壊れているのが露見した。
予備経路が一度も成功していなかった。 予備経路の開始・停止が、StreamElements の受信スレッド(Task.Run)上の状態変化通知からそのまま呼ばれていた。UnityWebRequest がメインスレッド外で実行され、失敗していた。イベント通知はコールバックの呼び出し元スレッドを引き継ぐ。
両経路から同時に届いた。 予備経路の稼働中も主のメッセージを透過していたので、両方から同時にメッセージが届くと、重複排除用の HashSet・Queue・List がロック無しで並行呼び出しされて壊れた。「切り替えは排他」という設計意図が、実装では守られていなかった。
1 件の解析失敗で受信ループごと落ちた。 メッセージの authorDetails のロール(isChatOwner など)が null やオブジェクトだったとき、ToObject<bool>() が例外を投げ、受信ループごと切断と再接続を繰り返した。
フェイルオーバーの予約が取りこぼされた。 副の開始待ちが「主が Failed でなくなった」ため見送られたあと予約が残り、主が再び Failed になっても二度と副が始まらなかった。
どう直したか
- 予備経路とメタデータ監視のループは、初期化時に捕捉した
SynchronizationContext上の async/await で回し、裏スレッドを使わない。 - 副の稼働中は主のメッセージを透過しない。受信処理はロックで直列化する。
- ロールは真偽値とその文字列だけを真として扱い、1 件の解析失敗は警告して読み捨てる。
教訓
- フェイルオーバー経路は普段動かないので、壊れていても気づかない。 意図的に主を壊す検証(JWT をわざと壊す)を手順に組み込む。
- イベント通知は呼び出し元スレッドを引き継ぐ。 他コンポーネントのイベントから開始処理を書くときは、自分の
SynchronizationContextへ戻す。 - 「切り替えは排他」は、旧経路の出力を明示的に止めるまで守られない。 片方しか呼ばない前提の共有状態は、その前提をコードで強制する。
- 外部 JSON のスキーマは緩い。
ToObject<bool>()のような強い仮定は 1 件の異常で受信ループ全体を殺す。 - クォータは機能仕様ではなくアーキテクチャの制約。 単価から逆算して「常用しない保険」と割り切ると、設計が一貫する。
未解決
クォータは永続化していないので、再起動すると当日の使用量が 0 から数え直しになる。API の id と StreamElements の msgId が一致する保証はなく、切り替え前後の同一コメントの二重表示は完全には防げない。実 API キーでの疎通確認はまだ行っていない。
Related Articles