docs: record CMPP main flow regression
This commit is contained in:
@@ -0,0 +1,65 @@
|
||||
# CMPP 第四阶段后续性能优化方案(暂停实施)
|
||||
|
||||
更新日期:2026-08-21
|
||||
当前决定:本方案仅作为后续工作依据,本轮不再修改性能代码、不再部署性能补丁、不再继续阶梯压测。
|
||||
|
||||
## 1. 当前结论
|
||||
|
||||
测试环境以客户单价 `0.0325 元/计费条`、三运营商混合号码和六个隔离供应商账号执行最终 100 条/秒档。入口 2999/2999 受理,P50/P95/P99 为 `30/63/96ms`,但从首条消息入队到最后一次供应商提交耗时 `167.991 秒`,完整供应商提交吞吐约 `17.85 条/秒`。负载期一度有 1651 条消息停留在 `submit_queued`,说明瓶颈位于 BullMQ 发送 Worker 到 Gateway 之间的逐消息业务处理,不是六条供应商连接的协议容量。
|
||||
|
||||
## 2. 根因说明
|
||||
|
||||
当前发送 Worker 对每条消息依次执行完整消息查询、运营商/省份识别及回写、应用路由与通道连接查询、签名报备候选查询、最终通道签名二次确认、Redis 通道限速、会话累计更新、提交记录创建、消息状态更新、Gateway BullMQ 写入、Redis Stream 发布和批次进度聚合。
|
||||
|
||||
主要放大点:
|
||||
|
||||
1. 一条客户 CMPP 短信对应一个内部 `batchTaskId`,按任务键进行的进度单飞无法跨 2999 个内部任务合并。
|
||||
2. 路由选择已读取签名报备候选,提交前又对最终通道执行一次报备查询。
|
||||
3. 每条短信都在事务内更新所属通道的 `CmppSubmitSession.submitTotal`;六通道形成六个高频热点行。
|
||||
4. 每条短信分别创建提交记录、更新消息并刷新内部任务,数据库往返数量随消息数线性增长。
|
||||
5. 同一个 Gateway 命令当前同步写 BullMQ 和 Redis Stream;两者的恢复职责需要重新确认,不能未经验证直接删除任一边界。
|
||||
6. Worker 并发高于数据库有效并发时只会增加连接等待;单纯继续提高 `API_SEND_WORKER_CONCURRENCY` 不能解决问题。
|
||||
|
||||
## 3. 后续优化顺序
|
||||
|
||||
### P0:补齐可观测证据
|
||||
|
||||
- 在测试环境启用 `pg_stat_statements`,或为发送热路径增加固定低基数耗时指标。
|
||||
- 分别记录消息加载、号码识别、路由、签名报备、限速、提交事务、Gateway 发布和任务进度耗时。
|
||||
- 同时记录 BullMQ waiting/active/completed、数据库连接池等待、事务耗时和六通道命令供给速率。
|
||||
|
||||
### P1:低风险数据库往返收敛
|
||||
|
||||
1. CMPP 单消息内部任务使用已知状态直接更新计数,不再执行整批 `GROUP BY`。
|
||||
2. 将活跃路由、在线通道和签名报备通过条件合并为一次受数据库事实约束的候选查询,取消第二次等价查询;不使用长期进程缓存替代实时停用/撤销状态。
|
||||
3. 提交事务只复用已存在的开放会话 ID;`submitTotal`改为异步批量累计或从提交记录聚合,不让统计字段锁住真实发送。
|
||||
4. 确认 Gateway BullMQ 与 Redis Stream 的消费、重放和死信职责;若存在重复同步发布,改为单一持久入口加可恢复 Outbox。
|
||||
|
||||
### P2:发送微批处理
|
||||
|
||||
- Worker 在 5~10ms 窗口内领取最多 32 或 64 条任务。
|
||||
- 使用 `WHERE id IN (...)` 批量加载消息,按应用、运营商、签名分组预取静态路由候选。
|
||||
- 在线状态、停用状态和最终报备条件在提交批次内重新核验,不建立跨批长期缓存。
|
||||
- 一次短事务批量创建独立 `SmsSubmitRecord`、更新独立 `SmsMessageRecord`,保留每条消息唯一 `submitId`、幂等键、补发和计费关联。
|
||||
- Gateway 命令使用 Redis pipeline/批量发布;任一部分失败必须能根据 PostgreSQL 事实安全补发,不能重复提交或重复计费。
|
||||
|
||||
### P3:容量参数复核
|
||||
|
||||
- 完成 P1/P2 后再让 Worker 并发与数据库连接预算匹配,初始建议按 24~32 个有效数据库槽验证,不直接扩大到更高并发。
|
||||
- 按 PostgreSQL `max_connections` 为 API、Worker、Gateway 回调和运维连接保留独立余量。
|
||||
- 通道 TPS、窗口和六连接继续独立限速,平台业务吞吐不得绕过供应商配置。
|
||||
|
||||
## 4. 正确性约束
|
||||
|
||||
- 正价短信继续执行真实冻结、接受后扣费、拒绝释放和最终失败退款;不得用单价 0 结果外推计费性能。
|
||||
- 计费、报备、路由、消息状态、重试认领和回执幂等必须保留 PostgreSQL 稳定事实。
|
||||
- 不得用进程内缓存保存余额、号码频控、在线状态或最终报备决策。
|
||||
- 多分片长短信继续保留逐片持久化和聚合结果边界;不能为吞吐恢复重复单分片回调。
|
||||
- 任何非向后兼容迁移必须同时提供 PostgreSQL 与运行源码恢复路径。
|
||||
|
||||
## 5. 后续验收方式
|
||||
|
||||
每轮变更均执行:自动化回归 → 正价 smoke → 50 → 100 → 200 → 300 → 500 条/秒。每档必须核对入口受理、Inbox、BullMQ、Gateway Stream、供应商提交、回执、上行、消息终态、冻结/释放/扣费/退款、数据库锁和连接池。出现丢失、重复、账务不一致、队列持续增长或数据库持续不稳定时立即停止升档。
|
||||
|
||||
500 条/秒只有在入口和完整供应商提交均持续达到目标、队列可在限定时间稳定排空且零丢重、账务恒等式成立时才判定通过。
|
||||
|
||||
@@ -4789,3 +4789,5 @@ npm run verify:phase8
|
||||
| TC-CMPP-500-P4-010 | 同一批次并发任务进度刷新 | 并发调用共享一个运行中聚合查询,并在其间有新状态提交时只补一次尾随聚合;最终任务计数与消息终态一致 |
|
||||
|
||||
执行记录(2026-08-21):`P4-001/003/004/005/008/009/010`已由自动化、真实PostgreSQL账务、六通道隔离模拟器及配置回读覆盖;正价smoke通过。`P4-006`在100条/秒档2999/2999入口受理,但最后供应商提交耗时167.991秒,完整链约17.85条/秒,判定失败并按停止线未升200/300/500。`P4-002/007`本轮未追加专项压力场景,保留待测;临时单价、主备和号段规则已恢复,财务流水保留审计。
|
||||
|
||||
主流程回归记录(2026-08-21):停止继续性能优化后,以2条单价325的隔离CMPP短信验证接收、发送、供应商SubmitResp、最终回执和计费,Inbox/Submit/Receipt/Message/Billing均2条闭合,冻结/释放/扣费金额均650。另以实际MessageId注入1条测试机内部Gateway上行事件,上行精确匹配且客户CMPP普通Deliver/ACK最终delivered;Gateway原始CMPP上行解析由全量Go测试和vet通过补充覆盖。临时单价已恢复为0,队列和数据库稳定,允许进入Git发布检查。
|
||||
|
||||
@@ -3851,3 +3851,13 @@ git diff --check
|
||||
- 最终100条/秒30秒使用`1380028/1300028/1890028`:2999/2999受理、零拒绝/节流/连接错误,P50/P95/P99=`30/63/96ms`;移动/联通/电信`998/996/1005`,六通道各承载479至517。中间审计完成扣费1391笔,较修复前同口径805笔显著改善,但仍有1651条`submit_queued`及命令Stream`128/1322`;最终消息2815 delivered、43 failed、141 submitted,冻结/释放各2999、charged 2988、refunded 32,SmsBillingRecord charged 2956/refunded 32,双Stream和数据库最终0/0。首条入队到最后供应商提交167.991秒,完整提交约17.85条/秒,100档仍失败,故未执行200/300/500。
|
||||
- 第四阶段全部隔离正价样本共12040条,金额单价均325;冻结/释放各12040笔、总额3913000,charged 11992笔/3897400,refunded 125笔/40625。SmsBillingRecord最终charged 11867/refunded 125;测试账户从初始1经审计充值20000000、扣费与退款后余额16143226,恒等式`1+20000000-3897400+40625=16143226`成立。
|
||||
- 测试结束已恢复配置:同企业11个应用单价均0;三主通道priority10/非备用、三备priority20/备用,weight均100;3条临时运营商规则已删除并重启Worker。真实充值、扣费、退款和操作日志保留作为审计事实;六连接在线,双Stream 0/0,数据库0等待锁/0 idle in transaction。预生产未发布、未压测、未写入;本地未push,受保护工作区文件未纳入提交或删除。
|
||||
|
||||
## 2026-08-21 第四阶段停止优化后的主流程回归
|
||||
|
||||
- 按用户决定停止继续性能优化,新增`docs/phase-4-send-worker-optimization-plan.md`只记录后续诊断、P0至P3实施顺序、正确性约束和验收停止线。本轮不再修改或部署性能代码,不再执行阶梯压测。
|
||||
- 测试环境标记保持`90345fba...+workspace.p4progress.d82933c344ec`。回归前六个隔离供应商连接在线,API、Worker、Gateway、Nginx、PostgreSQL、Redis均active且健康,数据库无等待锁。仅将测试应用`920001`单价从0临时改为325金额单位,保留前后快照和操作日志。
|
||||
- 客户CMPP最小真实样本生成2条:2/2 SubmitResp成功,P50/P95/P99=`18/20/20ms`。按本轮开始时间和专用号段`1380031`直接核对真实数据库,Inbox completed 2、SmsSubmitRecord accepted 2、SmsReceiptRecord delivered 2、消息delivered 2,客户侧状态回执下游投递delivered 2。
|
||||
- 正价计费闭环通过:两条消息单价325、总金额650;冻结/释放/正式扣费各2笔且金额分别为`-650/+650/-650`,SmsBillingRecord charged 2,无重复、无退款;测试账户余额从16143226精确降至16142576。
|
||||
- 在客户CMPP连接保持在线期间,经测试机内部Gateway事件入口注入一条携带实际`messageId/channelId/phoneNumber`的上行事件。`SmsUplinkMessage`按messageId精确匹配原应用和下发记录,内容“主流程回归上行短信”;客户端捕获`registered=0`普通Deliver并返回ACK,`CmppDownstreamDelivery`最终uplink delivered 1。该步骤验证Gateway事件入口至API入库、匹配和客户下行Deliver/ACK;供应商到Gateway的原始CMPP Deliver解包由随后Gateway全量`go test ./... -count=1`和`go vet ./...`覆盖,本轮未伪称为真实供应商上行报文注入。
|
||||
- 客户连接上线时还收到历史pending回执补投,因此客户端汇总`receipts=283`不能作为本轮回执数量;本轮严格使用开始时间、专用号段、MessageId和数据库外键隔离,新增回执事实为2条、上行为1条。该现象是既有待投递恢复行为,不归因到本次新增短信。
|
||||
- 回归结束已把`920001`单价恢复为0并保存after快照;双Stream pending/lag均0,数据库0等待锁/0 idle in transaction,API/Worker/Gateway均active。真实消息、计费、回执、上行和操作日志保留审计,预生产未变更。
|
||||
|
||||
Reference in New Issue
Block a user