docs(generator): развилка фикса задачи 20 решена после ревью постановки
- Зачем:
- адверсарное ревью показало: генератор не обрабатывает SIGTERM
(python — PID 1, docker compose stop ведёт к SIGKILL посреди batch),
а вариант «ждать записи generator_batch_history» гонку не закрывает.
- Что:
- направление фикса: минимальный SIGTERM-хендлер с дозаписью batch,
альтернативы отклонены с доводами, bounded live mode вне скоупа.
- критерии дополнены: красный сценарий обязан ловиться, стабильность
обосновывается структурно, запрещены вероятностные смягчения.
- в диагноз внесены механизм обрыва и различие device/geo vs location.
- Проверка:
- чтение постановки: все находки ревью закрыты решением или доводом.
This commit is contained in:
+50
-12
@@ -51,34 +51,72 @@ location/device/geo -> DDS строит `dds.event` через `LEFT JOIN locati
|
|||||||
Судьба гипотез:
|
Судьба гипотез:
|
||||||
|
|
||||||
1. Запасная ветка рождения — **опровергнута**: ветка уже хранит донора
|
1. Запасная ветка рождения — **опровергнута**: ветка уже хранит донора
|
||||||
(`generation.py:194-209`, комментарий «Запасная ветка тоже восстановима»)
|
(`generation.py:196-209`, комментарий «Запасная ветка тоже восстановима»)
|
||||||
и громко падает на неполных locations. Перепроверено координатором по коду.
|
и громко падает на неполных locations. Перепроверено координатором по коду.
|
||||||
2. Недетерминизм live — **подтверждена частично**: решает не сид, а настенный
|
2. Недетерминизм live — **подтверждена частично**: решает не сид, а настенный
|
||||||
момент остановки процесса и какие топики успели дописаться.
|
момент остановки процесса и какие топики успели дописаться.
|
||||||
3. Проверочный SQL — **опровергнута в формулировке**: поля не «легитимно
|
3. Проверочный SQL — **опровергнута в формулировке**: поля не «легитимно
|
||||||
различаются», SQL маскирует пропущенный location под «смену фактуры».
|
различаются», SQL маскирует пропущенный location под «смену фактуры».
|
||||||
|
|
||||||
## Направление фикса (из диагноза)
|
Уточнение механизма обрыва (адверсарное ревью постановки, 2026-07-12;
|
||||||
|
перепроверено координатором по коду):
|
||||||
|
|
||||||
1. **Основное:** сделать остановку live управляемой — завершать после полного
|
- Генератор не обрабатывает SIGTERM: ловится только `KeyboardInterrupt`
|
||||||
batch/tick и flush всех топиков (bounded live mode или ожидание записи
|
(`service.py:175`), а `docker compose stop` шлёт именно SIGTERM. В compose
|
||||||
`generator_batch_history` с полными sent-счётчиками).
|
у сервиса generator нет `init:`/`stop_signal:`, python работает PID 1 —
|
||||||
|
SIGTERM игнорируется, и через grace-период прилетает SIGKILL: жёсткий
|
||||||
|
обрыв посреди batch, без `finally` и без flush.
|
||||||
|
- Почему отставание device/geo не валит гейт, а location валит: device/geo
|
||||||
|
привязаны к `click_id` (одно значение на визит) — при пропуске весь визит
|
||||||
|
однородно NULL, массив уникальных значений длины 1, проверка проходит;
|
||||||
|
location привязан к `event_id` — частичный пропуск даёт смешанный массив
|
||||||
|
(реальное значение + NULL) и «смену фактуры». Догонять device/geo фикс
|
||||||
|
не обязан — важна граница batch.
|
||||||
|
|
||||||
|
## Направление фикса (развилка решена после ревью постановки 2026-07-12)
|
||||||
|
|
||||||
|
1. **Основное: корректная обработка SIGTERM в генераторе.** Минимальный
|
||||||
|
хендлер: по SIGTERM выставить `_running = False`, дать текущему tick'у
|
||||||
|
дописаться (все четыре топика + flush + запись batch history), затем
|
||||||
|
штатный `finally`/`stop()`. Runtime-check после `docker compose stop`
|
||||||
|
дожидается фактического завершения контейнера. Так генератор завершается
|
||||||
|
только на границе batch — гонка снята по построению, а не вероятностно.
|
||||||
|
- **Отклонено: «ожидание записи `generator_batch_history` перед stop»
|
||||||
|
как самостоятельный фикс** — запись history подтверждает только
|
||||||
|
ПРОШЕДШИЙ batch (`service.py:586-603`, пишется после цикла публикации);
|
||||||
|
следующий batch к моменту stop уже может быть в полёте, и SIGKILL
|
||||||
|
оборвёт его так же. Довод — ревью постановки, перепроверен по коду.
|
||||||
|
- Bounded live mode (генератор сам останавливается по лимиту) — более
|
||||||
|
тяжёлая альтернатива; в скоуп не входит, возвращаться к ней только
|
||||||
|
если SIGTERM-хендлера окажется недостаточно (с доводом в задаче).
|
||||||
2. **Дополнительно:** precheck в seam-check на непарные
|
2. **Дополнительно:** precheck в seam-check на непарные
|
||||||
browser/location/device/geo live-строки — чтобы ошибка называла реальную
|
browser/location/device/geo live-строки — чтобы ошибка называла реальную
|
||||||
причину, а не «фактура поменялась».
|
причину, а не «фактура поменялась». Precheck не подменяет основную
|
||||||
3. Просто увеличить sleep перед stop — отклонено: снижает вероятность,
|
проверку: настоящая смена фактуры внутри `click_id` обязана падать
|
||||||
но гонку не убирает.
|
как и раньше.
|
||||||
|
3. **Отклонены любые вероятностные смягчения**, а не только «увеличить
|
||||||
|
sleep»: settle-sleep перед прогоном, рост `WAIT_LIVE_ROWS`/`LIVE_SECONDS`,
|
||||||
|
сужение окна `GEN_LIVE_CHECK_MINUTES` и прочие способы снизить
|
||||||
|
вероятность — гонку они не убирают и фиксом не считаются.
|
||||||
|
|
||||||
## Acceptance criteria
|
## Acceptance criteria
|
||||||
|
|
||||||
- [x] Причина расхождения 8/19 найдена и названа (код, не догадка) —
|
- [x] Причина расхождения 8/19 найдена и названа (код, не догадка) —
|
||||||
см. «Диагноз» выше.
|
см. «Диагноз» выше.
|
||||||
- [ ] Остановка live в runtime-check управляемая: batch дописывается во все
|
- [ ] Генератор корректно завершается по SIGTERM: текущий batch дописывается
|
||||||
топики целиком до остановки (направление фикса, пункт 1).
|
во все четыре топика целиком (flush + запись history), потом процесс
|
||||||
|
выходит; runtime-check дожидается фактической остановки контейнера
|
||||||
|
(направление фикса, пункт 1).
|
||||||
- [ ] Seam-check различает «непарные live-строки» и «смена фактуры»:
|
- [ ] Seam-check различает «непарные live-строки» и «смена фактуры»:
|
||||||
precheck называет реальную причину (пункт 2).
|
precheck называет реальную причину (пункт 2).
|
||||||
- [ ] `make generated-history-runtime-check` стабилен: N подряд прогонов
|
- [ ] Красный сценарий по-прежнему ловится: настоящая смена per-event
|
||||||
зелёные (N >= 3), зафиксировано в задаче.
|
фактуры внутри `click_id` (инъекция в тесте или контролируемое искажение
|
||||||
|
данных) валит гейт с прежним сообщением — precheck и фикс не сделали
|
||||||
|
проверку мягче.
|
||||||
|
- [ ] Стабильность обоснована структурно (гонка снята по построению:
|
||||||
|
завершение только на границе batch), а не статистикой прогонов;
|
||||||
|
`make generated-history-runtime-check` — N подряд зелёных (N >= 3)
|
||||||
|
как дымовая проверка поверх этого довода, зафиксировано в задаче.
|
||||||
|
|
||||||
## Blocked by
|
## Blocked by
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user