导读:① 起点案例 · ② 全链路五段拆解(含机制+成因,最详细)· ③ 拐点实证 · ④ 慢路损失多大 · ⑤ 总根因(僵尸 tier-1)· ⑥ 提升点 · ⑦ 数据快照(含 cron 3→2 复核)· ⑧ 队列三问(Leg B 入队 / BC 极限 / 快车道 100 条)· ⑨ 2026-06-16 B/C 落地成效 + 真尾巴 + pri 体系(最新)
输入是一条抖音视频链接 douyin.com/video/7648553920499141739。反查得到:
| 项 | 值 |
|---|---|
| 账号 | 歆笛的装修日记(知识号) |
| 分层 | Tier-1 优质号 |
| 累计评论 / 高意向 | 184 / 15 (整号 29 条视频累计;非单条视频) |
| 近 7 天高意向 | 11 条(全压在近 7 天,是个正在爆量的号) |
| 高意向触达状态 | 15 条里 13 条还没触达(analyzed) |
逐条看这 15 条的「发言→抓到」延迟,发现一个反常:同样是 Tier-1,有的 1 分钟抓到,有的 29–48 分钟,还有的拖到 几天。这就是整条调查的起点。
| # | 用户 | 发言 | 抓到 | 延迟 | 评论摘要(意向分) |
|---|---|---|---|---|---|
| 1 | 大海 | 05-19 07:32 | 05-21 13:38 | 2.3 天 | 帮忙发一份吧 (85) |
| 2 | @~#~? | 05-19 09:39 | 05-21 13:38 | 2.2 天 | 要一份 (85) |
| 3 | 小肥猫 | 05-19 09:47 | 05-21 13:38 | 2.2 天 | 我要一份 (85) |
| 4 | 风少 | 05-20 20:50 | 05-21 13:38 | 16.8 时 | 100平15万预算求方案 (93) |
| 5 | ,,, | 06-09 22:32 | 06-14 07:49 | 4.4 天 | 下个月准备装修,真的需要 (85) |
| 6 | 🍒奶黄包 | 06-12 00:29 | 06-14 01:24 | 2.0 天 | 我也需要,准备装修 (85) |
| 7 | 英儿 | 06-12 08:33 | 06-14 01:24 | 40.9 时 | 来一套资料 (85) |
| 8 | 安安 | 06-12 11:35 | 06-14 01:24 | 37.8 时 | 258平大概多少钱 (93) |
| 9 | 燕飞狐 | 06-13 11:05 | 06-14 04:37 | 17.5 时 | 最近准备装修,求份资料 (85) |
| 10 | 双子娃娃 | 06-13 23:52 | 06-14 01:24 | 1.5 时 | 71平学区房,求资料 (93) |
| 11 | 龍凤呈祥 | 06-14 08:08 | 06-14 08:37 | 29 分 | 一百平小三室,求资料 (100) |
| 12 | 用户5080 | 06-14 09:44 | 06-14 10:14 | 30 分 | 下月要装修了 (85) |
| 13 | 古悠然 | 06-14 15:49 | 06-14 15:51 | 1 分 | 准备装修,求全套资料 (85) |
| 14 | 信仰 已触达 | 06-14 16:49 | 06-14 16:57 | 7 分 | 多少平米的定制 (85) |
| 15 | 二宝妈 已触达 | 06-15 00:35 | 06-15 00:37 | 2 分 | 求资料,毛坯房 (85) |
汇总:中位 17.5 时 · 最快 1 分 · 最慢 4.4 天。趋势——越往后(6-14 下午起)越快;5 月那几条 2 天级、6-14 凌晨 01:24 那批是「断档恢复一次性补抓」的积压。这个「越来越快」的拐点,③ 坐实。
一条评论 T0 用户发出 → 进我们库,走完五道关。先看每段实测耗时,再把最关键的 B、C 两段彻底讲透(抓取机制、冷视频成因都在这一节,不用往后翻):
用户点「发送」后,这条评论要先在抖音自己的系统里能被 API 查到。我们管不着,但很快——用最快的端到端样本(1 分钟)倒推,这段只占几秒。
"抓这条视频" = worker(干抓取活的程序)去这条视频底下把评论捞一遍。它什么时候被抓到,完全由我们的抓取机制决定 —— 有两条路,下面讲全。
| 路 | 怎么触发 | 覆盖谁 | 节奏 |
|---|---|---|---|
| 🐢 整号慢路 | 这条视频所在的「号」被整体抓一遍时,顺带把它主页前 20 条视频都抓了 | 所有号(按 tier 分快慢) | tier-1 号 ≥30 分 / tier-0 号 ≥7 时 |
| ⚡ 视频快车道 | 不看号,直接挑「正在冒新评论的 top 100 视频」单独抓 | 够格的热视频 | 每 2 分钟挑一批 |
号分两层,走两条 cron(cron = 定时任务,到点自动触发的"闹钟")。最容易被绕晕的是「闹钟频率 ≠ 每个号实际多久被抓一遍」:
| tier-0 普通号 | tier-1 优质号 | |
|---|---|---|
| 闹钟多久响(cron) | 每 1 小时(原 4h) | 每 2 分钟(原 5min→3min→今天 2min) |
| 每次派多少 | 挑 150 个最久没抓的号 | 所有 tier-1(受下面节流) |
| 同一个号多久能再抓 | 无硬闸,靠 150 名额轮 | ≥30 分钟(节流闸,今天从 45 改 30) |
| 队列优先级 | pri=5 慢道 | pri=1 快道 |
| 每个号实际间隔 | ≥7 小时(1019 个号挤) | ≥30 分钟(且超产能更长) |
由同事 tuan19960216 在 2026-06-14 11:03 上线(PR #340)。藏在 tier-1 的闹钟里,每 2 分钟独立跑一轮(下文把「闹钟响一次 / 跑一轮」叫「1 个 tick」,现 cron */3 = 每 3 分钟一个 tick):
pri=1),不分 tier、不受 30 分钟号节流约束;近 2.5 天 274 条走慢路(>30 分)的高/中意向评论,按「评论来时它那条视频前 24h 有没有上过快车道」分类:
| 成因 | 条数 | 占比 |
|---|---|---|
| 🥶 冷视频鸡生蛋(从没上过快车道) | 265 | 97% |
| 📉 掉车(上过又没赶上) | 9 | 3% |
| 其中深夜掉车 | 1 | — |
就算 Leg B 把活派进了队列,worker 也不是立刻抓。实测中位 276 秒、p99 327 秒。机制如下:
pri=1:tier-1 整号 + 视频快车道)和慢道(pri=5:tier-0 整号 + lead 资料)。pri=1 永远插在 pri=5 前面。/queue/stats 报 pending=2 是假象(只数最近 1000 条窗口),真实 pending=640(pri1 103 / pri5 537);8 槽真忙(claim 78/分、running 常态 8 个无僵尸)。Leg C 的真因 = pri=1 队列常年积压 ~100 条,新活按"同级先到先得"排在后面——实测一个 23:06:53 的 tick 灌的批次,全等到 ~23:11:35 才被领走(285 秒)。排不空因为:① 快车道昼夜不停每 2 分钟灌一批(深夜 313s ≈ 午间 276s,无低峰);② 慢整号活(2+ 分钟)混在同条 pri=1 道占槽;③ 还有 poison 活(一条 38 小时前的 pri1 整号活重试到顶卡死、永远 pending)。轮到后,worker 调抖音评论 API:抖音新评论排最前,增量抓只取首页、整页无新即早停,通常 1 个请求 ~5 秒搞定;写库亚秒。所以抓取本身不慢,慢的全在 B、C 两段等待。
B-3 说快车道 6-14 11:03 上线。歆笛这条视频的延迟正好在那之后断崖式变好——这就是 B 两条路切换的铁证:
| 发言 | 抓到 | 等待 | 走哪条路 | 谁 |
|---|---|---|---|---|
| 06-14 08:08 | 08:37 | 29 分 | 🐢 整号慢路 | 龍凤呈祥 ⭐ |
| 06-14 09:44 | 10:14 | 30 分 | 🐢 整号慢路 | 用户508 ⭐ |
| ───── 11:07 这视频首次挤上快车道 ↓ ───── | ||||
| 06-14 15:49 | 15:51 | 1 分 | ⚡ 快车道 | 古悠然 ⭐ |
| 06-14 16:49 | 16:57 | 7 分 | ⚡ 快车道 | 信仰 ⭐ |
| 06-15 00:35 | 00:37 | 2 分 | ⚡ 快车道 | 二宝妈 ⭐ |
| 06-14 23:29 | 06-15 14:25 | 14.9 时 | 🐢 掉回慢路 | 🐟ᐝ |
全库高/中意向评论按延迟分桶(口径分三层,避免被历史积压误导):
| 口径 | 样本 | 中位延迟 | ≤5 分 | >30 分(走慢路) |
|---|---|---|---|---|
| 全部历史 | 13,842 | 2344 分 | 4.5% | 86% (陈年积压污染,忽略) |
| 近 7 天 | 2,830 | 261 分 | 13.7% | 70% (含 6-14 断档尾巴) |
| 近 2 天(真常态) | 444 | 10 分 | 41% | 37% |
顺着 B-2/B-4 往上追:冷视频之所以这么多,是因为大量 tier-0 号被饿死、长期不被抓。而 tier-0 被饿死,又是因为 tier-1 太多——
auto-tier1 每天把「近 3 天高意向 ≥5」的活跃号升 tier-1,但系统没有任何降级逻辑——只升不降。于是 tier-1 越攒越多:
| 活跃 | 不活跃 | |
|---|---|---|
| tier-1 优质号 | 176 | 10 |
| tier-0 普通号 | 1019 | 1828 |
| 近 7 天高意向 | 个数 | |
|---|---|---|
| 🟢 仍达标(≥5) | 46 | |
| 🟡 不够格(1–4) | 68 | |
| 🔴 挂零(0) | 62 |
| 原因(为什么要做这刀) | 现有内容(做了什么 · 状态) |
|---|---|
| P0-a 断总根因:tier-1 只升不降攒到 176、74% 僵尸,挤爆抓取产能、把 1019 个 tier-0 饿死 → 冷视频多、慢尾多。 | ✅ 已落地:6-16 手动砍 27 个真死号(高+中=0)176→149;#365 对称 auto-demote(自动降级、断只升不降)已合并部署。下一步可按 mid lead 价值再砍到 ~46。单这一刀解大半。 |
| P0-b 治 Leg B 冷视频:97% 慢路是「冷视频鸡生蛋」——冷视频赶不上 60min 新鲜门槛、永远进不了快车道(冷视频定义见 B-4)。 | ✅ 已落地:#363 破鸡生蛋——任一视频近 6h 一冒高/中意向评论,立刻临时拉进快车道(pri=0 插最前),不等它够 60min 门槛。(注入空扫待补「去重」,见 ⑨-D) |
P0-c 治 Leg C 排队:慢整号活(23–211s)和快活(5s)挤同一条 pri=1 道 → 排队 4.6 分;且快车道空跑洪水(多数复扫 found=0)白占位。 |
✅ 已落地:#363 快慢分道把整号活降 pri=3 让快活先走(尾延迟 p90 −61%/p99 −75%);#366 空跑退避(连 3 次 0 新 → 9→18→30 分钟退避)。直接压那 4.6 分钟。 |
| P0-d 清 poison + pri=1 减负:38h 前重试到顶仍卡 pending 的僵尸活白占;慢整号活也还堵在 pri=1 道。 | ✅ 已落地:6-16 已删那个 59h poison 墓碑活;并随 P0-c 把慢整号活挪出 pri=1。(「自动清 poison」还可补成常态 job) |
| P1 怀疑「并发不够、要扩机」。 | 有效并发实测 ~8–11 槽(非瓶颈),无需盲目扩机;真瓶颈是 pri=1 队列结构(已由 P0-c/d 治)。 |
| 不要做 想靠「提 cron 频率」提速(闹钟 2min→1min)。 | 别做——会让快道空跑翻倍、Leg C 更糟。(#346 已验证 cron 不是瓶颈;甚至 */2→*/3 回退反而更好,见 ⑦) |
理论地板:治好 B、C 两段后,整条链可压到 10–30 秒。今天那几条 1 分钟抓到的,就是低峰不拥堵时跑出来的——目标是把「1 分钟内」从偶发变常态。
今日改动(PR #346,已上线):scrape-hot 闹钟 */3 → */2 + 源节流 45 → 30 分。但实测发现 cron 不是瓶颈(真瓶颈是 Leg B/C),所以原计划第二步 */1 已取消。
scrape_video 快活 88,455 条,其中 77% 复扫 0 新评论(空跑);快车道每 2 分钟灌 ~67 条(dedup 后)。*/3→*/2 把这股空跑洪水抬高了 50%,而它灌的全是 pri=1 活——正是堵 Leg C 的那条队。所以:改回 */3 反而少 33% 空跑、减轻 pri=1 积压,只换来热视频复扫间隔从 ~2 分变 ~3 分(次要)。真正起作用的是同批的源节流 45→30(让 tier-1 更早可重抓),不是 cron tick。结论:tick 是二阶旋钮,回 */3 没坏处;真问题仍是 P0-a 降级 + P0-c 队列双层优先级。cron tick=2分 ✓Leg C 中位 276s / p99 327sLeg D 中位 4.9s快车道命中率 18%worker 实测有效并发 ~11 · 总吞吐 ~3000 活/htier-1 149(6-16降27真死号)tier-0 1046慢路 37%(近2天)冷视频 97%
fly status 实查坐实,不是推测)——akke-worker-prod 现 2 台 started、都在东京(nrt),auto_stop=off 常驻不缩,不会因为 pri=1 洪水自动多起第三台(Fly 没有"按负载自动扩容"这回事;早先写「瞬时 >2 台 / deploy token 读不到机器数」是错的,现已实查更正)。要加机器只能手动 fly scale count=N。② "槽"分两层,别混(这就是之前 8 / 11 打架的根源):工位(job slot)= 2 台 × WORKER_CONCURRENCY=3 = 6 个,是"同一时刻最多领几个活"的硬上限;但一个"整号活"内部还能同时拉 2 条视频(MAX_VIDEOS_PER_SOURCE_CONCURRENT=2)→ 真正"同时在飞的抓取请求"能到 ~6–12 条。Little's law 反推的 ~11,量的是后者(在飞的请求数),不是工位数;running 行 14 里还含 ~3 个 zombie 虚高。总吞吐 ~3000 活/h。③ 已执行 P0-a 第一刀:把「近7天 高+中意向产出=0」的 27 个真死号降回 tier-0,active tier-1 176→149(回滚清单存盘)。scrape_jobs 表抢活(调 claim_pending_scrape_jobs_batch,靠 Postgres FOR UPDATE SKIP LOCKED)。这把锁保证同一条活绝不会被两台同时领——谁手头空了谁去抢下一条,天然就负载均衡,没有主从、不用分工、不会撞车。所以"两台如何配合"的答案是:它们根本不需要互相知道对方存在。fly scale count=2→3 能线性加工位(每台 +3),但 ⑥-P1 已实测并发不是瓶颈——这 6 个工位有近 9 成时间在空跑(复扫 0 条新评论)。盲目加机器只会让"空跑"跑得更快,治不了 Leg B/C 的结构病。纵向把单台 WORKER_CONCURRENCY 3→5 也不行,会撞 shared-cpu-1x 的 CPU 墙(代码注释明写 5+ hits CPU wall)。Leg B 的"入队" = 把这条视频 / 号写成一行 scrape_jobs(Postgres 表,不是 Redis / 内存队列)。两类行:
| job_type | 是什么活 | priority |
|---|---|---|
scrape_source | 整号活:去主页捞 ≤20 条视频逐条抓 | tier-0 = 5 / tier-1 = 1 |
scrape_video | 单视频快活:快车道增量复扫 | 1 |
scrape_jobs 表。 worker 的 8 个 slot 各自轮询,调 claim_pending_scrape_jobs_batch RPC(Postgres SKIP LOCKED + ORDER BY priority ASC)领活。Leg B = 等 cron 把活写进表("何时轮到抓它");Leg C = 活已在表里、按 priority 排队等某个 slot 领走。| 段 | 现状 | 极限地板 | 怎么做 |
|---|---|---|---|
| Leg B | 冷视频几十分~几小时 | ≤2 分(常态 1–2 分) | P0-b 破鸡生蛋:任一视频一冒高意向评论立刻强行注入快车道,不等它自己够 60min 门槛 → 压到 ≤1 个 tick(2 分) |
| Leg C | ≈ 4.6 分 | 亚秒~数秒 | P0-c 双层优先级(带新评论的活提 pri=0 插队、慢整号活移出 pri=1)+ P0-d 清 poison → 活到达空队列 + 有空 slot 立即领 |
| B+C 合计 | — | ≈ 10–30 秒 | = ⑥ 末尾「理论地板」。今天偶发「1 分钟抓到」就是低峰不拥堵时已摸到这个地板 |
| GOOD | BAD / 风险 |
|---|---|
| ① 高意向触达从分钟~小时 → 1–2 分,抢客户热度窗、转化↑ ② tier-0 解放、冷视频减少、漏抓减少 ③ 队列不积压、poison 自清、系统更稳 | ① P0-b 强注入若不 capped 会重蹈 6-13「洪水偷产能饿死 tier-0」——必须配 P0-a 先释放产能 + 集合 capped ② 提扫描频率 / req-min 触发反爬、伤号(硬上限) ③ P0-a 降级可能误杀暂时低产的优质号(需回升机制) ④ 工程复杂度↑、更多队列状态要监控 |
journeys-fanny#pt)的「技术机制」区。⑥ 列的 P0 提升点今天全部落地(多个 PR 已合并部署)。但实测下来(全天 24h 复核):Leg C 把典型延迟砍了 2/3、但极端尾巴被注入洪水拖坏;Leg B 喜忧参半;而"评论拉取还是很久"的真尾巴另有其因——不是排队、是老视频覆盖盲区。逐项说清。
⑨ 段提到的每个 #编号 改动,先在这张表里一句话说清「它是什么 / 治什么病 / 现在什么状态」,下面 B、C 再展开。比喻统一用「果园摘果」:视频 = 果树,评论 = 果子,高意向评论 = 熟透值钱的果(lead),抓评论 = 派人去摘。
| 改动 | 一句话(果园比喻) | 治什么病 | 状态 |
|---|---|---|---|
| #363 · Leg B 破鸡生蛋 | 看到偏远老树刚冒熟果,破例先抱上"重点照看名单"、还排最前,不等它够格 | 冷视频上的高意向评论,第一条几乎必慢 | 已上线 6-16 |
| #363 · Leg C 快慢分道 | "摘单棵树"的快活和"巡整片园子"的慢活分开排队,让快的先走 | 慢整号活堵在快活前面,快活干等 | 已上线 6-16 |
| #366 · 空跑退避 | 一棵树连去 3 次都空手就少去(间隔 9→18→30 分钟),一冒新果立刻恢复勤去 | 视频被反复复扫却抓 0 新、白占工位 | 已上线 6-16 |
| #365 · tier-1 自动降级 | 不再活跃的优质号自动降回普通档(对称于自动升档) | tier-1 只升不降、撑爆产能、饿死 tier-0 | 已上线 6-16 |
| #377 · cold-intent 回扫 | 近 14 天还在冒高意向的老视频,每 20 分钟低频回扫一遍 | 常青老视频上的新高意向,没人定期扫(老视频盲区) | 已上线 6-16 |
| #381 · 注入去重 | 破鸡生蛋的注入活也纳入退避(去掉原本豁免),别每 tick 重派同一棵树 | pri=0 注入洪水:同一视频 80 次/6h、100% 抓 0 新、拖坏 p99 | 已上线 6-17 |
| #413 · 早停修复 | 增量抓评论别"一页全老就停",要连续 N 页全老才停(抖音按热度排、新评论埋后面) | 快道判"空"里 33% 是假空、漏了真评论(含 lead) | 已部署 6-18 · 终判:假空 33%→13% 治住 ✓,但快道量 +59% 把低优先级挤死、claim-wait 尾巴反恶化(见 D 段 🔬) |
1)「Leg」是什么?=一条评论从"被发出"到"我们抓进库",中间分好几段路(② 段把它拆成五段)。本段只关心其中相邻两段:
· Leg B = 「派活」那段:评论已经存在了,但系统还没派出一个活去抓它("菜做好了,没人来端")。
· Leg C = 「排队执行」那段:活已经派进队列了,等 worker 领走真正去抓("菜端到打饭窗口,等阿姨打饭")。
2)「地基①②」不等于「Leg B / C」。地基是底盘(系统本来就这么转);Leg B / C 是在底盘上做的改动招式。下面先讲底盘,再讲招式。
以下「地基」是纯现状(系统现在怎么转,不含本轮改动);改动一律见上方 A0 一览表。
| 活的类型 | 抓什么 | 快慢 |
|---|---|---|
单视频快活scrape_video | 只抓一条具体视频(快车道按规则点名的那条,job 里带它的 url——通常是「正在冒新评论的热视频」或「近 6h 冒过高意向的视频」)底下的评论,且是增量(只取比上次新的) | 快 · ~5 秒/条 |
慢整号活scrape_source | 不点名具体视频,给的是一个号:去它主页捞最近 ≤20 条视频,再逐条把每条的评论都抓一遍(抓哪几条由号当下决定) | 慢 · 中位 23 秒、最慢 211 秒 |
| 高意向注入活 | 本质也是单视频快活,但因「这视频近期冒过高意向」被特批插队(近期 = 近 6 小时,为什么是 6h 见 B 段;机制见 B) | 快 · ~5 秒/条 |
aweme_id 点名抓那一条。谁来点名 = 快车道 / 高意向注入 / UI「抓这条」,它们把一个 aweme_id 写成一行 scrape_video 活。所以"单视频快活"永远只盯一条视频,不碰这个号底下别的视频。auto-tier1 cron 每天把"近 3 天高意向 ≥5 条"的活跃号自动升上来(对称的自动降级见下方 ⑤/⑦ #365);tier-0 是默认档、绝大多数号都在这。每个活贴一张「号码牌」priority,worker 严格从小号往大号领,同号牌内先到先得。越急、越快、越该趁鲜的,贴越小号:
| 号码牌 | 谁贴这号(入队来源) | 含义 |
|---|---|---|
0 | 高意向注入 · vip-rescan · UI「抓这条」(运营在后台仪表盘上手动点「立即抓取这条」的按钮) | 最优先,最该趁鲜抢 |
1 | 常规单视频快活(fresh-catch 快车道) | 快,先放走 |
3 | tier-1 慢整号活 · rescan-recent | 慢,让到快活后面 |
5 | tier-0 每小时轮抓 · lead 资料(给某个潜在客户建档时顺带触发的抓取,scrape_lead_profile,没指定 pri 就吃默认 5) | 最次,量大不急 |
pri=0 高意向注入:插最前,空队列时秒级 ~ 1 个 tick(2–3 分)就被领走。pri=1 常规快车道单视频:执行~5 秒/条;排队中位 0、尾巴(p90) ~2 分。pri=3 tier-1 慢整号:执行中位 23 秒、最慢 211 秒/个;让到快活后面,实测 pending 最老 ~9 分。pri=5 tier-0 每小时 + lead 资料:只在上面三档全空时才轮到 → 实测能积压到 12 小时没人抓(就是 ⑤ 总根因里被饿死那批)。scrape_lead_profile:识别出一个潜在客户后,系统顺手排一条活去抓这个用户的主页/视频补全画像,它没单独设优先级,就吃了默认 pri=5。)| 号码牌 | 6-16 改动前 | 现在(#363 后) |
|---|---|---|
0 | vip-rescan、UI 手动 | + 高意向注入 ← 新增 |
1 | 常规快活 + tier-1 整号活(挤同一道) | 只剩常规快活 |
3 | 基本空着 | tier-1 整号活(从 1 降来)、rescan-recent |
5 | tier-0 轮抓、lead 资料 | 不变 |
就两处变化:① tier-1 整号活:pri 1 → 3(原来和快活挤 pri=1 同一道、堵快活,这就是 Leg C 快慢分道);② 新增 pri=0 高意向注入(原来没有,这就是 Leg B 破鸡生蛋)。其余不变。
↑ worker 永远先把 pri=0 领光、再 1、再 3,上面三档全空了才轮到 pri=5 → tier-0 被饿死(⑤ 总根因的机制)。
DEFAULT 5),要改得改代码走 PR。worker(~30s/轮)调认领 RPC,核心 = WHERE pending AND scheduled_at≤now AND attempts<3 ORDER BY priority ASC, scheduled_at ASC FOR UPDATE SKIP LOCKED(严格 pri 小→大、同级先到先得、并发安全)。领到即开抓;卡死(>8min)被扫回重排;attempts 到 3 被排除=永久 pending 墓碑(P0-d 清的就是它)。先回顾:Leg B = 「派活」那段路 —— 评论已经存在了,但系统迟迟没派出一个活去抓它。本段治的就是冷视频上这个"迟迟没人派活"。
【问题】一条视频要先进「快车道」才会被频繁单独复扫;而进快车道的门槛是——它的评论得在 60 分钟内被我们新鲜抓到过一次。可冷视频(老视频 / 不活跃号底下的)只能等慢整号活几十分钟~几小时扫一遍,永远赶不上 60 分钟 → 永远进不了快车道 → 它上面新冒的高意向评论第一条几乎必慢。死循环:要被快抓得先进快车道,要进快车道得先被快抓(「鸡生蛋」)。
【改动】不再死等门槛。只要一条视频近 6 小时内冒过高/中意向评论,就直接把它塞进快车道、还插在最前面(pri=0)优先抓——「破鸡生蛋」= 直接抱它上车,不等它自己够 60 分钟的格。
6h 的由来:注入要圈"刚冒过高/中意向的视频",6h 是个"近期"窗口——够宽,半夜冒的意向到白天还能补上。
实测发现它的毛病是"太长":高意向视频天生是老·常青视频(实查"曾产高意向、还活着"的视频全部 >4 天龄)。一条视频 6h 内只要有过一条高意向评论,就被每个 tick 重选注入一次 → 6h ≈ 120–180 个 tick → 同一条被反复抓 80 次、100% 抓 0 新。
但核心病不在 6h 长短,在"每个 tick 都重注一遍"。缩到 2h 只是按比例砍空跑(治标);根治靠"注入去重"(同一视频注入过就别每 tick 重派)——这已由 #381 落地(A0 清单里那条)。所以 6h 维持不动。
empty_streak):连抓 3 次都 0 新才开始拉长间隔(9→18→30 分钟),一冒新评论立刻清零、恢复每 tick 勤抓。所以真在不断冒评论的活跃视频永远不空手、永不退避、频率不降;被降频的只有"冒过一波后安静下来"的(实测那反复 80 次正是这批,100% 空手)——对它们降频一条 lead 不少抓、只省白跑。后续旋钮:可给高意向视频设更短的退避上限(如封顶 10 分钟)。src/app/api/cron/scrape-hot/route.ts → runVideoFastLane。WINDOW_H=48h 内、被 ≤FRESH_CATCH_MIN=60min 新鲜抓到的评论数」top MAX=100,全 pri=1(即 B-3 那套)。HIGH_INTENT_HOURS=6h 内 intent_label∈{高,中} 的评论」对应视频,免 60min 门槛并入、priority=0、截断时置顶不被截。开关 HOT_FAST_LANE_HIGH_INTENT_HOURS=6(=0 关)。先回顾:Leg C = 「排队执行」那段路 —— 活已经派进队列了,等 worker 领走真正去抓。本段治的就是这个排队:别让慢活堵快活、别反复空扫白占位。
【问题①·排队】活派进队列后 worker 一个个领。原来慢整号活(23~211 秒)和单视频快活(5 秒)挤在同一条号牌道(都 pri=1)——前面卡一个慢整号,后面快活全得干等,实测排队中位 ~4.6 分钟。
【改动① · #363 快慢分道】把慢整号活降一档(pri 1→3),让 5 秒的快活先走、别被慢活堵。实测典型延迟 median −67%(见 D);但极端尾巴 p99 反被注入洪水拖坏 → 已由 #381「注入去重」补上(见下方改动③)。
【问题②·空扫】很多视频被反复复扫却抓不到新评论(实测近 24h 复扫 88,455 条、77% 抓 0 新),白占 pri=1 的位置、加重排队。
【改动② · #366 空跑退避】一条视频连抓 3 次都 0 条新评论,就把它下次复扫往后推(9→18→30 分钟封顶);一旦又冒新评论立刻清零、恢复勤抓。
empty_streak=连续抓 0 新几次、next_fast_lane_at=推到几点才允许再进快车道。9×2ⁿ:9→18→30,第三步本该 36 被 30 封顶)。FAST_LANE_BACKOFF_BASE_MIN=9 / _CAP_MIN=30,嫌慢调小、嫌费调大)。runTier1SourceEnqueue):tier-1 整号活 priority 1→3;同批 vercel.json cron */2→*/3(⑦ 已验 cron 非瓶颈、回 */3 反少 33% 空跑)。20260616114802 给 videos 加 empty_streak/next_fast_lane_at+partial index;worker _run_scrape_video 仅对 hot_fast_lane && !high_intent 的活按 new_comments_inserted 记账、退避间隔 min(30, 9×2^(streak−3));选视频端 runVideoFastLane 在截断前过滤 next_fast_lane_at>now(pri=0 注入原不退避;2026-06-17 #381 已去掉此豁免、注入也退避,见改动③)。开关 HOT_FAST_LANE_BACKOFF / FAST_LANE_BACKOFF_START_STREAK=3 / _BASE_MIN=9 / _CAP_MIN=30。【为什么要补】#366 退避当初故意豁免 pri=0 注入(怕压住破鸡生蛋该抢的活)。全天复核坐实这豁免错了——注入洪水(80 次/6h、0 新、把 p99 拖到 80 分钟)的实测拆解见上方 D 段,此处不重复。结论:豁免要去掉。
【改动】去掉那条豁免,让高意向注入跟普通快活一样退避——注意是「自适应」,不是粗暴的「摘过一次就再也不去」。
videos.empty_streak),任何一条路摘到这树的新果都清零 → 真在不断结果的好树永不空手、永不退避、频率一点不降;被拉长的只有"冒过一波后安静下来"的树(注入空跑的正是这批)。所以它不会把"我们关注的活跃视频"抓慢。scrape_jobs / videos)下表时间都是 2026-06-17 当天;Z = UTC(0 时区),+8 小时 = 北京时间,已在每行标出北京时间。
.order 重测后修正为下面的渐进衰减——这条曲线才和"连 3 次空扫才起退避≈9 分钟"的机制自洽。)| 时刻 | pri=0 注入创建率(每 10min) | worker 运行槽(瞬时) | tier-0(pri=5)积压 |
|---|---|---|---|
| 部署前 · 11:43 (03:43Z) | ~180(≈18/min 持续灌,对得上全天 26995) | pri=0 霸占 6/11 槽 | 353 个 · 最老 13.0h |
| +12min · 12:00 (04:00Z) | 65(−64%) | — | 352 · 12.8h |
| +22min · 12:10 (04:10Z) | ~22(−87%,仍在降) | pri=0 = 0;pri=5 拿 10/16 槽(62%) | 350 · 12.7h |
empty_streak=3/4 那两档在涨)。起因:运营反馈中高 leads 变少。循线去查——"快道判 0 新的视频,后来全量轮有没有补到、抓那一刻就已经存在的评论"。
结果(近 12h 抽 120 个判空视频):
· 67% 是真空:确实没新评论可抓(结论不变)。
· 33% 是假空:抓时评论已存在、却被快道漏掉,晚 50~553 分钟才由最慢的全量轮补到,漏的里含中意向 lead。
根因:快道增量抓取「整页全老就停 + 只看前 50 条」,假设抖音按时间倒序返回;但抖音 web 评论接口默认按热度排,新评论被埋在后面够不到。
要更正前文一处口径:退避省的是浪费没错,但"0 新 = 纯空跑、不丢 lead"只对那 67%;剩下 33% 的真评论被推给最慢的全量轮 —— 这才是"中高 leads 变少/变慢"的直接根因。
退避只是治标,这个早停 bug 要单独修 —— 就是 A0 清单里的 #413(已于 6-18 部署;这些视频抓得到东西、empty_streak 清零、自然不再退避)。验证脚本 _verify-fastlane-earlystop-bug.ts。
(6-19 终判:早停半治住假空 33%→13% ✓,但「不再退避」让快道量 +59%、反把低优先级活挤到队尾饿死,claim-wait 尾巴恶化 —— 详见 D 段 🔬 终判框。)
16bcfc233 / Fly worker v306(已 /health 探活)。改动:scrape-hot/route.ts(hiIds 纳入 next_fast_lane_at 退避过滤)+ worker/queue.py(去掉 high_intent 豁免)。无 migration。急停开关 HOT_FAST_LANE_BACKOFF=0(全关退避)。scheduled_at 落在窗口内、started_at 非空(已认领)统计。| scrape_video 入队→被认领等待 | median | p90(最慢 10%) | p99(最惨 1%) |
|---|---|---|---|
| ① 06-15 部署前 (n=26544) | 233.7s | 454s | 1151s |
| ② 06-16 注入洪水峰·#381 前 (n=51170) | 75.5s | 486s | 4596s |
| ③ 06-17→18 #381 后 ~21h (n=27269) | 57.8s | 1138s | 2191s |
| ④ 06-18→19 #413 后·全天 24h (n=35507) | 84.2s | 1877s | 6202s |
pri=1 跟快视频抢道,部署后整号活搬到 pri=3(3160)、pri=1 里的整号只剩 112。但 p90 基本没动、p99 反而从 20 分钟炸到 80 分钟。为啥?看同一份数据的另一个数:pri=0 高意向注入活从全天 905 个暴涨到 26995 个(30 倍)——破鸡生蛋的注入洪水(每 tick 重注,见下)在 pri=1 前面又垒了一座 pri=0 大山,典型活照样快,但挤在洪水里的那 1% 倒霉蛋等得比以前更久。started_at 取最后一次认领造成的虚高;但方向——"尾巴没改善、注入洪水是主因"——跟全部其它证据一致。)· ①→②(#363 快慢分道):median −67%(典型大赢);但注入洪水把 p99 从 ~19 分钟炸到 ~77 分钟(尾巴翻车)。
· ②→③(#381 注入去重,6-17 上线):洪水退了(活数 51170→27269)、median 史上最好(57.8s)、p99 从洪水峰 4596 砍到 2191(−52%)、高+中 leads 回到 446(占比 8.3%)。
⚠️ 但尾巴还没回到基线:p99 仍 ~2× 基线(2191 vs 1151)、p90 反而 454→1138 变差。最可能的残余原因 = 早停 bug——#381 退避更狠了,而那 33%「假空」(真评论被漏,见下方 🔬)被退避推给最慢的全量轮、拖长了尾巴。
→ #413 早停修复 2026-06-18 部署,本想治这条尾巴;④ 全天窗口终判见下方 🔬 —— 结论反转:完整性治好了,尾巴反而更糟。
先把两个指标分开(容易混):「假空率」=快道每次抓有没有把已存在的评论漏掉(抓取完整性);「claim-wait 尾巴」=活在队列里排多久才被领走(队列吞吐)。#413 只动前者,但人们以为它能顺带治后者——终判证明这个假设错了。
① 早停 bug(直接靶子)✅ 治住了:假空率 33%→13%(post-fix 10h、抽 120 视频);videos.empty_streak≥3 从 313 崩到 17——视频几乎不再被误判空。
② 但 claim-wait 尾巴没回基线、反而更糟(见上表 ④):p90 454→1877s(基线 4.3×)、p99 1151→6202s(基线 5.3×,比注入洪水峰 ② 的 4596 还惨 = 史上最差)。
为什么修了完整性、尾巴反炸?(证据链坐实):#413 让快道几乎不再判空 → 视频不再累积空跑 → 不再被退避 → 快道 pri=1 量 +59%(13105→20858,100% 是快车道)。这股高优先级洪水把低优先级 scrape_video(cold-intent 老视频回扫 pri=3 等)挤到队尾饿死——尾巴那 1% 的活全是 pri≥2(pri=0/1 一个都没有),得等数小时靠 aging 才浮上来。时段上也对得上:尾巴全集中在北京白天/晚上(高负载),深夜零负载零尾巴。结论:尾巴是被快道挤死的低优先级活,不是快道本身。
所以 B 方案(max_comments 50→150)坐死不做:它治的是「截断型」完整性(老评论被埋到前 50 名外),却会让每个快道活抓更多页、更慢、排空更慢 → 低优先级饿得更死 → 尾巴更糟。拿错药治错病。
真正的旋钮 = pri 防饿死(每批 worker 槽给 pri≥3 留配额 / 强化 aging)。讽刺的是:cold-intent 老视频回扫(专门救完整性慢尾的②方案)自己卡在队尾,被③的快道洪水在排队层面抵消了。这条要动 worker 领单逻辑(高危),单开 PR 专门做、不糊。
上面 #413 终判把 6202s 尾巴归到「cold-intent pri=3 被快道洪水挤死、真解=pri 防饿死配额」。6-19 复跑 36h(覆盖 6-18→19、按 pri 段拆 scrape_video)把这个判断收窄了:
· cold-intent pri=3:全部认领,claim-wait p50 27m / p99 1.9h / max 2.0h、0 条超 6h、pending 仅 15 条且都没等满 1h。那条 6202s≈1.7h 的尾巴是真的(本窗口 p99 1.9h 正印证它),但封顶在 2.0h,正好踩在 cold-intent 自己「激进追平 ≤2h」的设计容差里,没有超出 2h 的长尾。
· tier-0 pri=5:已被 aging 压到 p99 1.1h / max 13.4h(仅 8 条,0.01%)、pending 最老 6.8h——不再是早先的 12h / 27h 积压。aging(migration 20260615144043)在起作用。
· leads 没掉:高+中意向入库近 7 天稳在 370–463/天,所谓「446→380」落在日间方差内,不是这条尾巴的连带伤。
结论:pri 防饿死配额(动 worker 领单逻辑、高危 PR)当前没有一个真实坏掉的指标支撑,从「该上了」降级为「留观」。真要再压这条 1.7h 尾,对症且便宜的杠杆是 env COLD_INTENT_RESCAN_INTERVAL_MIN(120→60) / COLD_INTENT_RESCAN_MAX(纯 fly secret、不改码、不开 PR),而不是先写 RPC。
这段窗口里,pri=0 高意向注入活跑完 9039 个(= scrape_jobs 表中 priority=0、标 high_intent、在此窗口完成的单视频活条数;全天口径为 26995 个)。但这 9039 个活只摊在 112 个不同视频上 —— 平均每条被重复抓 80.7 次(9039 ÷ 112 ≈ 80.7)。三点说清这洪水的性质:
pri=1 前垒出一座 pri=0 大山、把极端尾巴 p99 从 20 分钟拖到 80 分钟。| 窗口 | 入库评论总数 | 高意向 | 中意向 | 高+中 | 高+中占比 |
|---|---|---|---|---|---|
| ① 06-15 全天 (部署前) | 4080 | 74 | 265 | 339 | 8.3% |
| ② 06-16 全天 (#381前) | 5291 | 93 | 280 | 373 | 7.0% |
| ③ 06-17 全天 (#381后) | 5361 | 97 | 349 | 446 | 8.3% |
| ①→③ 变化 | +31% | +31% | +32% | +32% | 8.3%→8.3% |
端到端「高意向评论 comment_time → 入库」实测(近 36h,n=124):median 34 分(典型还行),但 >1h 占 47%、>6h 占 42%、>12h 占 38%——这条重尾就是日报里那批标 expired_at_claim 被跳过没触达的 lead。按 source tier 拆开,真因和直觉相反:
| source tier | n | >6h 慢尾占比 | median 时延 |
|---|---|---|---|
| tier-0(pri=5 每小时) | 6 | 17% | 4 分 ← 居然最快 |
| tier-1(pri≤3 快车道) | 110 | 42% | 34 分 |
| 无源(孤儿视频) | 8 | 63% | 99 时 |
E 段那条"38% 老视频慢尾"怎么治?验证过 4 个方向,下面逐个说人话、再标现在的真实状态(不是计划、是查过库的实况):
① 把快车道的 48 小时窗口拉大到 72 / 96 小时 → 不做(已否决)
什么意思:快车道只盯"近 48 小时内冒过新评论"的视频。直觉会想:那把 48 拉到 96,不就把老视频也圈进来了?
为什么不做:模拟过——窗口拉到 96h,候选池涨 46%,但多出来的全是已经冷下去的老视频(大半还被退避挡在外面),真正救回的慢尾只有 0–1 条。窗口管的是"往回看多久",可这些视频的病是"过去那段压根没人去扫",看得再远也没用。方向错,不做。
② 给"老的高意向视频"单开一条低频回扫 → ✅ 已上线在跑,最对症
什么意思:专门挑那些"以前出过高/中意向、但现在掉出了 48h 快车道"的老视频(数量不多,是 Pareto 钓点),单独给它们排一条慢班车——每条视频最多 2 小时扫一次,把"几小时~7 天才被扫到"的盲区压成"保底 2 小时一扫"。
现在跑得怎么样:已上线(PR #377,cron /api/cron/cold-intent-rescan,每 20 分钟一轮,走 pri=3)。6-17 首份快照实测:它的空跑率 65%,是所有回扫通道里最低的——反过来说 35% 的轮次真抓到了新评论,是 4 条通道里唯一"打中"的,对症。代价:它走 pri=3,把 pri3 积压从 96 顶到了 384(所以下面 ④ 变急)。
③ 修"孤儿视频" → 暂没单独做(② 可能已顺手覆盖)
什么意思:库里有一批视频认不出是谁发的(source_account_id 是空的),任何"按号抓"的活都覆盖不到它们,只能烂在那——E 段那 8 个孤儿视频中位慢到 99 小时就是这种。
现在状态:没单独动手。因为 ② 是"按视频"撒网、不按号,孤儿视频天然就被 ② 网进去了 → 先看 ② 跑一段够不够,不够再单独按作者把来源回填上。
④ pri 防饿死(治另一个病:tier-0 被饿死、积压 12 小时) → 6-19 复诊降级 · 留观(暂不做)
什么意思:worker 严格"小号牌先领",pri=5 的 tier-0 活只在前面全空时才轮到 → 能积压 12 小时没人碰。两个治法:aging(一个活等太久就自动升号牌往前插)或配额(每批留 X% 名额给 pri≥5,不让它彻底饿死)。
为什么变急:② 那条 cold-intent 走 pri=3 上线后,又往 tier-0 头上压了一层——6-17 快照实锤 pri3 积压 96→384、tier-0 最老活仍卡 12 小时。所以 ④ 从"以后再说"提到了"该上了";要是 ④ 一时上不了,就得先把 ② 的 COLD_INTENT_RESCAN_MAX 调小、给 tier-0 泄压。
✅ 6-19 复诊更正:以上是 6-17 快照。6-19 复跑 36h 实测——tier-0 已被 aging 压到 p99 1.1h / max 13.4h(8 条)、cold-intent pri=3 max 2.0h / 0 条超 6h、pending 仅 15 条,积压基本治住。④ 从「该上了」降级为「留观」,pri RPC 暂不做——详见上方 D 段 6-19 复诊框。
② 段是 6-15 的拍照,当时 Leg B / Leg C 是两个大头。这 5 天的 7 刀 PR 基本都砸在这两段。下表把每一段的「原本(6-15) → 现在(6-20)」摆一起对比,再标出每段的物理地板——理论上还能压到多快、靠什么压。B、C 怎么联动、各自现在排多久,见下方 B↔C 详解。
| 段 · 这一段在干嘛(人话) | 原本 6-15 | 现在 6-20 | 物理地板:还能压到多少 · 怎么压 |
|---|---|---|---|
| Leg A 上架 评论在抖音侧能被查到 |
~几秒 | ~几秒 · 稳 | 就是抖音侧那几秒延迟,我们碰不着。已在地板,无可压。 |
| Leg B 入队 等系统派个活去抓这条视频 |
冷视频 几十分~几小时 |
中位 29min 热视频几分钟 35% 仍 >6h |
地板 ≈ 1 个扫描周期(2 分钟)——再密就撞抖音反爬红线(每号约 67 次请求/分)。热视频已到地板;要把冷/老视频也压到分钟级,靠的是扩覆盖(#377 老钓点回扫 + 收 cold-intent 口径让更多活钓点被选中),不是再调快闹钟。 |
| Leg C 排队 活派进队列、等 worker 领走 |
中位 4.6 分 | 快道 ≤3min (秒清) tier-0 13h |
地板 = 亚秒~数秒(队列空 + 有空工位时)。高/中意向走的快道已到地板;tier-0 那 13h 是产能不够(aging 只把它兜在 <24h、不凭空造产能),不是排队算法的锅,要压只能加工位。 |
| Leg D 抓取 worker 去视频底下把评论捞回来 |
~5 秒 | ~5 秒 (#413 修了 33% 漏抓) |
1 个请求 + 抓到首页即早停,~5 秒已是地板。 |
| Leg E 写库 写进我们库 |
亚秒 | 亚秒 · 稳 | 从不是瓶颈,已在地板。 |
这俩老被混在一起,其实是一前一后两件事:
Leg B =「决定何时把哪条视频/号丢进队列」的所有触发器,分三支(均查代码 + vercel.json 坐实)。看表前先花 30 秒把术语换成人话:
is_vip):判定=这个号产 lead 最多 + 近期还在冒评论,由 hot-video-rotation 每天算一次。"≤4d"=vip-rescan 只盯"发布未满 4 天"的 VIP 视频、每小时复扫一次(高意向新评论越早抓命中率越高:1h≈100%、隔天减半),更老的自动退役。| 支系 | 成员 | 抓谁 · 怎么触发 | 进 pri | 在快照② |
|---|---|---|---|---|
① 整号轮抓scrape_source去主页捞 ≤20 条视频逐条抓 | tier-0 整号 | /scrape:每 1h 挑 150 个最久没抓的普通号 | 5 | 否 |
| tier-1 整号 | /scrape-hot:每 2-3min 扫优质号(#363 从 pri=1 降到 3) | 3 | 否 | |
② 单视频复扫scrape_video只抓一条视频的增量 | fast-lane 常规 | /scrape-hot:top100 热视频(近 48h 有新鲜评论) | 1 | ✅ |
| fast-lane 高意向注入 | /scrape-hot:近 6h 冒高/中意向的视频强行插队(#363 破鸡生蛋) | 0 | ✅ | |
| vip-rescan | /vip-video-rescan:VIP 视频(发布 ≤4d 的高价值视频)每小时增量复扫 ⚠️ 它的"货源"VIP 名单由 hot-video-rotation 物化,而后者近乎停摆(见下)→ vip-rescan 实际也在断粮 | 0 | ✅ | |
| cold-intent-rescan | /cold-intent-rescan:近 14d 还冒意向的老钓点(>4d,#377) | 3 | ✅ | |
| rescan-recent-videos | /rescan-recent-videos:发布 <7d 的视频按 7d 全窗口复扫 ⚠️ 已确认没开(2026-06-21 查生产库):env 未设 =1,cron 到点即返回 disabled,近 7d 入队 0 条 | 3 | 否 | |
| hot-video-rotation | /hot-video-rotation:>7d 但近 14d 还活的历史爆款,7d 全窗口 ⚠️ 近乎停摆(2026-06-21 查):最近一条 6-17 且 failed、近 7d 入队 0 条 | 5 | 否 | |
③ lead 资料scrape_lead_profile | scrape_lead_profile | /analyze-once:给高意向用户抓主页资料 | 5 | 否 |
rescan-recent/hot-video-rotation 虽然也是单视频复扫、但没贴这种标签,整号轮抓和 lead 资料又是另一种活——所以都没进那张表。一句话:快照只看到 4 个 ≠ Leg B 只有 4 个。Leg C 是一条队列,它没有自己的"触发器"——"家族"就是 Leg B 那 8 个活落进来后、按号牌(pri)分成的 4 条道。worker 严格按号牌从小到大领(同号牌先到先得)。各道现在排多久=6-19 自动快照:
| pri 档 | 这道里装的活(全来自 Leg B) | C 段现在排多久 | 谁卡这档 |
|---|---|---|---|
pri=0最急 | 高意向注入 + vip-rescan + 手动 | 秒清 | 抢救冷视频上刚冒的高意向新评论 |
pri=1快道 | fast-lane 常规(单视频快活,~5 秒) | pending 39 / 最老 3 分钟 | 热视频(基本不卡) |
pri=3中道 | tier-1 整号 + cold-intent + rescan-recent | 400 条 / 最老 57 分钟 | tier-1 优质号 + 老钓点 + <7d 新视频 |
pri=5慢道 | tier-0 整号 + hot-video-rotation(7d 全窗) + lead 资料 | 278 条 / 最老 13 小时 | tier-0 冷号(被 #357 aging 按在 <24h、没饿死) |
| 级 | 现状 | 要不要调 | 为什么 / 若硬调的预期 |
|---|---|---|---|
tier-0 整号pri=5 | 排 13h | 不调 | aging 已兜底(13h<24h、符合设计);tier-0 本就少产高意向。提 pri → 抽走产能稀释高意向源、回吐 #346/#363 加速,高意向 median 反升。 |
tier-1 整号pri=3 | 排 57min | 不调 | #363 刚把它 1→3 才让快道秒清;整号活不赶时间(高意向已被 pri=0 抢走)。提回 pri=1 → 慢整号活重新堵 fast-lane,退回 #363 之前的病。 |
fast-lane 常规/注入pri=1 / 0 | 秒清 | 不调 | 已是最优、无积压,没有可改善空间。 |
cold-intent-rescanpri=3 | 排 57min、99% 空跑 | 不调 pri (但要收口径) | 它是治慢尾的独苗,但慢尾瓶颈是「14d 回扫周期 + 120min/视频限频 + 钓点枯」,不是那 57min 排队。且 99% 空跑——提 pri = 让一堆空跑活插队挤掉有用活,纯负收益。 |
| vip / hot-rotation / rescan-recent | 断粮 / 停摆 / 没开 | pri 无意义 | 它们根本不产活,pri 是"僵尸数字"。真问题是要不要复活,不是改它排第几。 |
scrape_source 整号 completed 近 1h=140 / 近 6h≈789(~131/h) / 近 24h=3522,最近一条几秒前才完成,整号在持续出活、没归零。那个「0」是 count 全表扫间歇超时返回空(即本页 ⑩-C 口径修正同款坑,重试即得真值)。真相=整号有 ~9.9h 积压尾巴、但在 tier-0 普通号(pri=5)上、被 #357 aging 兜在 <24h,tier-1 优质号整号(pri=3)是新鲜的——是已知长尾、非急症。所以"保底配额"解的是被夸大的问题;更对症的先手仍是收 cold-intent 口径(顺带给同档 tier-1 整号让路)。_run_job 用 asyncio.gather 一次并发抢 3 个活,一批一起返回;只要 3 个执行槽被慢整号活占满,新抢回来的快活就在槽外干等」。worker/services/queue.py):早已改成 task-per-slot(每槽独立循环)——_poller_loop 起 N 个互不相干的 _slot_worker,每个槽自己跑「抢 1 个 → 干完 → 再抢 1 个」,没有"一批一起回"的栅栏。_run_job 只跑一个活,外面套 asyncio.wait_for(deadline=1500s) 兜死活。gather 只出现在一个整号活内部并发抓它那几条视频时(信号量=2)。为什么这个订正重要:旧 gather 架构下,"一个慢活拖死整批"是真的(p99 曾到 1253s)——这正是当时改架构的原因。但现在不成立了:一个慢整号活只占它自己那一个槽,兄弟槽照样不停抢快活。真正会堵,只发生在「所有槽同时被慢整号活占满」那一刻,新来的快活才得等空槽。
| 项 | 常被说错 | 实际(现状) |
|---|---|---|
| poller 架构 | gather 一批抢、一起回 | task-per-slot:每槽独立 claim-run 循环 |
| 名义并发 | 3 | 6(fly scale count=2 × WORKER_CONCURRENCY=3,代码默认值才是 3;洪水下 Fly 还会临时多起机器) |
| 单活兜底 | (没提) | wall-clock 25 分(1500s) 超时强杀 + 重启浏览器;僵尸 8 分打回 pending |
| claim 排序 | 纯 priority ASC | priority 减 aging 提权(见 B) |
另一处常被漏掉的现状:worker 抢活的 claim_pending_scrape_jobs_batch 早已不是严格 priority ASC。6-15 的 #357 给它加了一层 aging 公平性安全网,专治 ⑤/⑨ 反复出现的 tier-0 被饿死:
pri=1)、不越级抢热源新鲜度。公式 effective = priority − LEAST(等待小时 / 6, priority−1),env QUEUE_CLAIM_AGING_HOURS=6 可热调免发 migration。会随时间漂的数字(时延分布 / 空跑率 / pri 积压)以自动数据快照页为准。三个 headline 结论:
| 段 | 6-19 快照 | 怎么读 |
|---|---|---|
| ① 时延(评论→入库,高/中意向) | 全部中位 29 分,但均值 34h、>6h 慢尾 35%(最慢 165h) | 一半 ~29 分内抓到(健康);尾巴被「老·常青钓点」拖爆——正是 ⑨-E 那条 38% 真尾巴,#377 在追但没追平。 |
| ② 空跑率 | fast-lane 常规 95% / 高意向注入 88% / cold-intent 99% / vip-rescan 99% | 退避(#366/#381)省的是"再入次数",绝对空跑率仍极高——lookback 窗口里大量"冒过一波就安静"的视频。这是当前最大、尚未根治的浪费(B 方案待灰度)。⚠️ cold-intent 99% 比 6-17 的 65% 命中恶化,钓点在枯,口径该收。 |
| ③ pri 积压(合计 717) | pri=1 pending 39 / 最老 3min;pri=3 400 / 57min;pri=5(tier-0)278 / 最老 13h;pri=0 秒清 | 高优先档(0/1)基本秒清;唯一真积压在 tier-0,正是 aging 安全网兜底那条尾巴(见 B)。 |
scrape_video_comments 自插的「台账行」(result={}、计数记在恒 0 的 comments_found 列)撑成假 100%。6-19 已剔除台账行 + 改行拉取避开 count 全表扫超时——所以现在 88–99% 是真实空跑率,不是口径假象。/update-scrape-report 一键命令。)f2==0.0.1.7 硬 pin httpx==0.27.2,升级必须协调 httpx 兼容,且只剩 3 个采集号逼近 24h 封号红线,dev 灰度务必盯每号 API 量。② 「解耦 pri 与 tier / 新建 pri=2」大半已落地:快慢分道(#363,tier-1 整号已 pri=3)+ 老视频视频级回扫(#377,cold-intent pri=3)都做了,现有 0/1/3/5 四档已把快慢分开,不必再新建 pri=2。③ 查 RPC 装没装不必连库:③ 段能跑出按 pri 分桶就是 RPC 在线的证据(否则 fallback legacy 单活路径、跑不出分桶)。直查生产 scrape_jobs(近 48h,2026-06-21):
| 指标 | 数值 | 读法 |
|---|---|---|
| cold-intent 完成回扫 | 13074 次 | 落在 1070 个钓点(实际量 ~6500/天;前一查被 PostgREST 默认 1000 行截断过,已分页拉全) |
| 零命中钓点(48h 一条新评论没抓到) | 947 / 1070(89%) | 它们白扫了 11874 次 = 91% 的活 |
| 其中被反复扫 10+ 次的 | 683 个 | 浪费高度集中在这批死钓点 ← 退避的靶子 |
| 真正有产出的活钓点 | 123 个 | 自身命中率也才 28%(每 4 竿中 1 条),但"抓到即清零"能保它们高频 |
| 退避可省(连续 2 次空跑起退) | 77% 的活 | K=3 起退省 70% |
| 抓不到? | 抓得慢? | |
|---|---|---|
| 现在(退避前) | 对它管的钓点基本不会——1378 钓点每 2h 该扫一遍 = 需 689 竿/h,产能 900 竿/h,够用、不会被 300 上限漏掉 | 有 ~2h 地板:每钓点 2h 才回去一次,最坏等 ~2h(+ 排队 57min) |
| 真正"抓不到"的另一类 | 一条全新冷视频还没冒过意向、没进 14d 清单 → cold-intent 不认识它,只能等「整号轮抓」扫到。但这是整号的活、非 cold-intent 的锅——它天生是"你先冒过一次鱼、我才把你列进钓点"的事后补扫。 | |
| 退避后的新风险 | 会让"先死后活"的钓点第一条新鱼更慢:一个退避到"8h 一去"的钓点突然来高意向,最坏等 8h 才被发现——比现在的 2h 还慢;若封顶设 24h 更糟,等于亲手拉长慢尾、跟目标反着来。 | |
| 改动面 | 动什么 |
|---|---|
| migration | videos 加 cold_intent_empty_streak + next_cold_intent_at 两列(与 #366 的 empty_streak/next_fast_lane_at 分开,语义隔离) |
cron cold-intent-rescan/route.ts | 每 tick 先结算上轮:0 新→streak++、间隔×2(封顶 6h,高意向钓点地板 2h);有新→清零、复位 120min。选 due 钓点改用 next_cold_intent_at。worker 不碰(cron 自结算,比 #366 由 worker 写更省)。 |
supabase/migrations/** + src/app/api/cron/** 都是高危路径,必须开分支 + CI + claude-review,禁直推 main。交接定稿见 repo docs/handoffs/2026-06-22-cold-intent-backoff.md。/queue/stats + Lark 采集快照卡。诚实边界:① 每日 tier 数变化库里无历史表、不可严格重建,本页用当前快照近似;② Leg C 的「8 槽悖论」已于 23:00 实查坐实=不是闲置,是 pri=1 队列常年积压 ~100 条 + 慢整号活占槽 + poison 活(见 Leg C);fly machines API 因 deploy token 无读权限未取到机器数,但 running 常态 8 + 78/分吞吐,有效并发 ~8;③ 僵尸判定用 7 天 / 高意向≥5(对齐升级线)。worker/services/queue.py 现状代码 + 各 PR body。scrape_jobs 近 48h 聚合;退避方案为设计稿、尚未上线(交接见 repo docs/handoffs/2026-06-22-cold-intent-backoff.md)。