perf: batch gateway submits and isolate callbacks

This commit is contained in:
hectorzhao
2026-08-25 11:39:59 +08:00
parent c6f11014d6
commit 761c123b65
29 changed files with 1912 additions and 193 deletions
+17 -4
View File
@@ -1,7 +1,9 @@
# CMPP 第四阶段后续性能优化方案(暂停实施
# CMPP 第四阶段后续性能优化方案(执行中
更新日期:2026-08-21
当前决定:本方案仅作为后续工作依据,本轮不再修改性能代码、不再部署性能补丁、不再继续阶梯压测
更新日期:2026-08-24
当前状态:已完成第1步“现状复核与恢复准备”、第2步“P0可观测证据”、P1低风险数据库往返收敛和P2计费锁持有时间治理;Worker消息加载微批实测退化已回退,其余P2及P3不再继续
第1步恢复资产:`/opt/cmpp-platform-backups/phase4-step1-20260824T020501Z`。资产包含PostgreSQL自定义格式备份、测试环境运行目录、systemd/Nginx/PostgreSQL/Redis配置、服务状态、迁移清单与SHA-256清单;`pg_restore --list`、tar目录和哈希复核通过。
## 1. 当前结论
@@ -28,6 +30,8 @@
- 分别记录消息加载、号码识别、路由、签名报备、限速、提交事务、Gateway 发布和任务进度耗时。
- 同时记录 BullMQ waiting/active/completed、数据库连接池等待、事务耗时和六通道命令供给速率。
执行结果(2026-08-24):测试环境已启用`pg_stat_statements`;发送Worker已增加11个固定阶段直方图、BullMQ状态、Worker槽位、处理结果和PostgreSQL客户端池状态,标签不含业务实体。正价六通道100条/秒诊断档2999/2999受理,P50/P95/P99=`31/68/233ms`2999个唯一号码首次到达供应商覆盖`92.994秒`,约`32.25条/秒`,仍未达到完整100条/秒。阶段均值中`submit_transaction≈47.4ms`最高,其次`route_lookup≈33.5ms``message_load≈22.7ms``phone_routing≈18.5ms``task_progress≈18.2ms`Worker池waiting峰值3。`pg_stat_statements`显示企业账户advisory lock累计`390.932秒/587次`,开放会话累计UPSERT为`32.293秒/3616次`,因此P1仍应优先消除账户批次锁竞争和会话热点写入,再收敛逐消息查询与进度更新。
### P1:低风险数据库往返收敛
1. CMPP 单消息内部任务使用已知状态直接更新计数,不再执行整批 `GROUP BY`
@@ -35,6 +39,10 @@
3. 提交事务只复用已存在的开放会话 ID;`submitTotal`改为异步批量累计或从提交记录聚合,不让统计字段锁住真实发送。
4. 确认 Gateway BullMQ 与 Redis Stream 的消费、重放和死信职责;若存在重复同步发布,改为单一持久入口加可恢复 Outbox。
执行结果(2026-08-24):CMPP单号码内部任务在首次提交时按已知状态直接更新,结果、回执和超时回调在一条`UPDATE ... FROM`中读取唯一消息当前状态并写精确计数;最终压力窗口中应用产生的任务状态`GROUP BY`为0。路由规则、在线连接和签名运营商报备条件已合并到一次Prisma候选查询,取消最终通道二次报备查询且不缓存实时启停/报备事实。Worker按通道首次读取并复用`OPEN-{channelId}`会话ID,提交事务不再更新`submitTotal`热点行。Go Gateway仅消费`gateway.submit.commands` Redis Stream,其PEL自动认领、逐条ACK、结果Outbox和死信上报均有代码与测试;`gateway.submit.queue`没有消费者,因此发送Worker停止写入该BullMQ副本,压力前后遗留wait均为84119且不再增长。Redis Stream发布前的数据库/队列跨介质崩溃窗口仍没有独立PostgreSQL Outbox自动扫描器,本轮不把该残余风险伪称为已消除,留待P2微批Outbox一起治理。
最终正价六通道100档2999/2999受理,零拒绝、零节流、零连接错误,入口P50/P95/P99=`33/77/179ms`2999个唯一号码首次供应商提交覆盖`99.940秒`,约`30.00条/秒`,低于P0对照`32.25条/秒`,因此完整100条/秒仍失败并停止升档。Worker平均总耗时由约175.1ms降至约123.1ms`submit_transaction`由47.4ms降至26.8ms,路由加两次签名查询的原约47.3ms降为32.4ms,BullMQ发布阶段被消除;但同一企业正价回调的`pg_advisory_xact_lock`仍累计373.373秒/841次、均值443.963ms,继续压住共享数据库并发。这证明P1降低了逐消息往返,却没有解除剩余计费账户锁瓶颈;不应仅凭Worker阶段变快宣称吞吐提升。
### P2:发送微批处理
- Worker 在 510ms 窗口内领取最多 32 或 64 条任务。
@@ -43,6 +51,12 @@
- 一次短事务批量创建独立 `SmsSubmitRecord`、更新独立 `SmsMessageRecord`,保留每条消息唯一 `submitId`、幂等键、补发和计费关联。
- Gateway 命令使用 Redis pipeline/批量发布;任一部分失败必须能根据 PostgreSQL 事实安全补发,不能重复提交或重复计费。
执行结果(2026-08-24):正价CMPP入站冻结仍保持任务、API请求、消息、冻结流水和账户余额同一PostgreSQL事务,但`tenant-account` advisory lock改为在独立业务行写入后、事务提交前才获取。最终正价六通道50档该锁598次累计3.241秒、均值5.420ms;对比P1的373.373秒/841次、均值443.963ms,累计等待下降约99.1%,均值下降约98.8%。
同轮试做5ms、最多20条的Worker消息批量加载,六通道50档完整供应商提交仅约22.20条/秒,低于P1约30.00条/秒;去除批次最慢项栅栏后仍无收益,因此已回退。回退后最终正价六通道50档1499/1499受理,入口P50/P95/P99=`31/77/227ms`;唯一号码首次到达供应商覆盖69.054秒,约21.69条/秒,未达50,按停止线未执100及更高档。最终代码只保留已证明有效的计费锁缩短;PostgreSQL Submit Outbox与批量路由/持久化未继续实施。
最终正价样本1499条,MessageId、号码均1499个且唯一,`unitPrice` min/max均325。冻结/释放各1499笔487175;提交扣费1496笔486200,最终失败退费21笔6825`SmsBillingRecord` charged1475笔479375、refunded21笔6825,净扣与账户流水一致。全部压力档均使用325正价,没有执0计费压测。结束后仅按测试前快照恢复10个应用单价为0并删除3条临时号段规则,恢复后未再压测。
### P3:容量参数复核
- 完成 P1/P2 后再让 Worker 并发与数据库连接预算匹配,初始建议按 24~32 个有效数据库槽验证,不直接扩大到更高并发。
@@ -62,4 +76,3 @@
每轮变更均执行:自动化回归 → 正价 smoke → 50 → 100 → 200 → 300 → 500 条/秒。每档必须核对入口受理、Inbox、BullMQ、Gateway Stream、供应商提交、回执、上行、消息终态、冻结/释放/扣费/退款、数据库锁和连接池。出现丢失、重复、账务不一致、队列持续增长或数据库持续不稳定时立即停止升档。
500 条/秒只有在入口和完整供应商提交均持续达到目标、队列可在限定时间稳定排空且零丢重、账务恒等式成立时才判定通过。