From 39b0e0bb33ad20e0808328e70f8e50e0546677f0 Mon Sep 17 00:00:00 2001 From: Aryan Singh Date: Wed, 2 Sep 2026 11:40:24 -0500 Subject: [PATCH] Age a stalled transfer from created_at when started_at is missing The stall clock read started_at and returned zero when it was absent, which exempted every row written before that column existed -- precisely the jobs that have been stuck the longest. The live instance had one: a `downloading` job from before the upgrade, its transfer parked in Queued, Remotely, which the new check would have skipped forever. Falls back to created_at, which for a job still in `downloading` is the same question asked a slightly different way. Co-Authored-By: Claude Opus 5 --- app.py | 12 ++++++++++-- tests/test_acquisition.py | 11 ++++++++--- 2 files changed, 18 insertions(+), 5 deletions(-) diff --git a/app.py b/app.py index 9e8b646..82bbcea 100644 --- a/app.py +++ b/app.py @@ -1261,8 +1261,16 @@ def transfer_index(payload: list) -> dict[str, dict]: def stalled_for(job: sqlite3.Row) -> float: - """Minutes since this transfer last had anything to show for itself.""" - started = job["started_at"] if "started_at" in job.keys() else None + """Minutes since this transfer last had anything to show for itself. + + Falls back to created_at, which is what makes this work on rows written + before started_at existed. Those are `downloading` with no clock at all, + so returning zero for them would exempt precisely the jobs that have been + stuck the longest -- the ones this check exists to catch. + """ + keys = job.keys() + started = ((job["started_at"] if "started_at" in keys else None) + or (job["created_at"] if "created_at" in keys else None)) if not started: return 0.0 try: diff --git a/tests/test_acquisition.py b/tests/test_acquisition.py index 4d09dcc..6375225 100644 --- a/tests/test_acquisition.py +++ b/tests/test_acquisition.py @@ -253,10 +253,15 @@ def main() -> int: check("a 90-minute-old transfer reads as stalled", app.stalled_for(job_row("j4")) > app.STALL_MINUTES, app.stalled_for(job_row("j4"))) + # A row written before started_at existed still has to age out, or the + # jobs that have been stuck longest are the only ones exempt from the check. with app.db() as c: - c.execute("UPDATE jobs SET started_at=NULL WHERE id=?", ("j4",)) - check("a job with no clock is not spuriously stalled", - app.stalled_for(job_row("j4")) == 0.0) + c.execute("UPDATE jobs SET started_at=NULL, created_at=? WHERE id=?", + ((datetime.now(timezone.utc) - timedelta(days=2)) + .isoformat(sep=" ")[:19], "j4")) + check("a pre-upgrade row falls back to created_at", + app.stalled_for(job_row("j4")) > app.STALL_MINUTES, + app.stalled_for(job_row("j4"))) print(f"\n{ok} passed, {fail} failed") return 1 if fail else 0