fix(automation): 修正调度口径——作业每 5 分钟跑一轮,interval_seconds 只是单行重检间隔

P1a 把两个「间隔」混成了一个:next_run_at 用的是 job.interval_seconds,于是
作业 6 小时才跑一轮、每轮 200 条 = 800 条/天,全库 12,881 条要 16 天才能过
一遍 —— 等于没巡检,与规划里「账号 6 小时内被查一次」的目标差了两个数量级。

按规划原意修正:
- TICK_SECONDS 60 → 300:作业每个 tick(5 分钟)跑一轮;
- next_run_at 改为 finished + TICK_SECONDS,不再用 interval_seconds;
- interval_seconds 只作为「同一行多久重新检测」的选行 cutoff(选行逻辑本来就
  是这么用的,无需改);
- 容量口径随之更新:5 分钟一轮 × 200 条 = 40 条/分钟 ≥ 需要的 36 条/分钟,
  冷启动约 5.4 小时覆盖全库,稳态每轮一百多条;
- 前端标签由「巡检周期(小时)」改为「单账号重检间隔(小时)」,说明文案写清
  「每 5 分钟跑一轮」,避免运维误以为是作业周期。

同步更新规划文档 §7.2/§7.3(含两个间隔不能混的说明)与附录 C 的三段上线
步骤(立即跑一次 → 每轮 50 观察 → 每轮 200 启用)。
This commit is contained in:
yml2213
2026-10-02 19:54:31 +08:00
parent 2379598800
commit 7aaaeebe2b
5 changed files with 49 additions and 23 deletions
+17 -11
View File
@@ -374,8 +374,8 @@ lookup_order(order_no) -> {
| `key` (PK) | 作业标识:`ck_health` / `ck_relogin` |
| `title` | 展示名 |
| `enabled` | 开关(**默认 false**) |
| `interval_seconds` | 检测周期(CK 巡检默认 6h) |
| `batch_size` | 单轮处理上限(`ck_health` 默认 **200**,按 6h 周期反推,见 §7.3;`ck_relogin` 默认 20) |
| `interval_seconds` | **单账号重检间隔**(CK 巡检默认 6h)—— 注意不是作业运行周期,作业每个 tick 都跑(见 §7.3) |
| `batch_size` | 单轮处理上限(`ck_health` 默认 **200**,按「5 分钟一轮 × 200 条 = 40 条/分钟 ≥ 需要的 36 条/分钟」反推,见 §7.3;`ck_relogin` 默认 20) |
| `concurrency` | 单轮并发 |
| `config` (JSON) | 作业私有配置(退避表、每日上限、失败率阈值等) |
| `last_run_at` / `next_run_at` | 调度用 |
@@ -391,8 +391,10 @@ lookup_order(order_no) -> {
### 7.3 P1a · 作业一:`ck_health`(巡检,只检测)
- **选取范围**:从**持有 CK 的 `login_tasks` 行**里挑(即现有 `cookie_holding_filter()` 的口径:`cookie is not null and cookie != ''`)—— CK 是挂在登录记录上的,不是挂在 `accounts` 上;
- **调度**:每 5 分钟 tick 一次;每轮挑「距上次检测超过 `interval_seconds`(默认 6h)」的行,最多 `batch_size`(默认 50)个。
- **调度**:**每 5 分钟跑一轮**(`TICK_SECONDS = 300`);每轮挑「距上次检测超过 `interval_seconds`(默认 6h)」的行,最多 `batch_size`(默认 200)个。
这样全库是**滚动覆盖**的,负载平稳,不会出现「整点扫 1.2 万个账号」的尖峰;
- **⚠️ 两个「间隔」不能混**:`TICK_SECONDS` 是**作业多久跑一轮**;`job.interval_seconds` 是**同一行数据多久重新检测一次**(只用于选行 cutoff)。
如果误把作业周期设成 `interval_seconds`,就变成「200 条/6 小时 = 800 条/天」,全库 12,881 条要 16 天才能过一轮 —— 等于没巡检(P1a 实现时踩过一次,已修);
- **排除**:`auto_managed = false`;`attempt_status in (pending, running)`(正在被批次处理);最近 10 分钟内刚检测过(防重入);
- **动作**:调用现有 `check_douyu_cookie()`(两个只读接口,直连),写回 `ck_check_status / ck_check_result / ck_checked_at`;
- **并发**:默认 5,沿用现有 `ThreadPoolExecutor` 模式;
@@ -406,18 +408,19 @@ lookup_order(order_no) -> {
| 当前状态分布 | valid **10,079** / invalid **905** / **从未检测 1,897** |
| 历史检测时间 | 最早 2026-08-07,最新 2026-10-02(人工零星跑的) |
按 6 小时巡检周期折算:
按「单账号 6 小时内重检一次」折算:
```
需要吞吐 = 12,881 ÷ 6h ≈ 2,147 行/小时 ≈ 179 行/5 分钟 tick
单行耗时 ≈ 0.5~1s(两个只读接口),并发 5 → 179 行约 18~36 秒/tick ← 可行
需要吞吐 = 12,881 ÷ 6h ≈ 36 行/分钟
一个 tick = 5 分钟 → 每轮需要 ≈ 180 行
单行耗时 ≈ 0.13s(实测 3 个账号 0.4s,并发 3)→ 180 行并发 5 约 5 秒/轮 ← 很轻松
```
**所以 `batch_size` 默认值要按周期反推,不能拍一个 50**:50 的话实际巡检周期会被拉长到 ≈ 21 小时(12,881 ÷ (50/5min))。建议:
**`batch_size` 默认 200**(比需要的 180 留一点余量):
- 默认目标周期 6h → `batch_size` 默认 **200**(留余量),`concurrency` 默认 **5**;
- **冷启动**:首次启用时先跑小批量(如 50)观察一轮,确认无异常和风控反应,再调到目标值;
- 取行时按 `ck_checked_at ASC`(最久没查的优先),自然错峰,不会同一时刻全量到期。
- **冷启动**:全库积压 12,881 条,按 200 条/5 分钟 ≈ **5.4 小时**跑完第一遍,随后进入稳态;
- **稳态**:每分钟约 36 条到期,每轮扫到一百多条,负载很轻;
- 取行按 `ck_checked_at ASC`(最久没查的优先,`NULLS FIRST`),自然错峰,不会同一时刻全量到期。
### 7.4 P1b · 作业二:`ck_relogin`(自愈,默认关闭)
@@ -947,7 +950,10 @@ pending_material(待用户补资料) ← 模板有必填字段时的
- **P1a 先上**:只做「帮你把失效 CK 找出来」,零风险、立刻省掉大批量人工点检测;观察一到两周、确认检测结果与人工判断一致后,再打开 P1b;
- **P0 随时可做**:只动本系统,order_site 侧代码改动量为 0;
- **P1b 代码可与 P1a 一起写完**,但开关默认关闭,等观察结论再开;
- **P1a 上线按三段走**:先 `batch_size=50` 跑一轮看风控反应 → 调到 200 达到 6h 周期 → 观察一到两周确认检测结果与人工判断一致。
- **P1a 上线按三段走**:
① 打开 `AUTOMATION_ENABLED=true` 并重启(此时作业开关仍关着,调度线程空转,不会有任何检测);
② 在 `/automation` 页点「立即跑一次」并先把「每轮条数」设 **50** —— 手动触发不受开关限制,正好用来观察斗鱼风控与失败率;
③ 确认无异常后把「每轮条数」调 **200** 并打开「启用」,调度器接管(5 分钟一轮,约 5.4 小时覆盖全库一遍),观察一到两周确认检测结果与人工判断一致。
依赖关系: