fix(scheduler): drop cross-task mutex, quiet window + retry
Incident: evening full snapshot could be recorded as ok with already_completed/elapsed=0 after mutual skip with hourly sync, then next_run jumped to the next day. - Full-site snapshot never skips because another task is running - 23:00-01:00 Asia/Shanghai: defer discovery / snapshot_recent / candidate_cleanup to after 01:00; fans never defers - Skip records ok=false + skipped=true (not success / not completed) - Skip/fail reschedule soon (~5m); daily success still next snapshot_at - TaskControl return value drives next_run; system UI shows skip fields
This commit is contained in:
1 parent
39378c65b2
commit
9969e65202
10 files changed
+411
-84
No files matched your search
@@ -34,7 +34,14 @@ from ..analytics import (
|
||||
)
|
||||
from ..config import ConfigStore
|
||||
from ..database import Database
|
||||
from ..timeutil import day_bounds, next_daily_run, shanghai_date, shanghai_datetime
|
||||
from ..timeutil import (
|
||||
day_bounds,
|
||||
interval_next_run,
|
||||
next_after_snapshot_quiet_window,
|
||||
next_daily_run,
|
||||
shanghai_date,
|
||||
shanghai_datetime,
|
||||
)
|
||||
from .cache import DASHBOARD_CACHE, TTL_FANS, TTL_VIDEOS, build_cache_key
|
||||
|
||||
TRACKED_STATUSES = ("active", "unfaved")
|
||||
@@ -1971,9 +1978,10 @@ class DashboardQueries:
|
||||
"fans": int(next_daily_run(now, cfg.schedule.snapshot_at)),
|
||||
"snapshot": int(next_daily_run(now, cfg.schedule.snapshot_at)),
|
||||
"snapshot_seed": int(next_daily_run(now, cfg.schedule.seed_at)),
|
||||
"discover": int(now + discover_interval),
|
||||
"snapshot_recent": int(now + recent_interval),
|
||||
"discover": int(interval_next_run(now, float(discover_interval))),
|
||||
"snapshot_recent": int(interval_next_run(now, float(recent_interval))),
|
||||
}
|
||||
retry_seconds = 300
|
||||
for name, key, at in (
|
||||
("fans", "fans", cfg.schedule.snapshot_at),
|
||||
("snapshot", "snapshot", cfg.schedule.snapshot_at),
|
||||
@@ -1985,19 +1993,41 @@ class DashboardQueries:
|
||||
if not (isinstance(record, dict) and record.get("finished_at")):
|
||||
continue
|
||||
finished_at = int(record["finished_at"])
|
||||
summary = record.get("summary") if isinstance(record.get("summary"), dict) else {}
|
||||
skipped = bool(record.get("skipped") or summary.get("skipped"))
|
||||
failed = record.get("ok") is False
|
||||
if name in {"discover", "snapshot_recent"}:
|
||||
# 只按上次完成推下一档;进行中由 running 展示,不按墙钟抢跑。
|
||||
# 上次完成推下一档;skip/失败短补跑;静默窗推到 01:00 后。
|
||||
interval = (
|
||||
recent_interval if name == "snapshot_recent"
|
||||
else discover_interval
|
||||
)
|
||||
next_runs[name] = finished_at + interval
|
||||
if skipped:
|
||||
next_runs[name] = int(
|
||||
next_after_snapshot_quiet_window(float(finished_at))
|
||||
)
|
||||
elif failed:
|
||||
next_runs[name] = int(finished_at + retry_seconds)
|
||||
else:
|
||||
next_runs[name] = int(
|
||||
interval_next_run(float(finished_at), float(interval))
|
||||
)
|
||||
else:
|
||||
next_runs[name] = int(next_daily_run(finished_at, at))
|
||||
if skipped or failed:
|
||||
# 日更任务失败/skip:短间隔补跑,绝不按成功推到次日。
|
||||
next_runs[name] = int(finished_at + retry_seconds)
|
||||
else:
|
||||
next_runs[name] = int(next_daily_run(finished_at, at))
|
||||
errors = [
|
||||
{"task": name, **record}
|
||||
for name, record in tasks.items()
|
||||
if isinstance(record, dict) and record.get("ok") is False
|
||||
if isinstance(record, dict)
|
||||
and record.get("ok") is False
|
||||
and not record.get("skipped")
|
||||
and not (
|
||||
isinstance(record.get("summary"), dict)
|
||||
and record["summary"].get("skipped")
|
||||
)
|
||||
]
|
||||
counts = {str(row["status"]): int(row["n"]) for row in conn.execute(
|
||||
"SELECT status, COUNT(*) AS n FROM video GROUP BY status"
|
||||
|
||||
Reference in new issue
Block a user