评测 - 为您的社区添加 Discord 风格的语音房间 🎙

@Falco 嘿,我在测试更新后的 resenha 时遇到了一些可复现的问题。分享我的发现以及我本地正在运行的补丁,以防它们对上游开发有用。

1. 管理 UI 静默丢弃 room_type,导致房间无法保持阶段状态
app/controllers/resenha/admin_rooms_controller.rb:65room_params)在允许参数列表中省略了 :room_type,且 admin_room_serializer.rb 从未序列化它。

通过管理 UI 进行的每次阶段分配都会被丢弃,房间创建为开放状态,并在任何后续的管理编辑中“恢复”为开放状态。已通过生产日志确认;面向用户的控制器允许该参数,仅管理路径丢失了它。

# admin_rooms_controller.rb — 在允许列表中添加 :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 静默回退为开放状态
app/controllers/resenha/rooms_controller.rb:575ROOM_TYPES[...] || ROOM_TYPE_OPEN

在任何其他方面有效的房间编辑中,任何格式错误/过期的 room_type 都会悄悄地将阶段房间翻转为开放状态。返回 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:234heartbeat)无条件地重新添加存在状态,因此当 leave:220)处理时,正在传输中的心跳稍后到达并重新创建已离开的用户。
该幽灵存在状态将持续到 TTL 回收,而回收不会广播——客户端显示他们“在房间内”长达一分钟。

已在设备上复现;踢出具有相同的暴露问题。

# 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 webhook 幻影踢出新鲜会话
livekit_webhooks_controller.rb:37/57 — 离开仅通过用户身份匹配。
在快速断开/重新连接时,被取代会话的 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 个赞