[Bug] Unbounded _jobs-Dict + Race in /export/download vs Cleanup #19

Closed
opened 2026-05-27 12:21:05 +02:00 by ferdi2go · 1 comment
Owner

Probleme

a) _jobs waechst unbegrenztserver.py:241,289,310
Jobs werden nur bei erfolgreichem Status-Poll nach Completion via del _jobs[job_id] geloescht. Wenn Client abbricht, bleiben sie fuer immer.

b) Race /export/download vs _cleanup_zipsserver.py:366-381, server.py:478-497
Cleanup-Thread kann ZIP zwischen _zip_store.get(zip_id) und open(zip_path, "rb") loeschen. finally: os.unlink(zip_path) loescht ausserdem bei jeder Client-Disconnection -> kein Retry moeglich.

Fix

a) TTL-Cleanup-Thread analog _cleanup_zips, der _jobs mit created_at aelter als z.B. 1h purged.

b) Reference-Count im ZIP-Store: solange refs > 0, kein Cleanup. Bei Disconnect ZIP nicht im finally loeschen.

## Probleme **a) `_jobs` waechst unbegrenzt** — `server.py:241,289,310` Jobs werden nur bei erfolgreichem Status-Poll nach Completion via `del _jobs[job_id]` geloescht. Wenn Client abbricht, bleiben sie fuer immer. **b) Race `/export/download` vs `_cleanup_zips`** — `server.py:366-381`, `server.py:478-497` Cleanup-Thread kann ZIP zwischen `_zip_store.get(zip_id)` und `open(zip_path, "rb")` loeschen. `finally: os.unlink(zip_path)` loescht ausserdem bei jeder Client-Disconnection -> kein Retry moeglich. ## Fix **a)** TTL-Cleanup-Thread analog `_cleanup_zips`, der `_jobs` mit `created_at` aelter als z.B. 1h purged. **b)** Reference-Count im ZIP-Store: solange `refs > 0`, kein Cleanup. Bei Disconnect ZIP nicht im `finally` loeschen.
ferdi2go added the bugpriority: high labels 2026-05-27 12:21:05 +02:00
Author
Owner

Fix umgesetzt in server.py:

(a) Jobs-TTL

  • Job-Dict bekommt jetzt created_at beim Anlegen (/analyze, /export)
  • Background-Thread _cleanup_jobs purged Jobs aelter als _JOB_TTL = 3600s
  • Abandoned Jobs werden so nicht mehr ewig im RAM gehalten

(b) ZIP-Race + Disconnect-Loss

  • _zip_store[id] hat jetzt refs-Zaehler
  • /export/download inkrementiert refs vor Stream-Start, dekrementiert im finally
  • _cleanup_zips ueberspringt Eintraege mit refs > 0
  • os.unlink im Stream-finally entfernt - ZIP bleibt verfuegbar fuer Retry

Manueller Test: ZIP mit ID X einmal herunterladen -> 200 + 1094B. Direkt danach erneut mit gleicher ID -> 200 + 1094B. Vorher: 2. Versuch waere 404 gewesen, weil das finally die Datei geloescht hat.

Fix umgesetzt in `server.py`: **(a) Jobs-TTL** - Job-Dict bekommt jetzt `created_at` beim Anlegen (`/analyze`, `/export`) - Background-Thread `_cleanup_jobs` purged Jobs aelter als `_JOB_TTL = 3600s` - Abandoned Jobs werden so nicht mehr ewig im RAM gehalten **(b) ZIP-Race + Disconnect-Loss** - `_zip_store[id]` hat jetzt `refs`-Zaehler - `/export/download` inkrementiert `refs` vor Stream-Start, dekrementiert im `finally` - `_cleanup_zips` ueberspringt Eintraege mit `refs > 0` - `os.unlink` im Stream-finally entfernt - ZIP bleibt verfuegbar fuer Retry Manueller Test: ZIP mit ID X einmal herunterladen -> 200 + 1094B. Direkt danach erneut mit gleicher ID -> 200 + 1094B. Vorher: 2. Versuch waere 404 gewesen, weil das `finally` die Datei geloescht hat.
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: ferdi2go/OnlyFrames#19