调度:任务状态与进度持久化(跨重启/重建) #8

Closed
opened 2026-11-03 01:42:05 +08:00 by bot-ci · 3 comments

背景

今天 seed 后容器重启,白天被当成「到期」打穿克隆壳做了全量;当晚 23:15 反而因已 completed 不再跑。根因是:该不该跑、跑到哪、何时重试 都挂在进程内存的 next_run 上,一重建就丢,只能猜。

!7 里用墙钟/force 门槛补洞会越补越绕。下面用任务状态 + 进度持久化一次收口。

目标

调度器只认落盘状态,不因重启/重建提前或漏跑。

模型(每条任务一条记录)

字段 含义
name 任务名(snapshot / fans / snapshot_seed / snapshot_recent / discover / …)
stat_date 数据归属日(算哪天的数)
status pending / running / done / failed / deferred
due_at 最早可执行时间(墙钟到期或失败/跳过的延时重试)
progress 断点(如已处理 aid、阶段);无则空
updated_at 最后变更

失败、跳过、静默窗延后:只改 status + 写未来的 due_at,不清进度。

调度器

周期扫描状态库:

  1. status=running 且进程已死 → 视为中断,改为可续跑(pending/failed),保留 progress 与 stat_date
  2. due_at > now → 不触发
  3. due_at <= now 且未 done → 拉起;有 progress 则续跑,无则从头
  4. 成功 → done,清 running;按规则写入下一次 pending + 新 due_at(如次日 seed_at / snapshot_at)
  5. 失败/主动延后 → failed/deferred + due_at = now + 间隔

启动时不再把日更 next_run 置 0,也不根据「有 seeded」自行 force。

和今天两个问题的对应

  1. 重启要不要跑 / 算哪天
    看状态:有未完成(含中断的 running)→ 按同一 stat_date 续跑;已 done → 等下一条 pending 的 due_at;只有 seed 壳、晚上才 seal → 当晚 snapshot 的 due_at=snapshot_at,白天不会因重启提前全量。

  2. 延时重试被提前
    重试时间在 due_at 里;没到点调度器不跑。

进度

全站快照已有「当日 ok=1 跳过」半套断点,显式写入 progress(或沿用行级 ok)即可续跑。其它任务按粒度各自定义,接口统一。

非目标(本方案不做)

  • 不重做业务采集逻辑
  • 不引入分布式锁/多实例选举(仍单实例)
  • 不在本 Issue 里继续堆 !7 启动特例;!7 评审三项可保留,重启/force 相关改动与本方案对齐后再动

验收(原则)

  • 杀进程 / 重建容器后:未完成任务按原 stat_date 续跑,不重开错误的一天
  • seed 后、未到 snapshot_at:重启不会 force 全量
  • 失败写入未来 due_at 后重启:到期前不跑,到期后才跑
## 背景 今天 seed 后容器重启,白天被当成「到期」打穿克隆壳做了全量;当晚 23:15 反而因已 completed 不再跑。根因是:**该不该跑、跑到哪、何时重试** 都挂在进程内存的 `next_run` 上,一重建就丢,只能猜。 !7 里用墙钟/`force` 门槛补洞会越补越绕。下面用**任务状态 + 进度持久化**一次收口。 ## 目标 调度器只认落盘状态,不因重启/重建提前或漏跑。 ## 模型(每条任务一条记录) | 字段 | 含义 | | --- | --- | | `name` | 任务名(snapshot / fans / snapshot_seed / snapshot_recent / discover / …) | | `stat_date` | 数据归属日(算哪天的数) | | `status` | `pending` / `running` / `done` / `failed` / `deferred` | | `due_at` | 最早可执行时间(墙钟到期或失败/跳过的延时重试) | | `progress` | 断点(如已处理 aid、阶段);无则空 | | `updated_at` | 最后变更 | 失败、跳过、静默窗延后:只改 `status` + 写未来的 `due_at`,**不**清进度。 ## 调度器 周期扫描状态库: 1. `status=running` 且进程已死 → 视为中断,改为可续跑(`pending`/`failed`),**保留 progress 与 stat_date** 2. `due_at > now` → 不触发 3. `due_at <= now` 且未 `done` → 拉起;有 progress 则续跑,无则从头 4. 成功 → `done`,清 running;按规则写入**下一次** `pending` + 新 `due_at`(如次日 seed_at / snapshot_at) 5. 失败/主动延后 → `failed`/`deferred` + `due_at = now + 间隔` 启动时**不再**把日更 `next_run` 置 0,也不根据「有 seeded」自行 force。 ## 和今天两个问题的对应 1. **重启要不要跑 / 算哪天** 看状态:有未完成(含中断的 running)→ 按同一 `stat_date` 续跑;已 `done` → 等下一条 pending 的 `due_at`;只有 seed 壳、晚上才 seal → 当晚 snapshot 的 `due_at=snapshot_at`,白天不会因重启提前全量。 2. **延时重试被提前** 重试时间在 `due_at` 里;没到点调度器不跑。 ## 进度 全站快照已有「当日 ok=1 跳过」半套断点,显式写入 `progress`(或沿用行级 ok)即可续跑。其它任务按粒度各自定义,接口统一。 ## 非目标(本方案不做) - 不重做业务采集逻辑 - 不引入分布式锁/多实例选举(仍单实例) - 不在本 Issue 里继续堆 !7 启动特例;!7 评审三项可保留,重启/force 相关改动与本方案对齐后再动 ## 验收(原则) - 杀进程 / 重建容器后:未完成任务按原 `stat_date` 续跑,不重开错误的一天 - seed 后、未到 `snapshot_at`:重启不会 force 全量 - 失败写入未来 `due_at` 后重启:到期前不跑,到期后才跑

已在 !7 分支落地 #8(未合并)。

  • tip: f408da93c20237bb7bcfc36e74346452fd02ac0f
  • 模块:reaction_board/task_state.py(task:state:{name}:status / due_at / stat_date / progress)
  • 调度:启动只认落盘 due_at;恢复残留 running 同 stat_date 续跑;due_at > now 不提前
  • seed → 挂当晚 seal pending;seal 用 keep_scraped 打穿 seed、保留已 view 进度
  • 测试:test_task_state + test_{cli,scheduler,snapshot,timeutil,web_queries} 通过

PR:#7

已在 !7 分支落地 #8(未合并)。 - tip: `f408da93c20237bb7bcfc36e74346452fd02ac0f` - 模块:`reaction_board/task_state.py`(`task:state:{name}`:status / due_at / stat_date / progress) - 调度:启动只认落盘 `due_at`;恢复残留 `running` 同 `stat_date` 续跑;`due_at > now` 不提前 - seed → 挂当晚 seal pending;seal 用 `keep_scraped` 打穿 seed、保留已 view 进度 - 测试:`test_task_state` + `test_{cli,scheduler,snapshot,timeutil,web_queries}` 通过 PR:https://git.ofcss.com/kcloud/re-xxm/pulls/7

产品规则(2026-11-03 用户定)

  1. 跨天续跑:未完成的昨天应尽快按昨天 stat_date 做完(避免昨天没有快照基点)。
  2. 白天已成功全量 vs 晚间计划点:调度到期时若见 already_completed,但该次完成的开始时间早于当日计划点(如 snapshot_at 23:15),视为非计划内任务,仍按计划再跑;若开始时间等于或晚于计划点,才可跳过。
  3. 上条适用于所有每日固定钟点任务,不单全站快照。

实现侧:!7 按此收口(含升级回填、ensure_pending 跨日、CLI --force / keep_scraped、落盘 due_at 与内存调度同步)。

## 产品规则(2026-11-03 用户定) 1. **跨天续跑**:未完成的昨天应尽快按**昨天** `stat_date` 做完(避免昨天没有快照基点)。 2. **白天已成功全量 vs 晚间计划点**:调度到期时若见 `already_completed`,但该次完成的**开始时间早于**当日计划点(如 `snapshot_at` 23:15),视为非计划内任务,**仍按计划再跑**;若开始时间**等于或晚于**计划点,才可跳过。 3. 上条适用于**所有每日固定钟点**任务,不单全站快照。 实现侧:!7 按此收口(含升级回填、`ensure_pending` 跨日、CLI `--force` / `keep_scraped`、落盘 `due_at` 与内存调度同步)。

产品规则落地(tip f6bef59)

已在 !7 实现用户 2026-11-03 三条产品规则 + 评审缺口 A–D:

  1. 跨天:未完成的昨天 ASAP 用昨天 stat_date 续跑;stat_date < today 时 force seal,不受「今日未到 snapshot_at」挡住。
  2. 日更 off-plan:already_completed 但完成记录的 started_at(禁止 finished_at) 早于当日计划点 → 到点仍跑;started_at >= 计划点才跳过。
  3. 同上思路用于 snapshot / fans 等日更墙钟任务。

另:task:last 冷启动回填、ensure_pending 跨日不粘字段、keep_scraped 区分 seal/CLI --force、Scheduler due_reader 每轮认盘。

测过:test_{cli,scheduler,snapshot,timeutil,task_state,web_queries}。

## 产品规则落地(tip `f6bef59`) 已在 !7 实现用户 2026-11-03 三条产品规则 + 评审缺口 A–D: 1. **跨天**:未完成的昨天 ASAP 用昨天 `stat_date` 续跑;`stat_date < today` 时 force seal,不受「今日未到 snapshot_at」挡住。 2. **日更 off-plan**:`already_completed` 但完成记录的 **`started_at`(禁止 finished_at)** 早于当日计划点 → 到点仍跑;`started_at >=` 计划点才跳过。 3. 同上思路用于 snapshot / fans 等日更墙钟任务。 另:`task:last` 冷启动回填、`ensure_pending` 跨日不粘字段、`keep_scraped` 区分 seal/CLI `--force`、Scheduler `due_reader` 每轮认盘。 测过:`test_{cli,scheduler,snapshot,timeutil,task_state,web_queries}`。
Sign in to join this conversation.
No labels
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: kcloud/re-xxm#8