Глава 26 / 51
Том 4 · Глубокое погружение в перезагрузку

Модель перезагрузки: координация целевого состояния

Горячая перезагрузка — не функция, а система координации. Глава строит её ментальную модель: поняв «намерения» и «снимки», следующие пять глав лишь добавляют детали к этой модели.

Обзор

Позиционирование: картографическая глава тома 4. «Поколения» из гл. 03 и список отклонений из гл. 13 сливаются здесь в полную модель; гл. 27–31 раскрывают запуск, передачу, поколения, GC и антипаттерны.

26.1 Введение

Наивный дизайн горячей перезагрузки — «вызвал функцию, синхронно поменял код» — в конкурентном мире мгновенно банкротится: в момент вызова 50 запросов ещё крутятся на старом коде; что менять и как? Ответ XServer — моделировать это как координацию целевого состояния: вы подаёте не «поменяй сейчас», а «я хочу, чтобы сервис стал таким содержимым»; координатор (отдельный управляющий поток reload) последовательно сравнивает, компилирует и публикует, записывая итог как опрашиваемое терминальное состояние. Модель естественно решает три класса проблем: конкурентность, отказы и повторные подачи.

26.2 Координация целевого состояния

вы (любой worker-поток) управляющий поток reload (последовательно) ──────────────────── ────────────────────── xsReloadHostSubmit(pHost) │ сразу возвращает стабильный ID (неблокирующе) ▼ ┌─ принять (в очередь pending ≤64) xsdReloadQuery(id, &result) ◀── терминал ──┤ заморозить неизменяемый снимок (конфиг+исходники → Revision) State / Revision / Message │ полная прекампиляция кандидатов ──провал──▶ FAILED (старое поколение работает) │ атомарная публикация (новое поколение включается) └─ SUCCEEDED
  • Подача отделена от исполнения: Submit никогда не блокирует вызывающий поток — перезагрузку можно подать прямо из колбэка запроса
  • Однопоточная координация: все намерения исполняются последовательно на одном управляющем потоке — нет «двух перезагрузок, топчущих друг друга»
  • Аудируемые итоги: у каждого намерения стабильный ID и семисостояний терминал; кольцо хранит последние 256 результатов

26.3 Намерения и семь состояний

Каждый Submit — это намерение. Его жизненный цикл:

ACCEPTED ──▶ PREPARING ──▶ SUCCEEDED /* опубликовано */ │ ├──▶ FAILED /* провал компиляции/проверки; старое поколение продолжает */ │ ├──▶ SUPERSEDED /* вытеснено более новым намерением того же server */ └──▶ CANCELLED /* отменено до зоны побочных эффектов */ Слияние latest-wins: новое намерение server перекрывает неисполненные старые (просроченные конфигурации не встают в очередь); pending ≤64, кольцо результатов 256

latest-wins легко недооценить: поправьте конфигурацию пять раз и подайте пять раз — исполнится только последняя, промежуточные состояния никогда не выйдут в свет. Это ровно та семантика, что нужна и «правке на ходу» в разработке, и «быстрому откату» в эксплуатации.

26.4 Неизменяемые снимки

Принимая намерение, координатор делает ключевую вещь: замораживает вход — байты конфигурации и все затронутые исходники скриптов снимаются в этот момент, и считается отпечаток содержимого (Revision). Далее:

  • в рамках одного reconcile (особенно xsReloadAllSubmit) все server видят один вход — никакого «сервис A на новой конфигурации, сервис B на полу-новой»
  • как бы файлы на диске ни менялись при исполнении, намерение не затронуто — правка файлов и подача намерения это два действия; после снимка начинается один полный транзакция
  • Revision — якорь аудита: две успешные перезагрузки с одинаковым Revision = содержимое не менялось (координатор может на этом закоротить); лог с последовательностью Revision прослеживает «какая версия выходила в свет»

26.5 Контракт

  • Подача: Submit неблокирующий, возвращает стабильный ID; 0 = не принято (например, pending полон).
  • Исполнение: один управляющий поток, последовательно; после зоны побочных эффектов (публикации) новые намерения лишь встают в очередь — идущее не отменить.
  • Вход: намерения замораживают неизменяемый снимок; Revision — отпечаток содержимого и якорь аудита.

Частые ошибки

Submit принят за синхронную команду
Симптом
Сразу за Submit идёт утверждение нового поведения; тесты падают случайно.
Причина
Submit значит лишь «намерение принято» — терминал узнаётся Query. bool-обёртки (xsReloadHost и т.п.) тоже сообщают только приём.
✗ Ошибка
xsReloadHost(pHost);
assert(NewBehavior());  /* могло ещё не опубликоваться */
✓ Правильно
XS_ReloadId id = xsReloadHostSubmit(pHost);
/* опрашивать xsReloadQuery до терминала, затем assert */
Правка файла принята за автоперезагрузку
Симптом
После правки devfile поведение сервиса не меняется.
Причина
В XServer нет встроенного наблюдателя файлов — правка файла это просто правка; снимок происходит в момент подачи намерения. Хотите «сохранил — перезагрузилось», соберите сами: наблюдение файлов xrt + автоподача (упражнение гл. 27).

Упражнения

БазовыйНаблюдение жизни намерения

Напишите управляющий маршрут: подать перезагрузку и опрашивать каждые 10 мс, печатая последовательность State.

Приёмка: успешный путь показывает ACCEPTED→(PREPARING)→SUCCEEDED; нарочно битый скрипт даёт FAILED.
ПродвинутыйДоказательство latest-wins

Быстро подайте три намерения (у каждой версии маршрута свой маркер) и убедитесь: вышла только последняя, первые два SUPERSEDED.

Приёмка: все три ID опрашиваются до терминалов; поведение равно третьей версии.
ВызовАудит-лог Revision

Добавьте в управляющий маршрут историю: по кольцу результатов вывести последние N намерений ID/State/Revision/Message.

Приёмка: сходится «какая подача сейчас работает»; повторная подача без изменений сохраняет Revision.

Итоги

ТезисВывод
Моделькоординация целевого состояния: подаются намерения, не синхронные команды
Исполнениеодин последовательный управляющий поток; Submit неблокирующий со стабильным ID
Семь состоянийACCEPTED→PREPARING→SUCCEEDED/FAILED/SUPERSEDED/CANCELLED
latest-winsстарые намерения server перекрываются; промежуточные не выходят в свет
Снимкивход замораживается при приёме; Revision — отпечаток и якорь аудита
Что дальше по xrt