[fix] 低频采集两类失败: bms_order OOM循环 + return_tracking BMS日期格式收紧 #335

Closed
opened 2026-10-19 09:58:20 +08:00 by tanzk · 1 comment
Owner

现象:低频采集两类持续失败(用户报「低频任务似乎没有执行」,实为执行即失败):

  • bms_order:10-13 起每次失败,error=「服务重启清理:僵尸running任务」,最后成功 10-13 00:00
  • return_tracking:10-11 起每次失败,error=BMS 导出 400 JSON parse error,最后成功 10-11 00:00
  • 其余 9 类低频(refund/ticket/wallet/device_profile/device_combo 等)正常

根因(生产实证 2026-10-19):

  1. bms_order:dmesg 实锤 Memory cgroup out of memory: Killed process (gunicorn) anon-rss:4161640kB。
    导出被 BMS 硬截断 100,003 行(时间筛选不生效,已知行为),_fetch.py 用 openpyxl 全量读 +
    裁剪回写 + 复读校验 = 三次全量加载,峰值 > 4GiB 容器 memcg → OOM 杀 gunicorn → 容器重启 →
    lifespan 僵尸清理把在跑任务标 failed → 每小时循环。
  2. return_tracking:BMS 后端 ~10-11 收紧日期反序列化,只认 yyyy-MM-dd HH:mm:ss;
    退货跟踪页前端将日期序列化为 ISO-UTC(2026-10-18T23:35:02.000Z)发导出接口 → 400。
    我方填入格式正确(%Y-%m-%d %H:%M:%S 北京时间),非我方代码问题。

方案:

  • bms_order:_fetch.py 大文件读取换 calamine 引擎(Rust 流式,内存降一个量级;data_parser 已用);
    容器内存 4GiB→8GiB(主机余量 20G)双保险
  • return_tracking:导出失败时记录请求 URL/payload(诊断),再按实测端点做 Playwright route 改写
    (ISO → yyyy-MM-dd HH:mm:ss 北京时间),不改 BMS

验收标准:

  • bms_order 手动触发一次全流程成功(非僵尸失败),RSS 峰值 < 4GiB
  • return_tracking 手动触发一次成功,写入行数正常
  • 容器内存上限生效(docker inspect = 8GiB)
  • 标准发布 + 连续两轮小时调度无失败
  • 凭证关单
**现象**:低频采集两类持续失败(用户报「低频任务似乎没有执行」,实为执行即失败): - `bms_order`:10-13 起每次失败,error=「服务重启清理:僵尸running任务」,最后成功 10-13 00:00 - `return_tracking`:10-11 起每次失败,error=BMS 导出 400 JSON parse error,最后成功 10-11 00:00 - 其余 9 类低频(refund/ticket/wallet/device_profile/device_combo 等)正常 **根因**(生产实证 2026-10-19): 1. bms_order:dmesg 实锤 `Memory cgroup out of memory: Killed process (gunicorn) anon-rss:4161640kB`。 导出被 BMS 硬截断 100,003 行(时间筛选不生效,已知行为),`_fetch.py` 用 openpyxl 全量读 + 裁剪回写 + 复读校验 = 三次全量加载,峰值 > 4GiB 容器 memcg → OOM 杀 gunicorn → 容器重启 → lifespan 僵尸清理把在跑任务标 failed → 每小时循环。 2. return_tracking:BMS 后端 ~10-11 收紧日期反序列化,只认 `yyyy-MM-dd HH:mm:ss`; 退货跟踪页前端将日期序列化为 ISO-UTC(`2026-10-18T23:35:02.000Z`)发导出接口 → 400。 我方填入格式正确(%Y-%m-%d %H:%M:%S 北京时间),非我方代码问题。 **方案**: - bms_order:`_fetch.py` 大文件读取换 calamine 引擎(Rust 流式,内存降一个量级;data_parser 已用); 容器内存 4GiB→8GiB(主机余量 20G)双保险 - return_tracking:导出失败时记录请求 URL/payload(诊断),再按实测端点做 Playwright route 改写 (ISO → yyyy-MM-dd HH:mm:ss 北京时间),不改 BMS **验收标准**: - [x] bms_order 手动触发一次全流程成功(非僵尸失败),RSS 峰值 < 4GiB - [x] return_tracking 手动触发一次成功,写入行数正常 - [x] 容器内存上限生效(docker inspect = 8GiB) - [x] 标准发布 + 连续两轮小时调度无失败 - [x] 凭证关单
Author
Owner

关闭凭证核验 (2026-10-19)

# 验收标准 核验方式(可复现) 结果
1 bms_order 手动触发一次全流程成功(非僵尸失败),RSS 峰值 < 4GiB psql -c "SELECT id,status,total_rows,inserted_rows FROM bms_sync_logs WHERE id=10295" → `10295 success
2 return_tracking 手动触发一次成功,写入行数正常 psql -c "... WHERE id=10245" → `10245 success
3 容器内存上限生效 docker inspect csctalon-backend --format '{{.HostConfig.Memory}}' → 8589934592 (8.0 GiB) ✅
4 标准发布 + 连续两轮调度无失败 发布 git-20261019045912 → git-20261019084617(含 38f8042 / 4cb7d7c / b4e1d56 / e061250);近 1h 成功轮 = 0、失败 = 0(16:57 半开探测轮 8 源全 success;17:00 定时槽位 sync_20261019170222_9p938p 2/2 成功) ✅
5 凭证关单(本评论) — ✅

根因与修法(生产实证)

① bms_order OOM 循环 —— 内核实锤(修复前最后一次 07:53:25 UTC):
Memory cgroup out of memory: Killed process (postgres) total-vm:2149928kB anon-rss:1912936kB
根因:10 万行单事务单语句 upsert 使 postgres backend 匿名内存膨胀至 ~1.9GiB,撞破 db 容器
2GiB memcg 限额被 OOM kill → postmaster 全库 recovery → 后端 第1次导入遇到 DB 瞬态故障 (recovery mode) 重试仍撞 → 本轮未完成 标记失败(每小时循环)。
修法(e061250):upsert_bms_orders 分批 2000 行/commit,单批 ~3.8 万绑定参数(<65535),
WAL/事务内存随 commit 释放;upsert 幂等,中途失败重跑安全。
修复后同一规模(99980 行)导入 2.5 分钟完成,内核无新增 postgres OOM(grep 计数 = 18)。

② return_tracking 导出 400 —— BMS 后端 ~10-11 收紧日期反序列化只认 yyyy-MM-dd HH:mm:ss;
退货跟踪页前端将日期序列化为 ISO-UTC(2026-10-19T01:41:07.000Z)发导出接口 → 400
JSON parse error: Cannot deserialize ... expected format "yyyy-MM-dd HH:mm:ss"。
修法(4cb7d7c + b4e1d56):对白名单源注册 page.route 拦截,导出前将请求体内完整 ISO 形态
日期串改写为北京时 yyyy-MM-dd HH:mm:ss 放行(非 POST/非 JSON/无 ISO 一律原样放行);
route 匹配模式与导出响应监听器判据对齐为 export|download(退货跟踪导出端点
.../customer-return-tracking/downLoadExcel 不含 export,仅含 download)。

验收项勾选

已同步将正文 - [ ] 勾为 - [x]。

## 关闭凭证核验 (2026-10-19) | # | 验收标准 | 核验方式(可复现) | 结果 | |---|---|---|---| | 1 | `bms_order` 手动触发一次全流程成功(非僵尸失败),RSS 峰值 < 4GiB | `psql -c "SELECT id,status,total_rows,inserted_rows FROM bms_sync_logs WHERE id=10295"` → `10295 | success | total=99980 ins=98122 upd=1858`;`cat /sys/fs/cgroup/system.slice/docker-$(docker inspect -f '{{.Id}}' csctalon-backend).scope/memory.peak` → **2087587840 B ≈ 1.94 GiB** (< 4GiB) | ✅ | | 2 | `return_tracking` 手动触发一次成功,写入行数正常 | `psql -c "... WHERE id=10245"` → `10245 | success | rows=5420`(manual);`id=10299` → `10299 | success | rows=5`(half_open);执行日志含 `[bms_order]/[return_tracking] 导出请求 ISO 日期改写 2 处` | ✅ | | 3 | 容器内存上限生效 | `docker inspect csctalon-backend --format '{{.HostConfig.Memory}}'` → `8589934592` (**8.0 GiB**) | ✅ | | 4 | 标准发布 + 连续两轮调度无失败 | 发布 `git-20261019045912 → git-20261019084617`(含 38f8042 / 4cb7d7c / b4e1d56 / e061250);近 1h 成功轮 = **0**、失败 = **0**(16:57 半开探测轮 8 源全 success;17:00 定时槽位 `sync_20261019170222_9p938p` 2/2 成功) | ✅ | | 5 | 凭证关单(本评论) | — | ✅ | ### 根因与修法(生产实证) **① bms_order OOM 循环** —— 内核实锤(修复前最后一次 07:53:25 UTC): `Memory cgroup out of memory: Killed process (postgres) total-vm:2149928kB anon-rss:1912936kB` 根因:10 万行**单事务单语句** upsert 使 postgres backend 匿名内存膨胀至 ~1.9GiB,撞破 db 容器 2GiB memcg 限额被 OOM kill → postmaster 全库 recovery → 后端 `第1次导入遇到 DB 瞬态故障 (recovery mode)` 重试仍撞 → `本轮未完成` 标记失败(每小时循环)。 修法(`e061250`):`upsert_bms_orders` 分批 **2000 行/commit**,单批 ~3.8 万绑定参数(<65535), WAL/事务内存随 commit 释放;upsert 幂等,中途失败重跑安全。 修复后同一规模(99980 行)导入 2.5 分钟完成,内核**无新增 postgres OOM**(grep 计数 = 18)。 **② return_tracking 导出 400** —— BMS 后端 ~10-11 收紧日期反序列化只认 `yyyy-MM-dd HH:mm:ss`; 退货跟踪页前端将日期序列化为 ISO-UTC(`2026-10-19T01:41:07.000Z`)发导出接口 → 400 `JSON parse error: Cannot deserialize ... expected format "yyyy-MM-dd HH:mm:ss"`。 修法(`4cb7d7c` + `b4e1d56`):对白名单源注册 `page.route` 拦截,导出前将请求体内完整 ISO 形态 日期串改写为北京时 `yyyy-MM-dd HH:mm:ss` 放行(非 POST/非 JSON/无 ISO 一律原样放行); route 匹配模式与导出响应监听器判据对齐为 `export|download`(退货跟踪导出端点 `.../customer-return-tracking/downLoadExcel` 不含 `export`,仅含 `download`)。 ### 验收项勾选 已同步将正文 `- [ ]` 勾为 `- [x]`。
tanzk closed this issue 2026-10-19 17:26:59 +08:00
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: tanzk/csctalon#335