What a job last did lived in the API process's memory and was gone at every restart; the audit log kept a run only when it was manual, failed or changed something. Now each run is a job_runs row written at its start and its end in its own session: who started it, its outcome as data, what it spent of the YouTube quota (quota_events.job_run_id) and the lines it logged (log_events.run_id, filterable in the log console). - a run a stopped process left open is marked interrupted at startup - "run now" is a one-shot job on the scheduler, which now always starts (interval jobs only with SCHEDULER_ENABLED); the manual actor is reset after the run, since pool threads are reused - the dashboard's last run comes from job_runs, so it survives a deploy - runs leave the audit log: migration 0109 moves the old job_run rows over (and back on downgrade); the audit page's run folding goes - job_run_retention_days (default 30) prunes old runs in audit_gc
36 lines
1.3 KiB
Python
36 lines
1.3 KiB
Python
"""The words a background run's row is written with — the `job_runs` table's status and trigger.
|
|
|
|
One source for the literals: the scheduler writes them, the startup sweep rewrites `running`, the
|
|
model's CHECK constraints are built from these sets, and the dashboard reads them back. A run's
|
|
status is never typed as a string anywhere else (the download pipeline's `cancelled`/`canceled`
|
|
drift, `downloads/status.py`, is why).
|
|
"""
|
|
|
|
from enum import StrEnum
|
|
|
|
|
|
class RunStatus(StrEnum):
|
|
RUNNING = "running"
|
|
OK = "ok"
|
|
ERROR = "error"
|
|
# Sync was paused: the run was due and deliberately did nothing.
|
|
SKIPPED = "skipped"
|
|
# The process stopped while the run was going (a deploy, a crash): set at the next startup, so a
|
|
# row never claims to be running in a process that no longer exists.
|
|
INTERRUPTED = "interrupted"
|
|
|
|
|
|
class RunTrigger(StrEnum):
|
|
# The run's interval came round.
|
|
SCHEDULED = "scheduled"
|
|
# An admin pressed "run now" (or "run all").
|
|
MANUAL = "manual"
|
|
# A user's own action started work on one target (a channel's full history), outside the
|
|
# recurring jobs.
|
|
AD_HOC = "ad_hoc"
|
|
|
|
|
|
def sql_in(values: type[StrEnum]) -> str:
|
|
"""`'a', 'b'` — the IN-list a CHECK constraint names these values with."""
|
|
return ", ".join(f"'{v.value}'" for v in values)
|