レビュー:コミュニティにDiscordスタイルのボイスルームを追加する 🎙

@Falco こんにちは。更新された resenha のテスト中に、再現可能な問題がいくつか見つかりました。upstream に役立てば幸いですので、調査結果とローカルで適用しているパッチを共有します。

1. Admin UI が room_type を無音で破棄し、ステージルームが固定されない
app/controllers/resenha/admin_rooms_controller.rb:65 (room_params) で :room_type が permit リストから除外されており、admin_room_serializer.rb でもシリアライズされていません。

Admin UI 経由で行われたすべてのステージ割り当てが破棄され、ルームは「open」として作成され、後続の Admin 編集でも「open」に戻ってしまいます。本番ログで確認済みです。ユーザー向けコントローラーでは許可されていますが、Admin 経路でのみ失われています。

# admin_rooms_controller.rb — permit リストに :room_type を追加し、その後:
if permitted.key?(:room_type)
  value = Resenha::Room::ROOM_TYPES[permitted[:room_type].to_s]
  raise Discourse::InvalidParameters.new(:room_type) if value.nil?
  permitted[:room_type] = value
end
# admin_room_serializer.rb — room_type をシリアライズし、フォームが正しく初期化されるようにします

2. 無効な room_type が無音で open にフォールバックする
app/controllers/resenha/rooms_controller.rb:575ROOM_TYPES[...] || ROOM_TYPE_OPEN

それ以外に有効なルーム編集において、形成不良または古い room_type が存在すると、ステージルームが静かに open に切り替わります。400 エラーを返すことで、問題のある呼び出し元を可視化し、ルームの破損を防ぐことができます。

value = Resenha::Room::ROOM_TYPES[permitted[:room_type].to_s]
raise Discourse::InvalidParameters.new(:room_type) if value.nil?
permitted[:room_type] = value

3. ハートビートが退室したばかりのユーザーを復活させる(ゴーストプレゼンス)
rooms_controller.rb:234 (heartbeat) は無条件にプレゼンスを再追加するため、leave (:220) の処理中にハートビートが送られてくると、それが後に到達し、退室したユーザーを再作成してしまいます。
このゴーストは TTL による回収まで存在し、ブロードキャストされないため、クライアントは最大1分間ユーザーを「ルーム内」として表示し続けます。

デバイス上で再現確認済みです。キックでも同様の問題が発生します。

# ParticipantTracker: 15秒のトームストーン
def mark_left(room_id, user_id)  = redis.setex(left_key(room_id, user_id), 15, "1")
def recently_left?(room_id, user_id) = redis.exists?(left_key(room_id, user_id))
# leave/kick → mark_left; join/livekit_token → clear_left; heartbeat:
return head :no_content if Resenha::ParticipantTracker.recently_left?(@room.id, current_user.id)

4. 古い participant_left ウェブフックが新しいセッションをファントムキックする
livekit_webhooks_controller.rb:37/57 — 退室はユーザーIDのみでマッチングされます。
迅速な切断/再接続時、前任のセッションの participant_left が遅れて到着し、新しい セッションのプレゼンスを失効させます(ユーザーは再接続から約3秒後に「キック」され、約15秒後に戻ります)。
デバイス上で再現確認済みです。再接続が旧セッションの切断よりも前である場合、gone_at ヒューリスティックはセッションを区別できません。

# participant_joined → ライブ SID を記録
Resenha::ParticipantTracker.set_livekit_sid(room.id, user_id, event.dig("participant", "sid"))
# expire_participant → 前任のセッションの退室をスキップ
known = Resenha::ParticipantTracker.livekit_sid(room.id, user_id)
return if sid.present? && known.present? && sid != known

5. 最後の退室後の DeleteRoom 404 がログを洪水状態にする
lib/resenha/livekit/room_service_client.rb:84 は 200 以外のすべてのレスポンスで警告を発します。
SFU はルームが空になった瞬間に自動的にルームを閉じるため、最後の退室時の DeleteRoom はこれと競合し、「要求されたルームが存在しません」という状態は障害ではなく期待される終了状態です。
これにより、最後の退室のたびに Logster に記録されます。

elsif method == "DeleteRoom" && response.status == 404
  Rails.logger.debug("[resenha-livekit] DeleteRoom no-op for room #{room.id}: already gone")
  true

必要であればプルリクエストを送信できます。

以前述べた通り、あなたの方向性を完全に把握しているわけではありませんが、私が投稿したこちらの記事 Discourse Desktop Mac App - #10 by nicolsdennis をご覧いただければ参考になるかもしれません。

「いいね!」 1