From d60cf31d977645255f87488cdcd4f70ddd5704f1 Mon Sep 17 00:00:00 2001 From: heaven Date: Wed, 5 Aug 2026 05:29:40 +0300 Subject: [PATCH] Add the checkpoint arm to the cold seam review: the cheap-resume counter-argument did not survive an independent panel given that exact fact --- docs/research/25-seam-cold-review.md | 107 ++++++++++++++++++++++++--- 1 file changed, 95 insertions(+), 12 deletions(-) diff --git a/docs/research/25-seam-cold-review.md b/docs/research/25-seam-cold-review.md index 424caaad..93bb3800 100644 --- a/docs/research/25-seam-cold-review.md +++ b/docs/research/25-seam-cold-review.md @@ -100,14 +100,79 @@ 4. **Пиннинг задачи к версионированному пути бинаря на старте** (4 из 5): иначе апгрейд воркера меняет поведение между попытками одной задачи. Прямо в цель «детерминизм» (канон §6). -## Чего у них не было и что меняет вес их вывода +## Чего у них не было — и естественный эксперимент на этом (плечо 2) -Панель отвергла родителя-ребёнка на посылке «редеплой уничтожает часы оплаченной работы». **У нас -эта посылка неверна:** движок чекпойнтится на уровне chunk×stage и резюмится -(`backend/internal/pipeline/resume.go`), поэтому остановка платформы стоит одного чанка в полёте, а -не прогона. Их аргумент про деплой у нас в разы слабее. +Панель отвергла родителя-ребёнка на посылке «редеплой уничтожает часы оплаченной работы». У нас эта +посылка слабее: движок чекпойнтится на уровне chunk×stage и резюмится +(`backend/internal/pipeline/resume.go`), поэтому остановка платформы стоит одного чанка, а не +прогона. Оркестратор выдвинул это как контраргумент — и проверил его чужими руками, а не оставил +своим мнением. -Но он не единственный. **Аргумент про вторую машину этим не бьётся** — см. ниже. +**Плечо 2 (`wf_2b45042d-115`, тоже чистое — проверено поагентно):** та же формулировка, тот же +переодетый домен, та же изоляция, **изменена ровно одна переменная** — в сценарий добавлено, что +воркер чекпойнтится ежеминутно и резюмится, а прерывание стоит минут и центов, никогда не часов. + +**Расклад не сдвинулся.** + +| | плечо 1 (без факта) | плечо 2 (с фактом) | +|---|---|---| +| Родитель-ребёнок + поток на stdout/дескрипторе | 0 выбрали, 5 отвергли | **0 выбрали, 5 отвергли** | +| systemd, юнит-на-задачу | 5 | **5** | +| Append-only журнал с курсором | 3 (+1 вариантом) | **5** (4 файлом, 1 таблицей внутри БД воркера) | +| Push по unix-сокету | 2 (+2 вторым) | **0** — упал по причине, к новому факту не относящейся | + +Поле «сколько весил дешёвый резюм» отвечали все пятеро, и все пятеро сказали одно: **факт снял +ограничения, но выбор не сделал.** «Весил много, но почти целиком в сторону, обратную очевидной» · +«повлиял на жизненный цикл и почти не повлиял на механизм» · «изменил мою уверенность и число +строк куда сильнее, чем выбор». Один прямо опознал постановку: *«свойство вставлено в бриф как +приманка к выводу „прерывания дешёвы, значит просто слинкуйте воркер в control plane“»*. + +Контрфактическую сторону тоже проверили: при ДОРОГОМ прерывании они ушли бы в ту же сторону +сильнее, а не назад. То есть переменная не является шарниром ни в одну сторону. + +**Что она реально изменила** — объём механизма при той же форме: не нужен протокол drain/quiesce, +не нужно рукопожатие «можно ли тебя останавливать», не нужен интерлок деплоя, `systemctl stop` +принимается как полный глагол остановки. Один оценил: «примерно половина механизма, который иначе +понадобился бы, этим свойством удаляется». И ещё: факт **сменил основание отказа** от варианта +«воркер внутри control plane» — аргумент «деплой убьёт задачу» перестал работать, и отказ переехал +на радиус поражения (леджер и аутентификация не должны делить адресное пространство и OOM-цель с +еженедельно меняющимся воркером). Вердикт тот же, обоснование другое. + +**Вывод: контраргумент про chunk-резюм не работает.** Настоящие драйверы, названные независимо от +стоимости прерывания: + +1. **Несовпадение времён жизни** — деплой несколько раз в неделю против задачи на 4–30 часов. + Выбирает супервизора раньше, чем вообще заходит речь о протоколе. +2. **Плановая недоступность control plane делает ДОЛГОВЕЧНОСТЬ требованием к каналу.** Он лежит + несколько раз в неделю ПО ЗАМЫСЛУ, значит любой push-канал нуждается в буфере, ретраях и + дедупликации — а буфер, который вы построите, и есть журнал. Это и убивает сокеты, пайпы, + journald и localhost-HTTP как ОСНОВНОЙ канал. +3. **Атомарность денег** — «юнит сделан» и «юнит стоил $X» обязаны быть одной транзакцией в + собственном хранилище воркера. +4. **Радиус поражения** — см. выше. +5. **Standalone не должен быть режимом** — ветка `--no-server`, которую никто не гоняет, гниёт. + +**Оговорка, которой у панели не было и которая касается только нас.** У нас при остановке платформы +движок останавливается ТОЖЕ (сигнал группе, а на stdout ещё и SIGPIPE), поэтому окна «движок пишет, +слушателя нет» не возникает и события не теряются. Аргумент №2 бьёт по нам не потерей событий, а +тем, что **каждый деплой платформы прерывает все идущие переводы**, ценой одного чанка каждый. +То есть вопрос сводится к продуктовому: **должен ли четырёхчасовой перевод переживать деплой +платформы?** Если да — журнал и отдельный юнит. Если нет — пайп законен, и цена названа явно. + +## Что панель 2 добавила сверх первой + +- **Разделять долговечный денежный канал и косметический канал живости:** `events.jsonl` + (fsync, append-only, с номерами) отдельно от `status.json` (атомарное переименование, ~2 с, явно + НЕдолговечный, из него никогда не биллить). 5/5. +- **Рамка outbox:** строка журнала — это ПРОЕКЦИЯ уже закоммиченной строки в БД воркера, + побайтово переиздаваемая после краха. Это превращает файл из «второй записи» в производный + артефакт и снимает половину возражений про fsync-дисциплину. +- **Перекос версий на длинной задаче:** воркер выкатывается еженедельно, задача идёт 30 часов — + значит задачу может ПРОДОЛЖИТЬ более новый воркер, чем её начал. Подняли 4 из 5; двое штампуют + версию воркера в каталоге задачи и отказываются резюмиться без явного + `--allow-version-change`. Для нас это прямо в канон §6 (детерминизм) и рядом с `brief_hash`. +- Один из пяти выбрал вариант «control plane читает приватную БД воркера» (в плече 1 его отвергали + 5/5) — единственное расхождение с D39.85 §4 за три панели. Меньшинство, но зафиксировано. ## Развилка: одна машина @@ -130,11 +195,29 @@ ## Открытое -- Строка 103 единого бэклога: транспорт потока. Ставка на stdout защитима (chunk-резюм делает цену - их сценария низкой), но должна быть записана как СОЗНАТЕЛЬНАЯ, с названной ценой будущего - переезда, а не как «переезд не окупается». +- **Вопрос владельцу, к которому всё свелось: должен ли идущий перевод переживать деплой + платформы?** Три панели, пятнадцать архитекторов, ноль голосов за нынешнюю форму — но их довод + упирается именно в это, а у нас есть смягчение (движок останавливается вместе с платформой, и + резюм стоит одного чанка). «Да» ⇒ журнал + отдельный юнит, движок перестаёт быть ребёнком. + «Нет» ⇒ нынешняя форма законна, и цена записана явно: каждый деплой прерывает все переводы. +- Строка 103 единого бэклога: транспорт потока. В ЛЮБОМ исходе ставка должна быть записана как + СОЗНАТЕЛЬНАЯ, с названной ценой, а не как «переезд не окупается» — эта формулировка опровергнута + плечом 2. - Три денежные дыры выше (реконсилятор · `uncertain` · сверка с провайдером) — строками единого бэклога, зона платформы их одна не закроет: половина работы в движке. -- Пиннинг к версионированному бинарю — туда же. -- Чекпойнт-плечо панели (`wf_2b45042d-115`) проверяет, меняет ли дешёвый резюм расклад. Результат - дописать сюда. +- Пиннинг к версионированному бинарю и **отказ резюмиться воркером другой версии** — туда же. +- Разделение `events` (долговечный, денежный) и `status` (косметический, не для биллинга) — принять + или отклонить при проектировании эмиттера, до его постройки. + +## Что из этого следует методически + +Три панели дали три разных урока о самой процедуре, и они дороже любого вердикта: + +1. **Абстрактная формулировка обязана быть изолированной по-настоящему.** Запрет сети без запрета + файлов — не изоляция. Проверять постфактум, поагентно. +2. **Слабая аргументация ≠ неверная позиция.** Оркестратор сперва принял предложение зоны по её + ссылкам, потом отклонил его же, открыв ссылки и увидев, что они не держат. Обе итерации спорили + о качестве ссылок, а не о существе. +3. **Свой контраргумент проверять чужими руками.** Довод «chunk-резюм делает нынешнюю форму + нормальной» звучал убедительно, был предъявлен как решающий — и не пережил панели, которой этот + факт дали прямым текстом. Один из пятерых даже опознал его как приманку в постановке.