Фикс обрыва дренажа очереди повторным 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:
@@ -127,7 +127,11 @@ def run_evolution(
|
|||||||
# SIGTERM (остановка/рестарт systemd) -> исключение -> блок finally ниже
|
# SIGTERM (остановка/рестарт systemd) -> исключение -> блок finally ниже
|
||||||
# штатно досылает None и ЖДЁТ писателя: всё уже оценённое дописывается в
|
# штатно досылает None и ЖДЁТ писателя: всё уже оценённое дописывается в
|
||||||
# базу, а не теряется вместе с очередью в RAM («сохраняем всё»).
|
# базу, а не теряется вместе с очередью в RAM («сохраняем всё»).
|
||||||
|
# ВАЖНО: после первого TERM остальные игнорируем — systemd шлёт сигнал
|
||||||
|
# каждому процессу группы, и повторный TERM ловился ВНУТРИ writer.join(),
|
||||||
|
# обрывая дописывание базы (реальный кейс с потерей хвоста очереди).
|
||||||
def _terminate(_sig, _frm):
|
def _terminate(_sig, _frm):
|
||||||
|
signal.signal(signal.SIGTERM, signal.SIG_IGN)
|
||||||
raise KeyboardInterrupt("SIGTERM: дописываем очередь и выходим")
|
raise KeyboardInterrupt("SIGTERM: дописываем очередь и выходим")
|
||||||
|
|
||||||
signal.signal(signal.SIGTERM, _terminate)
|
signal.signal(signal.SIGTERM, _terminate)
|
||||||
@@ -174,10 +178,17 @@ def run_evolution(
|
|||||||
next_population.append(child)
|
next_population.append(child)
|
||||||
population = next_population
|
population = next_population
|
||||||
finally:
|
finally:
|
||||||
|
# с этого места нас нельзя прерывать: дописываем базу до конца
|
||||||
|
signal.signal(signal.SIGTERM, signal.SIG_IGN)
|
||||||
logger.finish()
|
logger.finish()
|
||||||
logger._write("дописываю хвост очереди в базу (писатель)…")
|
logger._write("дописываю хвост очереди в базу (писатель)…")
|
||||||
queue.put(None)
|
queue.put(None)
|
||||||
|
while True:
|
||||||
|
try:
|
||||||
writer.join()
|
writer.join()
|
||||||
|
break
|
||||||
|
except KeyboardInterrupt:
|
||||||
|
continue # уже завершаемся; ждём писателя до конца
|
||||||
logger._write("база дописана полностью")
|
logger._write("база дописана полностью")
|
||||||
|
|
||||||
assert best_pair is not None
|
assert best_pair is not None
|
||||||
|
|||||||
@@ -102,6 +102,9 @@ def run_detail(db_path: Path, run_id: str) -> dict | None:
|
|||||||
return {
|
return {
|
||||||
"run_id": row.run_id,
|
"run_id": row.run_id,
|
||||||
"search_mode": row.search_mode,
|
"search_mode": row.search_mode,
|
||||||
|
# версия физики обязана быть видна: прогоны разных моделей
|
||||||
|
# несравнимы, и «лучший» старой версии — не лучший вообще
|
||||||
|
"model_version": row.model_version,
|
||||||
"feasible": row.feasible,
|
"feasible": row.feasible,
|
||||||
"infeasible_reason": row.infeasible_reason,
|
"infeasible_reason": row.infeasible_reason,
|
||||||
"efficiency": row.efficiency,
|
"efficiency": row.efficiency,
|
||||||
|
|||||||
Reference in New Issue
Block a user