fix: prevent daily report refresh timeouts
CSS quality / css-quality (push) Has been cancelled

This commit is contained in:
hectorzhao
2026-09-08 17:16:43 +08:00
parent 2a9d03be2e
commit ebb185b22b
7 changed files with 1031 additions and 106 deletions
@@ -2232,3 +2232,10 @@
## 2026-09-08 分页交互补充
签名质量检测四个Tab的每页数量选择移至各自列表底部,与上一页、下一页和页码跳转放在同一分页区;保留独立日期、筛选和每页10/25/50/100,默认25,空结果也可选择数量。通道报备明细增加相同四档容量,默认25;修改容量回到第一页、清空当页勾选、保留已应用筛选,使用真实服务端分页,较早请求的成功或失败不得覆盖最新查询结果。本节补充前述运营修复要求;不更改后端接口、端口或数据模型,详见[运营修复设计](operations-fixes-20260908.md#分页交互补充2026-09-08)。
## 2026-09-08 报表生成可靠性补充
本节明确 5.19.1 中的事务范围为“每个日期独立事务”,四日窗口不共用一个长事务。对账、应用/通道利润及四类质量报表在同一日期内原子重建;失败保留该日旧报表,继续处理窗口内其他日期,只有四日全部成功才标记当天完成,失败下次检查仍可重试。默认启动 15 秒后执行、每小时检查,不承诺固定分钟触发。
应用利润成本只扫描目标日原短信关联的全部 accepted 提交,包括跨日补发;收入、成本快照、分片审计优先和历史成功回执兼容语义不变。通道维度仍按实际提交日统计。报表使用专属有限事务预算和数据库日锁,不调整发送/计费事务。T-5 及更早数据不被正常日任务改写,历史缺口须单独授权补齐;本轮不新增自动历史重算。实现与验收见 [日报生成超时修复](report-generation-reliability-20260908.md)。
@@ -0,0 +1,25 @@
# 日报生成超时修复
维护日期:2026-09-08。适用于对账单、利润报表、发送质量报表的共同生成任务。本文补充[需求 5.19.1](first-version-development-requirements.md#5191-报表对账)的执行与失败恢复机制,不改变收入、成本、计费条数、质量维度及 T+1 / T-4~T-1 业务口径。实施结果见[测试进度](testing-progress.md)。
## 证据与影响
预生产版本 `633ba597754c1b89943ea3a819f45042f41b019a` 在 2026-09-08 16:4016:51 只读核验时,三类报表最新日期均为 9 月 2 日,9 月 3~7 日有短信却缺报表。保留日志有 93 次生成失败,最近 16:12 的错误为 Prisma 事务上限 5000ms、实际已耗时 7510ms。应用利润成本 CTE 未限定原短信日期;9 月 4 日只有 2492 条短信,却扫描 103450 条 accepted 提交。仅执行原 SELECT 的实际计划耗时 9090.976ms;添加原短信日期范围的只读候选耗时 90.515ms。单次对照可能受缓存影响,不代表完整任务提速比例或已经上线。
同日期三类报表共用事务,应用利润耗时使事务过期,在下一条通道利润语句处报错,已插入的对账单也回滚。原滚动任务遇到一个日期失败即退出,阻止后续日期执行。
## 最小修复设计
1. 应用利润的成本 CTE 在关联 `SmsMessageRecord` 后限定 `message.queuedAt >= startAt AND message.queuedAt < endAt`,继续累计这些短信的全部 accepted 提交,包括跨日补发。分片审计优先、无分片审计才兼容明确成功的历史回执;不按当前通道价格倒算,不改通道维度的实际提交日归属。
2. 每个日期仍在一个独立事务内原子重建对账、应用/通道利润及四个质量维度;任何失败都保留该日期旧报表,不能先删后在事务外插入。
3. 报表专用事务默认上限 30000ms、获取连接最长等待 5000ms。`REPORT_REFRESH_TRANSACTION_TIMEOUT_MS` 可设正整数毫秒,非法值回退默认,上限 120000ms;不修改其他业务事务的全局设置。事务内设置同上限的 PostgreSQL `statement_timeout`,避免单条异常 SQL 无界运行。
4. 同日期使用 PostgreSQL 事务级 advisory lock(固定报表命名空间 + YYYYMMDD)。取锁失败视为该日期未完成,不删除报表;事务结束自动释放锁,下一调度周期可重试。日锁同时保护不同 API 实例和同进程手动服务调用。
5. T-4~T-1 逐日执行;记录每个失败日期及错误后继续其他日期,最后汇总失败并向调用者抛错。只有四天全部成功,调度器才记录本日刷新完成;部分失败保持下个小时重试资格,不静默报告成功。启动 15 秒后的首次执行和默认每小时检查保持;销毁服务同时清除启动与周期定时器。
6. 本轮不新增迁移、持久化任务表、API 写入口或自动历史回算。T-5 及更早报表不被日常任务改写;已发现的历史缺口须在修复部署后按明确授权、日期清单单独补齐。进程重启仍按现有四日窗口执行,不能声称历史缺口永久恢复机制已实现。
## 验收与交付边界
- 定向回归覆盖日期边界、成本扫描范围、部分失败继续后续日期、失败重试/成功去重、并发、原子回滚和生命周期停止;真实 PostgreSQL 验证跨日尝试、部分分片成功及历史回执兼容、三类报表及重复生成一致性。
- 真实 SQL 性能与结果对照在预生产只允许 SELECT、限时和只读事务,不调用生成服务或写业务表。完整生成测试使用本机独立 PostgreSQL 测试库,不能以 mock 通过代替真实数据库证据。
- 执行 API 全量、类型/生产构建及现有相关质量门禁。没有前端改动,不改变页面、权限、查询 API、端口或租户过滤。
- 本轮授权修改代码并本地提交;不推送、不部署两环境、不补跑预生产报表、不发送短信或修改业务配置。测试数据仅在隔离本地测试库构造,结果与未验证项记入进度。
+17
View File
@@ -5254,3 +5254,20 @@ OPS0908-01至07已按本轮范围验证;精确证据见testing-progress.md对
| OPS-PAGE0908-03 | 打开通道报备明细,设置筛选,到第2页勾选后依次改变四档容量 | 默认25;真实接口page=1及正确pageSize,总数/页数/行数一致;筛选与URL范围保留,旧勾选清空,不执行状态或发送操作 |
| OPS-PAGE0908-04 | 隔离测试让旧25条查询晚于新100条返回,分别返回成功和失败 | 旧结果和旧错误均不能覆盖最新查询;当前失败显示真实错误;组件卸载后旧请求失效 |
| OPS-PAGE0908-05 | 在1600×1000、1366×768、390×844检查两页面,刷新/跨路由,回归原公共分页消费者 | 下拉完整可见、可操作,表格内部滚动不造成页面整体溢出;旧消费者不新增容量控件;跳转输入在真实页码改变后重置,返回旧页不恢复未提交草稿 |
## 2026-09-08 日报生成超时与失败恢复
依据 [日报生成超时修复](report-generation-reliability-20260908.md)。涉及造数和失败注入的用例仅在本地独立 PostgreSQL 测试库执行;预生产仅限时只读核验,短信发送、业务配置变更与历史补跑不包含在验收授权中。
| 编号 | 场景 | 预期结果 |
|---|---|---|
| TC-DAILY-0908-01 | 大量窗口外历史记录下,对照目标日期应用利润原 SELECT 与限定原短信日期后的 SELECT | 除生成时间外结果字段逐项一致;成本扫描受原短信日期限定,记录实际执行计划和耗时,不能仅比较返回行数 |
| TC-DAILY-0908-02 | 原短信日后跨天、跨通道 accepted 补发,最终收入归最新成功提交 | 应用累计所有关联成功分片成本,仍归原短信日;通道按实际提交日归属且收入不重复;不存在按提交日期裁剪应用成本 |
| TC-DAILY-0908-03 | 长短信部分分片成功、审计存在但零成功、无审计但旧回执成功、失败/未知提交 | 审计优先;零成功不退回历史回执计整条成本;只有无审计且明确旧成功回执时按计费分片数兼容 |
| TC-DAILY-0908-04 | 北京时间零点、跨月/跨年、目标日起点及终点边界 | 每次恰好 T-4~T-1,起点包含、终点排除;不生成当天,不改写 T-5 更早报表 |
| TC-DAILY-0908-05 | 某日对账重建后注入真实 SQL 错误,其他三日继续,之后恢复再执行 | 失败日三表旧结果全部保留、无半成品;其余日期成功;返回含失败/成功日期的错误,不标记当天完成;重试成功后相同日期无重复 |
| TC-DAILY-0908-06 | 同实例重叠触发、两个事务/服务实例同时刷新同日 | 同实例定时入口防重入;数据库日锁未获取时不删除报表,明确失败并保留重试资格;锁释放后可重算且无重复 |
| TC-DAILY-0908-07 | 默认/有效/非法/超大事务超时配置,实际 PostgreSQL 限时及事务结束 | 默认 30000ms、maxWait 5000ms;非法值回退默认、最大 120000msstatement_timeout 仅当前事务有效,其他业务事务不改变 |
| TC-DAILY-0908-08 | 失败后次周期、成功后同日重复周期,以及启动 15 秒内销毁服务 | 失败继续重试、全成功后同日跳过;销毁清除启动及周期定时器,无销毁后新任务 |
| TC-DAILY-0908-09 | 真实生成后调用报表查询/汇总/分页及 CSV | 数据与 PostgreSQL 一致;既有日期/租户/维度筛选和金额精度不变,API 无假成功或静态数据 |
+14
View File
@@ -4750,3 +4750,17 @@ git diff --check
- 验收脚本问题与边界:独立Vite开发服务器曾出现本机连接超时,改用同一验收进程创建/关闭生产预览后通过;一次脚本假定Escape关闭Select导致等待超时,按当前触发按钮关闭方式修正后全流程通过,未扩改Select。错误/乱序以隔离测试验证;未报备真实非空、热力图超过100个维度、全部旧分页消费者的逐页人工验收未覆盖。真实API数据来自现有服务及PostgreSQL查询;本轮不修改数据库,未另取得测试受限数据库配置作直接SQL对账。
- 证据位于%TEMP%/cmpp-pagination-20260908before-quality/report截图、acceptance.json、filter-acceptance.json、三视口截图、frontend-tests.log、build.log、format.log、lint.log和开工diff快照。截图/脚本/凭据不进入提交。临时预览已关闭。
- 交付边界:本地代码、需求、设计和用例完成;本节随本轮精确文件/追加hunk作本地提交,提交号见Git记录。不推送、不测试部署、不预生产部署;不发送/补发/重投/入队短信,不变更余额、通道、客户配置或恢复管理员。测试环境仍运行旧2c228a9,本地提交不等于已上线。
## 2026-09-08 17:14 日报生成超时修复与本地提交前验收
- 授权:修改代码并本地提交,不推送、不测试部署、不预生产部署、不补跑历史报表。开工 main/HEAD 为 2a9d03be2edc06dbd9a494e971e6b04a8336619f,暂存空;只读 ls-remote 回读远端 main 为 50ae37242bc33acf1038fb62618fc4cd95409956,本地领先 3 提交。17 个已有脏跟踪文件及原未跟踪发布工具/诊断脚本继续保护;涉及三份旧脏文档仅追加并精确暂存本轮增量。
- 需求/设计:[可靠性补充](first-version-development-requirements.md#2026-09-08-报表生成可靠性补充)、[日报生成超时修复](report-generation-reliability-20260908.md)TC-DAILY-0908-0109。明确单日期三类报表原子事务、T-4~T-1 和既有财务口径,历史缺口不自动扩窗。
- 根因证据:预生产 633ba597754c1b89943ea3a819f45042f41b019a 三类表最新 reportDate 均为 9 月 2 日;9 月 3~7 日源短信非空。保留日志 93 次事务超时,9 月 8 日 16:12 的上限 5000ms、实际 7510ms。成本 CTE 扫全部 accepted 历史提交,单条 SELECT 实测 9090.976ms 已超过整日报表事务预算。
- 实现:应用成本 CTE 按原短信 queuedAt 限定日期,保留全部跨日提交的成功分片成本;日事务默认 30000ms、maxWait 5000msREPORT_REFRESH_TRANSACTION_TIMEOUT_MS 正整数覆盖且最大 120000ms,并设置事务局部 statement_timeout。删除前获取 PostgreSQL 日期 advisory lock;单日失败保留旧报表并继续其他日期,最终汇总抛错,只有全成功才标记本日完成。启动/周期定时器销毁清理,调度日与窗口共用同一时刻。未新增迁移/API 写入口/历史重算队列。两份已修改 TS 按当前 Prettier 门禁格式化,并显式忽略原 refundCents 解构值以修复该文件既有 lint 错误,列表/CSV 语义不变。
- 预生产只读结果一致性:在同一 REPEATABLE READ READ ONLY 事务中、statement_timeout=15000ms9 月 4 日原 SELECT 8919ms、当前候选 SELECT 100ms;两行全部结果列(含金额,按数组避免同名列丢失)摘要均为 576592fc45b7f3137882229321eaf905d1e9bba12631ccfe1a0b2016c759231b。仅移除 INSERT 头部执行 SELECT,防写入检查通过;事务最终 ROLLBACK,不调用线上生成服务。这是单日查询对照,缓存可能影响耗时,不宣称完整任务同比提速或全日期金额对账已完成。
- 代码验收:API 全量 64 套 694 项通过(工作区包含原保护 metrics 的 1 项额外测试,未夹带提交);最终报表定向 1 套 29 项通过。API TypeScript 生产构建、前端 TypeScript(既有 lint 组成)、changed-code Prettier/ESLint、结构检查、Stylelint、CSS 治理及 15 项工具测试、安全/部署静态门禁、git diff --check 通过。npm 当前不在 PATH,执行 package.json 对应 Node CLI;未安装依赖。API 命令为 node node_modules/jest/bin/jest.js --runInBand、node node_modules/typescript/bin/tsc -p tsconfig.build.json。
- 真实数据库/API:新增 tools/testing/verify-report-refresh.mjs,强制独立 REPORT_TEST_DATABASE_URL、loopback 地址及 cmpp_report_test_ 数据库前缀,不使用业务 DATABASE_URL。独立本机 PostgreSQL 8 组通过:7 个报表维度与完整自然日、跨日补发/审计优先/历史兼容、重复幂等、真实 SQL 失败三表回滚且后续日期成功、跨连接日期锁、超过旧 5 秒的成功事务、200ms 限额超时回滚恢复、真实 ReportsController HTTP 筛选/分页/汇总及三类 CSV。10 条消息/10 次提交的应用 9 月 4 日收入 4000、成本 800、利润 3200(0.0001 元整数单位)一致;单次注入 5.2 秒延迟后单日事务实际 5248ms 成功。金额源列采用生产 bigint,报表表完整列/类型/唯一约束,源表只建查询用列,未做全库迁移验收。测试 schema 残留 0,临时 PostgreSQL 已停止;测试资料目录保留。
- 证据:%TEMP%/cmpp-report-fix-20260908/{baseline.json,api-tests.log,report-tests-final.log}%TEMP%/cmpp-report-diagnosis-20260908/{profile-select.cjs.result.jsonl,compare-candidate.cjs.result.jsonl}%TEMP%/cmpp-report-pg-1788858316792/integration.log。脚本、源库结果未输出凭据或短信正文。
- 未执行:预生产完整任务写入验收/自然定时观察、两环境部署、历史缺口补齐、完整应用登录与浏览器。本轮无前端改动;本地 HTTP 是独立真实报表 Controller/Service/PG,不代表完整认证/UI 验收。旧 api/tools/verify-report-recalculation.ts 含过期金额断言和业务造数,不执行也不在本轮改写。T-5 更早缺口仍须部署修复后单独授权补齐。
- 交付:本地报表代码、测试工具、需求/设计/用例和本节一起按精确 7 文件/文档增量提交,提交号以 Git 为准;未推送、未测试部署、未预生产部署、未短信发送/补发/重投/入队,未修改业务余额、通道、客户配置或管理员。提交前保护校验确认全部已有跟踪修改未被覆盖。