181 lines
9.1 KiB
Markdown
181 lines
9.1 KiB
Markdown
<!-- 454dc363-a500-4be5-a576-c2dd86e5515a -->
|
||
---
|
||
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/controller/CashController.php) 本身很薄,性能问题在 [`slot-pwa/app/pop/logic/CashLogic.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 写 + 2~3 次 wallet HTTP**。
|
||
- **已有异步**:用户交易流水(`TransactionLogService::log` → `pwa_transaction_log` worker)、局终事件(`GameRoundEventService::publishFinalSettled`)、试玩活动(`TrialRewardNotifyService` MQ publish)。
|
||
|
||
因此:**可以队列化的是「不影响 POP 响应正确性的旁路写/追踪」**;**不能队列化的是幂等、钱包、局状态校验**。
|
||
|
||
---
|
||
|
||
## 当前同步链路(成功路径 BET)
|
||
|
||
```mermaid
|
||
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`](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`](slot-pwa/app/service/game/ProviderTxCompensationService.php) |
|
||
| 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 路径双查钱包:**
|
||
|
||
```686:711:slot-pwa/app/pop/logic/CashLogic.php
|
||
$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:**
|
||
|
||
```829:839:slot-pwa/app/pop/logic/CashLogic.php
|
||
$ledgerQuery = $this->walletGatewayService->queryBizLog(
|
||
$transferContext->uid(),
|
||
$finalizeContext->walletRequestId,
|
||
ProviderTxService::resolveWalletBizType($finalizeContext->txType)
|
||
);
|
||
$walletLedgerId = (int) ($ledgerQuery['ledger_id'] ?? 0);
|
||
$this->providerTxService->markWalletSuccess(..., $walletLedgerId);
|
||
```
|
||
|
||
- 钱包 `update` 响应 [`WalletEntity`](slot-pwa/app/entity/WalletEntity.php) **不含 `ledger_id`**,故又调 `queryBizLog`,**再多 1 次 HTTP**。
|
||
- 两条路(二选一或组合):
|
||
- **A(推荐)**:扩展 [`slot-wallet`](slot-wallet/app/api/logic/WalletLogic.php) `update` 响应带 `ledger_id`,POP 直接写入,去掉热路径 `queryBizLog`。
|
||
- **B(低改动)**:热路径 `markWalletSuccess(ledger_id=0)`,通过现有 `ProviderTxWalletCompensate` 或新建轻量 MQ consumer 异步回填 `wallet_ledger_id`(与 reward_grant 文档中 queryBizLog 补偿模式一致)。
|
||
|
||
### 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)
|
||
|
||
1. 去掉 BET 前 `getWallet` 预检,统一由 wallet 拒单。
|
||
2. `finalizeSuccessfulTransfer` 去掉同步 `queryBizLog`;`markWalletSuccess(ledger_id=0)` + 触发异步回填(复用 `ProviderTxCompensationService` 或新增 `provider_tx_ledger_backfill` 队列)。
|
||
3. 补充 Feature 测试:余额不足、幂等重复、wallet 成功但 ledger 延迟回填。
|
||
|
||
### Phase 2 — 跨服务优化(需 slot-wallet 配合)
|
||
|
||
1. `POST /api/wallet/update` 响应增加 `ledger_id`(bet/win 写入后返回)。
|
||
2. slot-pwa 优先读响应字段,补偿任务作兜底。
|
||
|
||
### Phase 3 — DB/观测优化(可选)
|
||
|
||
1. 合并 callback_log / provider_tx 多次 UPDATE。
|
||
2. 对 `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 架构的有意同步成本,用于幂等、审计、局聚合。
|