Это не выпуск функций. Здесь записано, почему WEOUR OS сначала остановила новые функции, чтобы построить слой производственной страховочной сети: резервное копирование, планирование, публикацию, откат.
- Область этого раунда
- Производственная инфраструктура (запись о строительстве этого раунда): резервное копирование · планирование · публикация · откат
- Ритм резервного копирования
- Раз в 6 часов (таймер launchd)
- Контракт успешного резервного копирования
- Четыре шлюза: stream / decrypt / structure / durable
- Градация здоровья резервного копирования
- HEALTHY ≤ 7 часов · DEGRADED ≤ 12 часов · STALE > 12 часов (FAILED означает неудачу отдельной попытки, а не уровень)
- Бюджет stagger планировщика
- По умолчанию 30 секунд (нижняя граница 0 / верхняя граница 60), развязан с интервалом tick
- Планировщик tick P50 (4 магазина)
- 119.5 s → 31.9 s
- Планировщик tick P95 (4 магазина)
- 369.9 s → 55.3 s
- Загрузка планировщика (duty cycle)
- 83.7% → 17.1%
- Выборка приёмки планировщика
- 11 tick (4 магазина)
- Первый Trial canonical-развёртывания
- 2026-09-23 неудача, конвейер выполнил откат
- Производственная проверка этого раунда
- release 20260923-rpg13-7f685ca (CANONICAL, validation / smoke PASS)
- Тесты управления публикацией
- 123 → 157
- Состояние хранения на момент наблюдения этого раунда
- release 19 · кандидатов на удаление 1 · источник неизвестен 6 · защищено 5 (retention dry-run)
- Коммит исправления планировщика
- Anxin360/weour-os@e92b059
- Коммит исправления readiness / iowait
- Anxin360/weour-os@d4cbe01
- Верхушка ветки trial Foundation V1.1
- Anxin360/weour-os@b8cc817
- Коммит управления V1.3
- Anxin360/weour-os@7f685ca
Почему сначала остановили новые функции
WEOUR OS уже работает в production с реальными данными: реальные магазины, реальные циклы планировщика, реальные записи о публикациях. Чем больше функций, тем выше цена ошибки; но до этого несколько вещей не были по-настоящему замкнуты:
- неизменяемые release только накапливались, диск сам не становился чище;
- резервные копии были, но «действительно ли эта копия успешна» не проверялось автоматически, и не было сигнала здоровья, который можно было бы автоматически потреблять;
- планировщик выглядел занятым, но занят он был в основном не бизнес-работой.
Это не проблемы функций, а проблемы способа работы. Сначала их исправить — и каждая следующая функция обойдётся дешевле.
Что изменилось
1. Хранение release: неизменяемым release нужна управляемая очистка
Публикация — это неизменяемая цельная сборка: переключение делается символической ссылкой на новый каталог release, откат — переключением обратно на старый каталог. Такая схема делает откат дешёвым, но ценой постоянного роста диска — при аудите каталоги release занимали около 74% использованного диска этой машины (44.12 GB / 59.44 GB использовано), а на том же диске ещё нужно оставлять запас для сборки.
Поэтому очистка была написана как приоритет категорий, а не «удалить самое старое»:
CURRENT > IMMEDIATE_ROLLBACK > DIAGNOSTIC > FAILED_TRIAL > VALIDATED_ANCHOR
> MIGRATION_BOUNDARY > MANUAL_PROTECT > UNKNOWN_PROVENANCE > ROUTINE
Только ROUTINE попадает в повседневную квотную очистку, остальные категории не удаляются попутно. Особенно важна одна из правил: release неудачного Trial не понижается до ROUTINE только потому, что его commit позже попал в main. Решение принимается только по доказательствам жизненного цикла, а не по имени каталога, временной метке или переписке.
2. Резервное копирование: от «есть копия» к «проверяемой копии»
Форма резервного копирования не переделывалась: хост резервного копирования сам забирает данные, production предоставляет только ограниченный поток принудительных команд, а закрытый ключ для расшифровки никогда не попадает на production-хост. Изменения произошли на стороне забора:
- Ритм и RPO: раз в 6 часов (таймер launchd), целевой RPO — 6 часов. Ритм объясним и не зависит от дрейфующего интервального таймера.
- Четыре шлюза: запуск считается успешным только после прохождения всех четырёх шлюзов — stream, decrypt, structure, durable. Код выхода
pg_dump, равный 0, сам по себе никогда не является успехом. - Неудача есть неудача: при провале любого шлюза запуск получает
FAILED,last-successне обновляется, очистка не выполняется. - Градация здоровья:
HEALTHY(≤ 7 часов) /DEGRADED(≤ 12 часов) /STALE(> 12 часов) выводится из «часов с момента последнего успеха» и может потребляться мониторингом напрямую как код выхода;FAILEDописывает отдельную попытку, а не уровень. - Тесты с внедрением отказов: покрывают удалённый отказ, слишком маленький артефакт, ошибку расшифровки, недопустимую структуру, наложенный запуск, устаревшую блокировку, политику хранения и повреждённое состояние здоровья.
Последний шаг — вернуть сигнал здоровья в production: после прохождения четырёх шлюзов хост резервного копирования через ограниченную учётную запись отправляет снимок в production; эта учётная запись может писать только один фиксированный файл, не может переключать release, не может перезапускать сервисы и тем более не может читать какие-либо ключи.
3. Планировщик: разделить семантику tick и stagger
Диагностика сначала дала неудобный вывод: в интервале работы 4 магазинов 96.8% настенного времени tick уходило на детерминированный stagger-сон — из 2610 секунд 84 секунды были реальной работой. В расчёте на каждое action сам сон занимал 4150 секунд/час, почти 115% настенного времени.
Корневая причина — смешение семантики: окно stagger было привязано к интервалу tick (180 секунд), а фиксированные задержки нескольких action на одном последовательном runner складывались последовательно.
Исправление разделило две вещи: интервал tick используется только для отчётного cadence и больше не участвует в расчёте stagger; у stagger есть собственный бюджет, по умолчанию 30 секунд, допустимые значения 0 / 30 / 60, недопустимые значения откатываются к значению по умолчанию. Измерения после развёртывания:
- tick P50 119.5 секунды → 31.9 секунды, P95 369.9 секунды → 55.3 секунды;
- загрузка (duty cycle) 83.7% → 17.1%;
- запланированный сон совпадает с фактическим, остальное время — объяснимая работа с API и DB;
- timer, cadence, переключатели магазинов и бюджет параллелизма не менялись.
4. Единый canonical-конвейер публикации
Публикация — больше не цепочка ручных команд, а упорядоченный воспроизводимый конвейер:
provenance → preflight → release lock → scheduler quiet window → source
→ build backend → monitored build → immutable release + manifest
→ candidate validation → rollback readiness → atomic switch → restart
→ readiness → functional smoke → state write → history → retention dry-run
Несколько мест сделаны намеренно: сборка выполняется только в тихом окне планировщика; переключение атомарно; цель отката проверяется до и после переключения; код выхода systemctl restart явно записывается как «юнит запущен», а не «приложение готово».
5. Первый Trial провалился — и был полностью сохранён
Первый Trial canonical-развёртывания прошёл свой путь успеха, затем провалился на этапе smoke, и конвейер выполнил свой canonical-откат.
Причина не в приложении: systemctl restart при возврате означает лишь, что юнит запущен, а не что порт уже привязан. Тогдашний smoke был немедленной HTTP-проверкой без какого-либо ожидания readiness, поэтому он увидел отказ соединения и ошибку шлюза.
Важен именно способ обработки: никто не обошёл конвейер, чтобы вручную исправить этот шаг. Конвейер остановился по своему контракту отказа и выполнил откат, этот release был сохранён для диагностики, а на этапе очистки не было выполнено ни одного удаления. Production продолжал нормально обслуживаться на release до переключения.
Последовавшее исправление сделало readiness ограниченным опросом, а мониторинг сборки — реально измеряющим iowait. При повторном прогоне прежний режим отказа был корректно поглощён новой логикой как временная неготовность, а не как неудача развёртывания.
6. Управление: оставить доказательства и «кто может публиковать», и «что было опубликовано»
Правила этого раунда добавлены для будущих себя:
- canonical deploy only: повседневная публикация идёт только через конвейер; исключения должны быть явно помечены.
deploymentMethod: каждая новая запись жизненного цикла несёт перечисление метода, исторические записи дописываются, а не переписываются; неопределимые исторические записи помечаютсяUNKNOWN_LEGACYи никогда не угадываются какCANONICAL.- Классификация неудачного Trial:
FAILED_TRIALопределяется по доказательствам жизненного цикла и не занимает повседневную квоту очистки. - Приоритет диагностических доказательств: перед удалением нужно записать обратно диагностическую запись уровня KB без ключей и проверить её; если записать не удаётся — удаление отклоняется.
На точке наблюдения этого раунда (production release 20260923-rpg13-7f685ca) политика хранения представлена в dry-run: запись показывает 19 release, из них 1 кандидат на удаление, 6 с неизвестным источником, 5 защищённых. Очистка происходит только при полных доказательствах (цель подтверждена как неиспользуемая, диагностическая запись записана обратно и проверена).
Как мы это проверили
Способ проверки каждого звена по возможности опирается на перепроверяемые доказательства, а не на «выглядит нормально»:
- резервное копирование проверено тестами с внедрением отказов и реальными прогонами четырёх шлюзов, сигнал здоровья потребляется мониторингом;
- исправление планировщика сначала смоделировано в изолированной среде с измеренными offset, затем после развёртывания сравнено до и после на 11 реальных tick;
- конвейер публикации проверен тем, что ему дали самостоятельно выполнить реальную публикацию: readiness пройден, functional smoke прошёл по шести маршрутам, состояние записано конвейером, и только затем выполнен dry-run retention;
- правила управления покрыты тестами — от «неудачный Trial не понижается» до «публикатор отклоняет аномальную нагрузку».
Что это не значит
Это не выпуск функций и не означает, что инфраструктура завершена.
В записи этого раунда ограничения резервного копирования прописаны явно: ключ расшифровки существует в единственном экземпляре и находится на той же машине, что и шифротекст; нет второй копии в другом месте; нет PITR, RPO — это и есть ритм в 6 часов; у резервного копирования медиа нет политики хранения; роли и глобальные объекты не входят в dump. Это известные компромиссы, а не проигнорированные детали.
Исправление планировщика решает только вопрос «куда уходит время», это не оптимизация параллелизма или производительности.
Управление также не покрывает всю историю: на точке наблюдения этого раунда всё ещё существуют старые release с неопределимым источником, они консервативно сохранены.
Эта запись относится к раунду строительства с 2026-09-22 по 2026-09-23. Все производственные состояния в тексте — факты на точке наблюдения этого раунда, а не текущее состояние в любой последующий момент; последующие производственные изменения вне рамок этой статьи.
И наконец, это не обещание будущих версий, сроков или возможностей. Здесь записано уже произошедшее обновление инфраструктуры и доказательства, которые его подкрепляют.
Где доказательства
Каждое фактическое утверждение в этой записи указывает на объект, который можно перепроверить: соответствие коммитов, идентификаторов развёрнутых release и количества тестов зарегистрировано в таблице фактов выше, а также в evidence-записи этой статьи. Основной текст лишь объясняет смысл и не повторяет перечень идентификаторов.