fix(workflow-runs): take part in the job TTL purge so the headless API doesn't leak - #288
fix(workflow-runs): take part in the job TTL purge so the headless API doesn't leak#288kevin9327 wants to merge 1 commit into
Conversation
|
This branch has conflicts with |
…I doesn't leak The /workflow-runs surface shares _jobs, _cancel_events and _completed_at with the /generate endpoints, but never participated in their TTL purge: - create_run_from_image never called _purge_old_jobs(), so terminal job records accumulated indefinitely unless a /generate/from-image call happened to sweep them. That is the opposite of the intended use — /workflow-runs is the headless automation surface, where nothing else triggers the purge. - cancel_run set status="cancelled" but never stamped _completed_at, so _purge_old_jobs() (which only sweeps entries that have a completion time) could never evict a cancelled run — a permanent leak of a JobStatus + threading.Event per cancellation. Mirror what cancel_job and generate_from_image already do: purge on create, and record the completion time on cancel. Collection routing is intentionally left untouched here. Adds api/tests/test_workflow_runs_lifecycle.py (both cases fail before, pass after). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
3c07a07 to
d968439
Compare
|
Rebased onto current Verification (venv with fastapi/python-multipart/httpx, run from |
What
The headless
/workflow-runsAPI leaks job records. It shares the_jobs,_cancel_eventsand_completed_atdicts with the/generateendpoints, but never took part in their TTL purge:create_run_from_imagenever called_purge_old_jobs()./generate/from-imagepurges terminal jobs older than_JOB_TTLon every call;/workflow-runs/from-imagedidn't, so records only ever got swept if a/generatecall happened to run the purge. For a pure automation client (the surface/workflow-runsexists for), nothing triggers it and_jobsgrows without bound.cancel_runnever stamped_completed_at._purge_old_jobs()only sweeps entries that have a completion timestamp, andcancel_job(the/generatesibling) sets one when it cancels.cancel_runsetstatus="cancelled"but no timestamp, so a cancelled run could never be purged — a permanent leak of oneJobStatus+ onethreading.Eventper cancellation.Fix
Mirror the
/generateendpoints: call_purge_old_jobs()when a run is created, and record_completed_at[run_id]when a run is cancelled. Collection routing is intentionally left untouched.Verification
api/tests/test_workflow_runs_lifecycle.py: one test proves a stale terminal job is evicted when a new run is created, the other proves a cancelled run gets a completion stamp so the purge can reach it. Both fail before, pass after.python -m unittest discover -s testsinapi/(venv withfastapi+python-multipart+httpx): all pass, 0 failures.