MimiCatLab
← ホームに戻る
Unity

普段動かないフェイルオーバー経路は壊れていても気づかない: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 がメインスレッド外で実行され、失敗していた。イベント通知はコールバックの呼び出し元スレッドを引き継ぐ。

両経路から同時に届いた。 予備経路の稼働中も主のメッセージを透過していたので、両方から同時にメッセージが届くと、重複排除用の HashSetQueueList がロック無しで並行呼び出しされて壊れた。「切り替えは排他」という設計意図が、実装では守られていなかった。

1 件の解析失敗で受信ループごと落ちた。 メッセージの authorDetails のロール(isChatOwner など)が null やオブジェクトだったとき、ToObject<bool>() が例外を投げ、受信ループごと切断と再接続を繰り返した。

フェイルオーバーの予約が取りこぼされた。 副の開始待ちが「主が Failed でなくなった」ため見送られたあと予約が残り、主が再び Failed になっても二度と副が始まらなかった。

どう直したか

  • 予備経路とメタデータ監視のループは、初期化時に捕捉した SynchronizationContext 上の async/await で回し、裏スレッドを使わない。
  • 副の稼働中は主のメッセージを透過しない。受信処理はロックで直列化する。
  • ロールは真偽値とその文字列だけを真として扱い、1 件の解析失敗は警告して読み捨てる。

教訓

  1. フェイルオーバー経路は普段動かないので、壊れていても気づかない。 意図的に主を壊す検証(JWT をわざと壊す)を手順に組み込む。
  2. イベント通知は呼び出し元スレッドを引き継ぐ。 他コンポーネントのイベントから開始処理を書くときは、自分の SynchronizationContext へ戻す。
  3. 「切り替えは排他」は、旧経路の出力を明示的に止めるまで守られない。 片方しか呼ばない前提の共有状態は、その前提をコードで強制する。
  4. 外部 JSON のスキーマは緩い。 ToObject<bool>() のような強い仮定は 1 件の異常で受信ループ全体を殺す。
  5. クォータは機能仕様ではなくアーキテクチャの制約。 単価から逆算して「常用しない保険」と割り切ると、設計が一貫する。

未解決

クォータは永続化していないので、再起動すると当日の使用量が 0 から数え直しになる。API の id と StreamElements の msgId が一致する保証はなく、切り替え前後の同一コメントの二重表示は完全には防げない。実 API キーでの疎通確認はまだ行っていない。

Related Articles