Полировка: писать одну запись напрямую — убран вечный завис в join()

Редкая гонка spawn+mp.Queue вешала главный процесс НАВЕЧНО после полировки
(22.5 часа простоя; /proc/*/stack: главный в do_wait, polish-писатель в
pipe_read — данные из очереди не доехали). Для одной записи процесс-писатель
и очередь не нужны вовсе: конкурентных писателей в этот момент нет, пишем
insert_run'ом напрямую. Плюс возврат SIGTERM к SIG_DFL после дренажа —
иначе фаза полировки неубиваема мягко.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
jze9
2026-07-09 11:51:21 +05:00
parent 9b98dbe27b
commit e52026d2b2

View File

@@ -22,7 +22,7 @@ from gausse.optim import worker_context
from gausse.optim.objective import EvaluationResult, build_run_record, evaluate from gausse.optim.objective import EvaluationResult, build_run_record, evaluate
from gausse.optim.progress_log import ProgressLogger, default_log_path from gausse.optim.progress_log import ProgressLogger, default_log_path
from gausse.optim.search_space import Genome, SearchBounds, crossover, mutate, repair, sample_genome from gausse.optim.search_space import Genome, SearchBounds, crossover, mutate, repair, sample_genome
from gausse.storage.database import run_writer_process from gausse.storage.database import insert_run, open_connection, run_writer_process
from gausse.storage.schema import RunRecord from gausse.storage.schema import RunRecord
@@ -204,6 +204,9 @@ def run_evolution(
# «Exception ignored … sem_unlink FileNotFoundError» после каждого цикла # «Exception ignored … sem_unlink FileNotFoundError» после каждого цикла
queue.close() queue.close()
queue.join_thread() queue.join_thread()
# после дренажа возвращаем обработку SIGTERM по умолчанию: иначе
# процесс в фазе полировки нельзя остановить мягко (только SIGKILL)
signal.signal(signal.SIGTERM, signal.SIG_DFL)
logger._write("база дописана полностью") logger._write("база дописана полностью")
assert best_pair is not None assert best_pair is not None
@@ -214,14 +217,17 @@ def run_evolution(
polished_genome, polished_result = polish_best(best_genome, db, bounds) polished_genome, polished_result = polish_best(best_genome, db, bounds)
logger._write(f"полировка готова: КПД {best_result.fitness*100:.2f}% -> {polished_result.fitness*100:.2f}%") logger._write(f"полировка готова: КПД {best_result.fitness*100:.2f}% -> {polished_result.fitness*100:.2f}%")
if polished_result.fitness > best_result.fitness: if polished_result.fitness > best_result.fitness:
conn_queue = ctx.Queue() # ОДНУ запись пишем напрямую, без процесса-писателя и очереди:
polish_writer = ctx.Process(target=run_writer_process, args=(conn_queue, db_path)) # конкурентных писателей в этот момент нет (основной уже завершён),
polish_writer.start() # а редкая гонка spawn+Queue вешала главный процесс в join()
conn_queue.put(build_run_record(polished_genome, polished_result, db, search_mode="evolve-polish")) # НАВЕЧНО (22.5 часа простоя, пойман по /proc/*/stack: главный в
conn_queue.put(None) # do_wait, писатель в pipe_read — данные из очереди не доехали).
polish_writer.join() conn = open_connection(db_path)
conn_queue.close() try:
conn_queue.join_thread() insert_run(conn, build_run_record(polished_genome, polished_result, db, search_mode="evolve-polish"))
finally:
conn.close()
logger._write("полированный геном записан в базу")
best_genome, best_result = polished_genome, polished_result best_genome, best_result = polished_genome, polished_result
return { return {