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,