zhx 02117bca4c fix: robot orchestrator 只运行本节点持有 lease 的机器人房
机器人房写命令间隔常大于 lease TTL(10s),双节点 orchestrator 无差别
起全量 runtime 导致互相抢占 ownership,跨节点命令全天 CONFLICT 刷屏,
礼物恢复 worker 在非 owner 节点把 pending saga 误标 compensated (~200/h)。

- startRobotRoomRuntime 启动前经 EnsureOwner 认领 lease,非 owner 跳过
- 每个 runtime 增加 lease keeper 按 TTL/3 主动续租,发现执行权被接管立即停止本地循环
- startActiveRobotRooms 改为 reconcile:lease 迁走或配置停用的房间停掉本地残留 runtime
- 礼物恢复 worker 先查房间归属,归他人时释放 claim 让 owner 节点结算
- 跨节点 ownership 冲突不再进入 compensated 终态,改走 retry(pending 不保证钱包未扣款)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-13 00:10:53 +08:00
..
2026-06-12 19:40:21 +08:00
2026-07-12 00:47:20 +08:00
2026-07-12 00:47:20 +08:00
2026-05-16 08:12:42 +08:00
2026-06-27 00:40:30 +08:00

room-service

room-service owns Room Cell state, room snapshots, command log, durable room facts, Redis lease, and Tencent IM room message bridging.

Direct IM UI

Room UI events are sent to Tencent IM directly after the Room Cell command commits. The direct IM lane is low latency and best-effort; it is not the owner of recoverable room state.

Direct-IM-only event types do not write room_outbox because snapshot or query APIs can repair missed display state:

  • RoomHeatChanged: heat display; snapshot can repair missed values.
  • RoomRankChanged: mic/rank display; direct IM display is preferred.
  • RoomRocketFuelChanged: coalesced in memory and flushed directly to Tencent IM.
  • RoomRocketIgnited: sent directly to Tencent IM; launch scheduling is still created after command commit.
  • RoomRocketLaunched: launch display is sent directly to Tencent IM.

Durable facts with UI display are sent directly to Tencent IM after the Room Cell command commits, but they still write room_outbox:

  • RoomGiftSent: room gift animation, public screen, and regional display use direct IM; statistics, activity, growth, task, and CP relation consumers still use durable outbox.
  • RoomUserJoined: entry animation, vehicle, and online display use direct IM; active-user statistics still use durable outbox.
  • RoomUserLeft: leave UI uses direct IM; recovery still comes from Room Cell command log and snapshot.
  • RoomMicChanged: mic UI uses direct IM; mic duration consumers still use durable outbox.
  • RoomMicSeatLocked, RoomClosed, RoomUserKicked, RoomUserMuted, RoomUserUnbanned, RoomProfileUpdated, RoomBackgroundChanged, RoomChatEnabledChanged, and RoomAdminChanged: UI updates use direct IM; state changes, side effects, audit, or notification recovery stay on command log/outbox.

Durable Outbox

Business facts that downstream services must not miss still write room_outbox and are published by the outbox worker. Sending a display event directly to Tencent IM does not change command log, snapshot, room lease, wallet debit, or room state ownership.

The room outbox worker no longer publishes room UI IM. It only fans out durable facts to MQ/activity consumers. Gift sender balance is not included in room IM because room messages are broadcast to all room users. SendGiftResponse.coin_balance_after returns the sender's final COIN balance synchronously, including lucky-gift reward balance when a reward is granted.

RoomRocketIgnited keeps a reliability check outside direct IM: after the room command commits, room-service still schedules the delayed launch wakeup, and the launch worker revalidates rocket id, level, launch time, status, reset time, and room lease before creating RoomRocketLaunched.

Robot display events keep their original best-effort display publisher path in this pass.