Feat/游玩计数 #604
Reference in New Issue
Block a user
Delete Branch "feat/play-count-with-cache"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
close #539
5s内apiserver内存缓冲
30分钟内按账号/ip去重
- modules/game_distribution.rs:新增 POST /api/game-distribution/games/{game_id}/plays,公开可带 bearer,非公开 404、限流 429、成功 200 {recorded} - 身份组成:登录用 userId、匿名用 clientId、都缺失回退 IP+UA;无效 bearer 按匿名处理不影响计数 - 请求体读取原始 Bytes,空体或畸形体不阻断计数- 玩法链路:路由表新增 POST /games/{gameId}/plays,并新增「游玩计数(已实现)」小节说明触发、落点、缓冲延迟、去重限流与写入语义 - 后端架构:game_distribution_game 节补 play_count 的批量 procedure 写入路径与参数 - decision-log:记录内存去重缓冲 + 批量 procedure 落库的决策、失败语义与影响范围- gameDistributionClient.ts:recordGamePlay 标注 Promise<{ recorded?: boolean }>,空 id 分支也返回同形状对象,消除 Promise<void> 与对象 Promise 的联合类型1. 复用后台请求的鉴权降级策略(已修复,提交 815728ab0)
2. 客户端 IP 只信 X-Real-IP(已修复,提交
95705cbb1+ 文档 08020e664)现状(修复前):client_ip_from_headers 优先取 X-Forwarded-For 第一段,而 nginx 用
$proxy_add_x_forwarded_for会保留调用方自带值,第一段可伪造;X-Real-IP 才是 nginx 覆盖写入的真实 TCP 对端。确认:无 CDN/LB 前置,deploy 也没用 nginx real_ip 模块,所以
$remote_addr(= X-Real-IP)= 真实客户端。改动:优先 x-real-ip;x-forwarded-for 只作回退且取最后一段(nginx 追加的真实对端);都没有才回环兜底。同时修好游玩上报的匿名身份/限流键与微信支付下单的 payer_client_ip。
测试:X-Real-IP 优先、XFF 回退取末段、空 X-Real-IP 回退与回环兜底。
3. 关停不再强制 flush(已按你的决定处理,提交
75ad087e2+ 文档 c0148dda7)你的决定:连“关停时最后冲一次库”都不要。
改动:main.rs 的 finalize_shutdown 删除游戏游玩计数强制 flush;worker 删除 flush_game_play_counter_for_shutdown,flush_deltas 去掉 requeue_on_build 参数;take_pending 改为仅测试使用。ADR 追加修订记录,玩法链路与 decision-log 同步。
效果:崩溃、被杀、正常关停都允许丢最后一个未落库窗口,关停路径不再为计数等待网络;平时零性能开销。
4. 玩家每次点“开始游戏”都先查一次数据库,才轮到内存限流(已修复,提交 3f0a77e65)
改动(大白话):接口现在先做内存限流预检——超过
IP+game每分钟 60 次的请求直接 429,不再去打 SpacetimeDB;通过的请求才去问“游戏是不是公开”。真正计数时 record 仍会重新判一次,所以预检只用来省一次远端查询、不承担正确性。新增只读 is_rate_limited(不消耗额度、不改状态)与对应测试。
代价(你已接受):非公开游戏在超限时可能先收到 429 而不是 404。
5. 排序移出互斥锁(已修复,提交 03f182856)
6. 去重/限流键改用元组(已修复,提交 7c3398029,含分隔符碰撞回归测试)
7. 锁中毒时留日志(已修复,提交 ea8734b70)
8. delta 无上界 / saturating_add(不修)
你的判断:u64 溢出在现实中不可能。reviewer 的 checked_add 改法还会让整批回滚、连累同批合法增量。已去掉。
9. input.clone()(reviewer 建议有误,已核实否决)
try_with_tx 要求闭包是 Fn,不能把捕获的 input move 出去;21 处 procedure 全部用 input.clone(),改成 move 会编译失败。不要改。
10. 统一 recordGamePlay 返回类型(已修复,提交 9de9f4b41)
11. flush 失败后直接丢弃剩余分片(已修复,提交 28ac8a103)
你的决定:剩余分片直接丢。
改动:flush_deltas 改成 while 循环;任一分片写失败即终止本次 flush,不再为后续分片逐个重建连接。
Build(确定未发出)只把当前分片放回下一轮,剩余直接丢弃;其余错误连本批一起丢弃,并记录丢弃增量。效果:数据库不通时不再逐片各等一次 30s 超时把 worker 卡住几分钟。12. 关停 flush 不再假承诺重试(已被 3 取代,提交
2122dc946→75ad087e2整体删除关停 flush)WIP: Feat/play count with cacheto WIP: Feat/游玩计数gamePlayClientId.tsto module and test exclusions