From b1bc9395cf98a36d047c8e83ece88d1ad7cff9af Mon Sep 17 00:00:00 2001 From: Hitonabi Date: Thu, 24 Sep 2026 17:42:42 +0200 Subject: [PATCH] jobengine: Nacharbeit laeuft vor dem Endzustand (Wettlauf, auf der Box sichtbar) Co-Authored-By: Claude Opus 5.5 --- backend/services/jobengine.py | 37 +++++++++++++++++++---------------- 1 file changed, 20 insertions(+), 17 deletions(-) diff --git a/backend/services/jobengine.py b/backend/services/jobengine.py index 80fd77a..ac2ddf4 100644 --- a/backend/services/jobengine.py +++ b/backend/services/jobengine.py @@ -96,7 +96,7 @@ def _pfad(job_id: str, endung: str) -> Path: def _speichern(job: dict) -> None: """Akte ohne Laufzeitfelder atomar schreiben.""" - daten = {k: v for k, v in job.items() if k not in _LAUFZEIT} + daten = {k: v for k, v in job.items() if k not in _LAUFZEIT and not k.startswith("_")} try: _jobs_dir().mkdir(parents=True, exist_ok=True) tmp = _pfad(job["id"], ".json.tmp") @@ -256,25 +256,28 @@ def _beende_gruppe(proc: subprocess.Popen) -> None: def _abschliessen(job: dict, code: int | None) -> None: - """Endzustand festhalten (genau einmal) und, wenn erfolgreich, die Nacharbeit ausführen.""" + """Endzustand festhalten (genau einmal). Bei Erfolg läuft die Nacharbeit VOR dem Endzustand: + Wer „done“ sieht, sieht auch schon ihre Wirkung (gesetzte Rolle, geleerte Zwischenspeicher).""" with _LOCK: - if job["state"] in _ENDE: + if job["state"] in _ENDE or job.get("_schliesst"): return - job["returncode"] = code - job["finished_at"] = time.time() - if job.get("canceled"): - job["state"] = "canceled" - elif job.get("zeitlimit") or (code is None and time.time() - job["started_at"] >= job["zeitlimit_s"] - 5): - job.update(state="failed", zeitlimit=True, error="Zeitlimit überschritten") - elif code is None: - job["state"] = "failed" - job.setdefault("error", "Der Auftrag endete ohne Rückmeldung.") - else: - job["state"] = "done" if code == 0 else "failed" + job["_schliesst"] = True + felder: dict = {"returncode": code} + if job.get("canceled"): + felder["state"] = "canceled" + elif job.get("zeitlimit") or (code is None and time.time() - job["started_at"] >= job["zeitlimit_s"] - 5): + felder.update(state="failed", zeitlimit=True, error="Zeitlimit überschritten") + elif code is None: + felder.update(state="failed", error=job.get("error") or "Der Auftrag endete ohne Rückmeldung.") + else: + felder["state"] = "done" if code == 0 else "failed" + if felder["state"] == "done": + _nacharbeit_ausfuehren(job) + with _LOCK: + job.update(felder, finished_at=time.time()) + job.pop("_schliesst", None) _PROCS.pop(job["id"], None) _speichern(job) - if job["state"] == "done": - _nacharbeit_ausfuehren(job) def _nacharbeit_ausfuehren(job: dict) -> None: @@ -523,7 +526,7 @@ def public_jobs(mit_log: bool = True) -> list[dict]: with _LOCK: _laden() _aufraeumen() - jobs = [{k: v for k, v in j.items() if k not in _INTERN} for j in JOBS.values()] + jobs = [{k: v for k, v in j.items() if k not in _INTERN and not k.startswith("_")} for j in JOBS.values()] for j in jobs: j["log"] = _protokoll_lesen(j["id"]) if mit_log else [] return sorted(jobs, key=lambda j: j.get("started_at") or 0)