Фикс обрыва дренажа очереди повторным SIGTERM + model_version в API деталей

systemd шлёт TERM каждому процессу группы: повторный сигнал ловился внутри
writer.join() и обрывал дописывание базы (FileNotFoundError-агония очереди
в журнале, потерянный хвост). Теперь после первого TERM сигнал игнорируется,
join неубиваем. run_detail отдаёт model_version — прогоны разных версий
физики несравнимы, версия обязана быть видна.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
jze9
2026-07-08 04:28:10 +05:00
parent 2af292e094
commit 185d7f4361
2 changed files with 15 additions and 1 deletions

View File

@@ -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)
while True:
try:
writer.join()
break
except KeyboardInterrupt:
continue # уже завершаемся; ждём писателя до конца
logger._write("база дописана полностью")
assert best_pair is not None

View File

@@ -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,