fix: beat takes each job's last run from task_run, so a redeploy no longer resets the schedule (4408)
CI and images / lint (push) Successful in 3s
CI and images / extension-version (push) Successful in 3s
CI and images / frontend-build (push) Successful in 20s
CI and images / backend-lint-and-test (push) Successful in 33s
CI and images / integration (push) Successful in 2m18s
CI and images / sign-extension (push) Successful in 4s
CI and images / build-agent (push) Successful in 5s
CI and images / build-web (push) Successful in 1m41s
CI and images / smoke-web (push) Successful in 53s
CI and images / promote (push) Skipped
CI and images / lint (push) Successful in 3s
CI and images / extension-version (push) Successful in 3s
CI and images / frontend-build (push) Successful in 20s
CI and images / backend-lint-and-test (push) Successful in 33s
CI and images / integration (push) Successful in 2m18s
CI and images / sign-extension (push) Successful in 4s
CI and images / build-agent (push) Successful in 5s
CI and images / build-web (push) Successful in 1m41s
CI and images / smoke-web (push) Successful in 53s
CI and images / promote (push) Skipped
Beat's default scheduler kept its memory in a shelve file nothing persists, and a scheduler that remembers nothing waits a full interval before any job. Since the one-container image, every redeploy restarts beat, and no daily or weekly job had run since 2026-09-21 (cleanup, backup and download-event pruning, membership sync, thumbnail backfill, integrity check, vacuum). TaskRunScheduler seeds each entry's last_run_at at startup from task_run's newest start for that task: an overdue job runs at once, one not yet due waits the remainder, and one never recorded is due now. prune_task_runs now keeps each task's newest row however old, or a weekly job would look never-run a day after it ran and fire on every restart. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LVjrnpQjRgHdvq95rASoiR
This commit is contained in:
@@ -569,6 +569,12 @@ def test_prune_task_runs_deletes_failures_older_than_7d(db_sync):
|
||||
started_at=now - timedelta(days=10),
|
||||
finished_at=now - timedelta(days=9),
|
||||
)
|
||||
# A later run of the same task, so the old failure is not its newest row
|
||||
# (the newest is kept for beat — see the test below).
|
||||
_make_task_run(
|
||||
db_sync, status="ok",
|
||||
started_at=now - timedelta(hours=2), finished_at=now - timedelta(hours=1),
|
||||
)
|
||||
db_sync.commit()
|
||||
|
||||
result = prune_task_runs.apply().get()
|
||||
@@ -581,6 +587,37 @@ def test_prune_task_runs_deletes_failures_older_than_7d(db_sync):
|
||||
assert surviving is None
|
||||
|
||||
|
||||
def test_prune_task_runs_keeps_each_tasks_newest_row_however_old(db_sync):
|
||||
"""#4408: beat reads a job's last run from task_run. A weekly job's only
|
||||
row is older than the 24h ok-retention, and pruning it would make beat
|
||||
think the job never ran and fire it on every restart."""
|
||||
from sqlalchemy import select
|
||||
|
||||
from backend.app.models import TaskRun
|
||||
from backend.app.tasks.maintenance import prune_task_runs
|
||||
|
||||
now = datetime.now(UTC)
|
||||
weekly = "backend.app.tasks.fake.weekly"
|
||||
older = _make_task_run(
|
||||
db_sync, status="ok", task_name=weekly,
|
||||
started_at=now - timedelta(days=14), finished_at=now - timedelta(days=14),
|
||||
)
|
||||
newest = _make_task_run(
|
||||
db_sync, status="ok", task_name=weekly,
|
||||
started_at=now - timedelta(days=7), finished_at=now - timedelta(days=7),
|
||||
)
|
||||
db_sync.commit()
|
||||
|
||||
prune_task_runs.apply().get()
|
||||
|
||||
db_sync.expire_all()
|
||||
surviving = set(db_sync.execute(
|
||||
select(TaskRun.id).where(TaskRun.task_name == weekly)
|
||||
).scalars().all())
|
||||
assert surviving == {newest}
|
||||
assert older not in surviving
|
||||
|
||||
|
||||
def test_prune_task_runs_keeps_recent_failures(db_sync):
|
||||
from sqlalchemy import select
|
||||
|
||||
|
||||
Reference in New Issue
Block a user