9.1 KiB
9.1 KiB
todos:
- id: "remove-prewallet-check" content: "Phase 1: 删除 executeBetWalletTransfer 中 getWallet 预检,改由 wallet bet 返回映射余额不足" status: pending
- id: "defer-ledger-query" content: "Phase 1: finalizeSuccessfulTransfer 去掉同步 queryBizLog,markWalletSuccess(0) + 异步 ledger 回填" status: pending
- id: "wallet-return-ledger" content: "Phase 2: slot-wallet update 响应增加 ledger_id,POP 直接写入 provider_tx" status: pending
- id: "merge-db-updates" content: "Phase 3(可选): 合并 callback_log / provider_tx 多次 UPDATE,并加分段耗时监控" status: pending isProject: false
POP modifyFee 性能分析与队列化建议
结论(直接回答)
是的,同步路径偏重,但不宜把「整单回调」丢进队列。
- Controller
slot-pwa/app/pop/controller/CashController.php本身很薄,性能问题在slot-pwa/app/pop/logic/CashLogic.php。 - POP 协议要求 同步返回最新
balance,且 钱包扣款/派奖必须在响应前完成,否则厂商会重试或判失败。 - 相比 jdb/quick/gasea(只做 wallet + Redis 幂等 + 异步流水),POP 额外引入了 callback_log、provider_tx、game_round 三套分表状态机,单次成功回调大约 6
10 次 DB 写 + 23 次 wallet HTTP。 - 已有异步:用户交易流水(
TransactionLogService::log→pwa_transaction_logworker)、局终事件(GameRoundEventService::publishFinalSettled)、试玩活动(TrialRewardNotifyServiceMQ publish)。
因此:可以队列化的是「不影响 POP 响应正确性的旁路写/追踪」;不能队列化的是幂等、钱包、局状态校验。
当前同步链路(成功路径 BET)
sequenceDiagram
participant POP
participant MW as ProviderCallbackLogMiddleware
participant Logic as CashLogic
participant DB as ShardDB
participant Wallet as slot_wallet
participant MQ as RabbitMQ
POP->>MW: TransferInOut
MW->>DB: insert callback_log
MW->>Logic: modifyTransferInOut
Logic->>DB: update callback_log parse/in_progress
Logic->>DB: createOrGet provider_tx
Logic->>DB: loadOrCreate game_round
Logic->>DB: markProcessing / markWalletCalling
Logic->>Wallet: getWallet (BET 余额预检)
Logic->>Wallet: update bet
Logic->>Wallet: queryBizLog
Logic->>DB: markWalletSuccess + round update
Logic->>MQ: TransactionLog (async)
Logic->>DB: markProcessSuccess
Logic->>POP: balance
对比 legacy slot-pwa/app/quick/service/CashService.php:无 callback_log / provider_tx / game_round,wallet 成功后只发 MQ 写流水,同步面小很多。
同步步骤分类
| 步骤 | 位置 | 是否必须同步 | 说明 |
|---|---|---|---|
| callback_log 收包落库 | Middleware | 建议保留同步 | 审计与 trace;但可合并多次 update |
| provider_tx create-or-get | resolveProviderTx |
必须 | 幂等键,防双花;必须在 wallet 前 |
| game_round load/create/validate | RoundService |
必须 | 后续 WIN/FINAL 依赖局状态 |
| 风控 evaluate + applyRisk | handleTransferRiskEvaluation |
必须(接入真实风控后) | HOLD/REJECT 需在 wallet 前 |
| getWallet 余额预检 | executeBetWalletTransfer |
可优化掉 | wallet 本身会拒余额不足,多 1 次 RTT |
| WalletService bet/win | invokeWalletBetOrWin |
必须 | 响应 balance 来源 |
| queryBizLog | finalizeSuccessfulTransfer |
可移出热路径 | 仅回填 wallet_ledger_id;已有补偿 ProviderTxCompensationService |
| game_round applyBet/Win/Final | applyRoundUpdateAfterSuccess |
必须 | 同局后续回调校验 |
| Redis round 缓存 | updateRoundStateInfo |
可弱一致异步 | 有 DB fallback,但异步需防乱序 |
| TransactionLog | TransactionLogService::log |
已异步 | worker 内还做 VIP/日报/big win |
| callback_log 终态 | markProcessSuccess |
可延迟 | 不影响 POP 响应;影响实时排障 |
| UserProfit / RTP stat | WalletService::bet/win 内 |
可异步 | Redis 统计,非 POP 协议字段 |
主要性能瓶颈(按收益排序)
1. 冗余 wallet HTTP(最高收益、最低风险)
BET 路径双查钱包:
$currentWallet = WalletService::getWallet($userInfo->uid, $userInfo->currency);
if ($currentWallet->balance < $feeAmount) {
// ... markRejected ...
}
// ...
return $this->invokeWalletBetOrWin($walletCommand, true);
- 先
getWallet,再updateWallet(bet),多 1 次完整 HTTP 往返。 - 建议:删除预检,直接
bet(),按 wallet 返回/错误码映射CODE_INSUFFICIENT_FUNDS(与 legacy 一致)。
成功后二次查询 ledger:
$ledgerQuery = $this->walletGatewayService->queryBizLog(
$transferContext->uid(),
$finalizeContext->walletRequestId,
ProviderTxService::resolveWalletBizType($finalizeContext->txType)
);
$walletLedgerId = (int) ($ledgerQuery['ledger_id'] ?? 0);
$this->providerTxService->markWalletSuccess(..., $walletLedgerId);
- 钱包
update响应WalletEntity不含ledger_id,故又调queryBizLog,再多 1 次 HTTP。 - 两条路(二选一或组合):
- A(推荐):扩展
slot-walletupdate响应带ledger_id,POP 直接写入,去掉热路径queryBizLog。 - B(低改动):热路径
markWalletSuccess(ledger_id=0),通过现有ProviderTxWalletCompensate或新建轻量 MQ consumer 异步回填wallet_ledger_id(与 reward_grant 文档中 queryBizLog 补偿模式一致)。
- A(推荐):扩展
2. 多次分表状态机 UPDATE(中等收益)
单次回调对 callback_log、provider_tx 分别多次 UPDATE(parse → in_progress → wallet_calling → success;middleware 还有 insert)。可考虑:
- 合并
markParseSuccess+markProcessInProgress为一次写(需改 Model SQL)。 - provider_tx 状态迁移合并(processing + wallet_calling 若对排障非强依赖可合并)。
- 注意:这是 DB 优化,不是 MQ;队列化这些状态反而增加一致性复杂度。
3. 适合队列、且不影响 POP 响应的旁路
| 候选 | 做法 | 风险 |
|---|---|---|
wallet_ledger_id 回填 |
MQ / 定时补偿 | 低;审计字段短暂为 0 |
callback_log 终态写 |
响应后 defer 或 MQ | 中;实时查 log 延迟 |
UserProfitService / GameUserRechargeRtpStatService |
挪到 TransactionLog worker | 低;统计延迟秒级 |
| Redis round 缓存更新 | 响应后 MQ | 中;需 uid+round 有序消费 |
不适合队列:
- 整个
modifyFee/ wallet bet-win(厂商等 balance) provider_tx幂等创建(必须先于 wallet)game_round聚合更新(同局后续回调依赖)
与「全异步回调」方案的边界
若把 wallet 也异步化:
- POP 无法立即拿到 balance → 协议不满足
- 失败重试会与幂等、局状态、钱包状态产生 竞态,需大量补偿与对账
现有设计已采用更合理模式:同步关键路径 + 异步旁路(流水、局终事件)。POP 的问题是 关键路径上叠了审计表 + 多余 HTTP,而非「异步做得不够」。
建议实施顺序(若后续要改代码)
Phase 1 — 热路径减负(推荐先做,改动集中在 slot-pwa)
- 去掉 BET 前
getWallet预检,统一由 wallet 拒单。 finalizeSuccessfulTransfer去掉同步queryBizLog;markWalletSuccess(ledger_id=0)+ 触发异步回填(复用ProviderTxCompensationService或新增provider_tx_ledger_backfill队列)。- 补充 Feature 测试:余额不足、幂等重复、wallet 成功但 ledger 延迟回填。
Phase 2 — 跨服务优化(需 slot-wallet 配合)
POST /api/wallet/update响应增加ledger_id(bet/win 写入后返回)。- slot-pwa 优先读响应字段,补偿任务作兜底。
Phase 3 — DB/观测优化(可选)
- 合并 callback_log / provider_tx 多次 UPDATE。
- 对
modifyTransferInOut加分段耗时 metrics(middleware / provider_tx / round / wallet / finalize),用数据验证 Phase 1/2 收益。
预期收益(量级)
- Phase 1:BET 成功路径 减少 1~2 次 wallet HTTP(通常 20~80ms/次,视网络与 wallet 负载)。
- Phase 2:WIN/FINAL 成功路径再 减少 1 次 queryBizLog。
- 队列化旁路(ledger 回填、统计):不缩短 POP 感知 RTT 的主矛盾,但降低峰值时 wallet/DB 叠加压力。
不能指望:把 callback_log / provider_tx / game_round 全部异步后 POP RTT 接近 quick/jdb——这三张表是 POP 架构的有意同步成本,用于幂等、审计、局聚合。