feat(scheduler): persist task state/progress/due_at across restart (#8)

Replace process-start next_run=0 guesses with kv task:state records
(status, due_at, stat_date, progress). Startup recovers stale running,
honors future due_at for delayed retries, and resumes the same
stat_date. Seed only enqueues evening seal; seal force keeps non-seed
ok=1 as progress (keep_scraped). Keeps !7 fail-logging and cleanup retry.
This commit is contained in:
bot-ci committed 2026-11-03 01:50:00 +08:00
1 parent 51ffa44577
commit f408da93c2
7 files changed
+1793 -902

No files matched your search

+45
View File
@@ -661,6 +661,50 @@ def test_seeded_day_skips_without_force_and_seals_with_force():
db.close()
def test_seal_keep_scraped_skips_view_progress_punches_seed():
"""晚间 seal(keep_scraped):已 view 的 ok=1 作进度跳过,seed 壳仍打穿。"""
clock = 1_800_000_000
with tempfile.TemporaryDirectory() as tmp:
transport = SnapshotTransport({
1: view_for(1, view=111),
2: view_for(2, view=222),
})
db, repo, service = make_service(tmp, transport)
try:
add_video(repo, 1)
add_video(repo, 2)
today = DATE
# 两条 seed 壳
for aid, view in ((1, 10), (2, 20)):
repo.upsert_snapshot(
today, aid,
metrics={"view": view, "like": 1, "coin": 0, "favorite": 0,
"share": 0, "reply": 0, "danmaku": 0},
ok=True, err=None, captured_at=clock, src="seed",
)
# 模拟 seal 中途:aid=1 已真实采集
repo.upsert_snapshot(
today, 1,
metrics={"view": 111, "like": 1, "coin": 0, "favorite": 0,
"share": 0, "reply": 0, "danmaku": 0},
ok=True, err=None, captured_at=clock + 1, src="view",
)
repo.kv_set(service.seeded_key(today), "1")
sealed = service.run_if_needed(date=today, force=True)
assert sealed.completed is True
assert sealed.skipped >= 1 # aid=1 进度跳过
assert sealed.success >= 1 # aid=2 seed 打穿
# 只应请求 aid=2 的 view(aid=1 已是 view 进度)
assert transport.count("/web-interface/view") == 1
assert int(repo.get_snapshot(today, 1)["view"]) == 111
assert repo.get_snapshot(today, 1)["src"] == "view"
assert int(repo.get_snapshot(today, 2)["view"]) == 222
assert repo.get_snapshot(today, 2)["src"] == "view"
finally:
db.close()
def main():
test_snapshot_success_resume_and_force()
test_resume_skips_ok_and_retries_failed()
@@ -679,6 +723,7 @@ def main():
test_seed_same_day_completed_is_already_completed_not_no_source()
test_seed_from_last_completed_does_not_mark_completed()
test_seeded_day_skips_without_force_and_seals_with_force()
test_seal_keep_scraped_skips_view_progress_punches_seed()
print("OK: 每日快照、断点续跑、失败占位与粉丝采集测试全部通过")