Étiqueter les événements de progression par run pour filtrer les retardataires

This commit is contained in:
Antoine Jacquin
2026-09-18 21:10:31 +02:00
parent ecd21d5b46
commit fbb56936f8
3 changed files with 46 additions and 13 deletions

View File

@ -72,6 +72,7 @@ import threading
import time
import urllib.parse
import urllib.request
import uuid
from pathlib import Path
from typing import Optional
@ -537,7 +538,7 @@ class ExportRequest(BaseModel):
# --- État du job de génération -------------------------------------------
_job = {"proc": None, "started": None, "returncode": None, "cmd": None, "finished": None, "qid": None}
_job: dict = {"proc": None, "started": None, "returncode": None, "cmd": None, "finished": None, "qid": None, "run_id": None}
_job_lock = threading.Lock()
@ -548,7 +549,7 @@ def _save_job_state():
plus le dernier run et la file de génération reste vide au rechargement.
Best-effort : n'acquiert pas le verrou (appelé aussi sous verrou).
"""
state = {k: _job.get(k) for k in ("started", "returncode", "cmd", "finished", "qid")}
state = {k: _job.get(k) for k in ("started", "returncode", "cmd", "finished", "qid", "run_id")}
try:
JOB_FILE.write_text(json.dumps(state), encoding="utf-8")
except OSError:
@ -567,6 +568,7 @@ def _load_job_state():
_job["cmd"] = state.get("cmd")
_job["finished"] = state.get("finished")
_job["qid"] = state.get("qid")
_job["run_id"] = state.get("run_id")
_load_job_state()
@ -881,7 +883,8 @@ def status(request: Request = None):
"regen_allowed": regen_allowed,
# Tuiles une par une (nom court, type, état) — agrégées depuis les
# événements écrits par le pipeline (.generation.events.jsonl)
"tiles": progress_snapshot(OUTPUT_DIR, viz_labels=_viz_step_labels()),
"tiles": progress_snapshot(OUTPUT_DIR, viz_labels=_viz_step_labels(),
run_id=_job.get("run_id")),
}
@ -1272,11 +1275,14 @@ def _launch_job(tiles, viz, req, qid=None):
LOG_FILE.parent.mkdir(parents=True, exist_ok=True)
log_fh = open(LOG_FILE, "w", encoding="utf-8")
# Nouveau run : journal d'événements remis à zéro (les tuiles affichées
# dans la file correspondent au run qui démarre, pas au précédent)
# dans la file correspondent au run qui démarre, pas au précédent) et
# identifiant de run transmis au pipeline (LIDAR_RUN_ID) : les événements
# tardifs d'un run annulé (workers survivants) sont filtrés à la lecture.
from .progress import reset_events
reset_events(OUTPUT_DIR)
run_id = uuid.uuid4().hex[:12]
_job.update({"proc": None, "started": time.time(), "returncode": None,
"cmd": cmd, "finished": None, "qid": qid})
"cmd": cmd, "finished": None, "qid": qid, "run_id": run_id})
_save_job_state()
# start_new_session : le pipeline et ses workers/PDAL forment leur
# propre groupe de processus — /api/stop peut le tuer en bloc sans
@ -1284,6 +1290,7 @@ def _launch_job(tiles, viz, req, qid=None):
# reste confiné à son groupe.
p = subprocess.Popen(cmd, stdout=log_fh, stderr=subprocess.STDOUT,
cwd="/app" if Path("/app").exists() else None,
env=dict(os.environ, LIDAR_RUN_ID=run_id),
start_new_session=True)
def _watch():