From 185d7f4361563648469e42f6c0a78bc39b3737ea Mon Sep 17 00:00:00 2001 From: jze9 Date: Wed, 8 Jul 2026 04:28:10 +0500 Subject: [PATCH] =?UTF-8?q?=D0=A4=D0=B8=D0=BA=D1=81=20=D0=BE=D0=B1=D1=80?= =?UTF-8?q?=D1=8B=D0=B2=D0=B0=20=D0=B4=D1=80=D0=B5=D0=BD=D0=B0=D0=B6=D0=B0?= =?UTF-8?q?=20=D0=BE=D1=87=D0=B5=D1=80=D0=B5=D0=B4=D0=B8=20=D0=BF=D0=BE?= =?UTF-8?q?=D0=B2=D1=82=D0=BE=D1=80=D0=BD=D1=8B=D0=BC=20SIGTERM=20+=20mode?= =?UTF-8?q?l=5Fversion=20=D0=B2=20API=20=D0=B4=D0=B5=D1=82=D0=B0=D0=BB?= =?UTF-8?q?=D0=B5=D0=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit systemd шлёт TERM каждому процессу группы: повторный сигнал ловился внутри writer.join() и обрывал дописывание базы (FileNotFoundError-агония очереди в журнале, потерянный хвост). Теперь после первого TERM сигнал игнорируется, join неубиваем. run_detail отдаёт model_version — прогоны разных версий физики несравнимы, версия обязана быть видна. Co-Authored-By: Claude Fable 5 --- src/gausse/optim/evolutionary.py | 13 ++++++++++++- src/gausse/web/stats.py | 3 +++ 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/src/gausse/optim/evolutionary.py b/src/gausse/optim/evolutionary.py index 3c65006..07f5e70 100644 --- a/src/gausse/optim/evolutionary.py +++ b/src/gausse/optim/evolutionary.py @@ -127,7 +127,11 @@ def run_evolution( # SIGTERM (остановка/рестарт systemd) -> исключение -> блок finally ниже # штатно досылает None и ЖДЁТ писателя: всё уже оценённое дописывается в # базу, а не теряется вместе с очередью в RAM («сохраняем всё»). + # ВАЖНО: после первого TERM остальные игнорируем — systemd шлёт сигнал + # каждому процессу группы, и повторный TERM ловился ВНУТРИ writer.join(), + # обрывая дописывание базы (реальный кейс с потерей хвоста очереди). def _terminate(_sig, _frm): + signal.signal(signal.SIGTERM, signal.SIG_IGN) raise KeyboardInterrupt("SIGTERM: дописываем очередь и выходим") signal.signal(signal.SIGTERM, _terminate) @@ -174,10 +178,17 @@ def run_evolution( next_population.append(child) population = next_population finally: + # с этого места нас нельзя прерывать: дописываем базу до конца + signal.signal(signal.SIGTERM, signal.SIG_IGN) logger.finish() logger._write("дописываю хвост очереди в базу (писатель)…") queue.put(None) - writer.join() + while True: + try: + writer.join() + break + except KeyboardInterrupt: + continue # уже завершаемся; ждём писателя до конца logger._write("база дописана полностью") assert best_pair is not None diff --git a/src/gausse/web/stats.py b/src/gausse/web/stats.py index 7da95e2..727ec65 100644 --- a/src/gausse/web/stats.py +++ b/src/gausse/web/stats.py @@ -102,6 +102,9 @@ def run_detail(db_path: Path, run_id: str) -> dict | None: return { "run_id": row.run_id, "search_mode": row.search_mode, + # версия физики обязана быть видна: прогоны разных моделей + # несравнимы, и «лучший» старой версии — не лучший вообще + "model_version": row.model_version, "feasible": row.feasible, "infeasible_reason": row.infeasible_reason, "efficiency": row.efficiency,