ISSUE-0074:KPI/sidebar 计数 0 邮箱 — 50k 窗口家族 bug 复发¶
补建说明:本 ISSUE v0.10.112 时已撰写但 commit 时被「批量删 untracked 中文文件」脚本误删(详见 ISSUE-0079 元 bug)。v0.10.117 补建。
现象¶
用户截图 v0.10.109 主面板:
| KPI | 显示值 | 实际值(导出日志验证) |
|---|---|---|
| 商家总数 | 187.1k | 187.1k ✓ |
| 官网采集进度 | 0 / 50.0k | ContactPool 14,812 条;mstage:success 1105 次 |
| 邮箱总数 | 0 | pageLog 660 个 success 含 emails;877 独立邮箱 |
| 手机总数 | 18.7k | 18.7k ✓(地图首页字段,本来就准) |
抓取真在跑 7.3 小时,660 个网站采到了邮箱 — 但 KPI 显示 0,用户以为产品坏了。
根因¶
src/utils/data-counts.ts:46 (v0.10.111 前):
const ROWS_CAP = 50000;
const rows = await selectByQuery('MapTaskData', {
limit: ROWS_CAP,
order: { by: 'id', type: 'desc' }, // ← 按 id 倒序
});
for (const r of rows) {
for (const e of r.emails || []) emailSeen.add(e); // 只统计窗口内
if (r.scrape_status === 2) scrapeDone++;
}
用户场景:187k 商家中:
- 最新 50k 行:用户最近创建任务时一次性 push 入的,全 scrape_status=0 + emails=[](待采集)
- 真采过 emails 的 660 行:id 较小,在 50k 窗口之外
按 id desc 取 top 50k → 窗口被新建任务挤满 → 已采数据全被剔除 → 邮箱去重 0、scrapeDone 0。
src/sections/data/data-view.tsx:62 (CAP = 50000) 同款 bug —— sidebar 邮箱/电话/官网去重列表也都被截。同时这个 5w 行/30s polling 是 187k 商家时商家列表卡顿主因之一。
历史¶
v0.9.35 当时把窗口从 2000 提到 50000,注释写明:"50000 可覆盖到中量级用户全量数据"。 用户 187k 已远超中量级,窗口被穿透。
同类历史: - ISSUE-0018:hasEmailOnly 全表 50k 上限静默截断 - ISSUE-0050:商家列表 chip count 截断 50k - ISSUE-0051:商家列表卡顿 — rows polling 5s
修复(v0.10.112)¶
data-counts.ts¶
-
三个总数改用
countByQuery(瞬间精确,不受 limit 限制): -
去重数改"按 status 分窗扫":
where: { scrape_status: 2 }限 50k —— 这些行才有 emails,且实际产出 ≤ 5wwhere: { scrape_status: 0 }限 50k —— 补充 phone(地图带的,status=0 时也有)
base.ts scrape_status: enableSearch:true 有索引,where 查询走索引快。
data-view.tsx¶
顶层 useRequest 同样改成"status=2 + status=0" 分窗拼接(最多 10w 行),不再被新建任务挤丢已采数据。
通用模式(写进 rule v0.10.113)¶
任何 select limit + order by id desc 派生计数都是 50k 窗口家族的隐性 bug:
- 新行 push 时窗口被占满 → 老数据被无声丢弃
- 解法:能用 countByQuery 走 count 就走;必须扫行的,先 where { 关键状态字段 } 缩小集合
详见 count-vs-scan-window,v0.10.113 起 pre-commit hook 强制扫描。
验证¶
- KPI 邮箱/scrapeDone 立即恢复真实值
- 商家列表 30s polling 仍拉 10w 行(status=2 + status=0),但分两次 5w,去掉了"全表 desc 排序"的 IO 放大
- 邮箱列表 / 电话列表去重数恢复
教训¶
- 每个新引入的
limit N注释里要写"超出 N 后会发生什么" - v0.9.35 注释只说"50000 可覆盖中量级",但没说"超出时会丢什么、怎么发现"
- 同类问题在 ISSUE-0018/0050/0051 都修过一次但不彻底
- 加 docs/rules:扫描类计数优先用 countByQuery + where 索引字段
- 本 ISSUE 文件 v0.10.112 时漏 commit(详见 ISSUE-0079)—— 用户审计才发现,进一步触发 meta-bug rule 沉淀