Глава 13 / 51
Том 2 · Конфигурация и сборка (финал)

Валидация и откат конфигурации: полная семантика fail-fast

«Отказать в старте» — лишь половина валидации; вторая половина — «после провала не остаётся обломков». Глава собирает валидацию, откат сборки и отклонения reload в единую карту обороны.

Обзор

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

13.1 Введение

У ошибок конфигурации три плохих финала: работа «с дефектом» (опаснейший), полузапущенное состояние (трудно отлаживать) и молчаливая правка в неверное поведение (самый коварный). Дизайн XServer перекрывает все три: ошибки старта всегда завершают процесс с именем поля; сборка — транзакция (частичный успех откатывается); изменения в рантайме либо действуют полностью, либо остаются нетронутыми. Эти три фразы и есть данная глава.

13.2 Матрица fail-fast

A-матрица func_test сторожит валидацию старта 21 кейсом — все с утверждением exit=1 и ошибкой, называющей конкретное поле. Покрытие слоится по этапам:

Слой 1 · структура services не массив / синтаксические ошибки JSON Слой 2 · поля белый список class · диапазон/отсутствие port · tls×udp · диапазон recv · ошибки типов (header_limit не число и т.п.) Слой 3 · уникальность повтор имени server · повтор/пустое имя host · двойная привязка домена Слой 4 · ресурсы отсутствие корня статики · PEM/пары сертификатов (глава 12) Слой 5 · семантика зарезервированные static headers · диапазон ключей error_pages · некорректный ws_protocol

Каждый слой завершается до входа в следующий — ошибка всегда указывает на самый ранний сбой, и вам не приходится гадать, разбирав пять наложенных ошибок.

13.3 Транзакция сборки и откат слушателей

Сборка после валидации (компиляция скриптов → ServiceInit → bind) тоже транзакционна. В func_test есть специальный кейс отката:

Кейс отката слушателя (наблюдаемое поведение func_test)
конфигурация: plain 8080 + tls 8443, причём 8443 уже занят внешним процессом
[xs] server ... http bound on 0.0.0.0:8080      /* сначала успешная привязка */
bind failed (8443)
→ занятый 8080 полностью откатывается (порт снова можно bind)
→ процесс завершается, полузапущенных остатков нет

В мультисервисном сценарии так же (глава 11): неудача компиляции скрипта любого server прерывает всю сборку — «трём хорошим дать поработать» не бывает.

13.4 Список отклонений reload

У запросов на изменение в рантайме (reload) свои ворота, явно отклоняющие:

ИзменениеРезультатПричина
изменение значения engine.workersFAILEDфакт уровня процесса нельзя перестроить на ходу
изменение неизменяемых полей слушателя (например recv_limit)FAILED (зафиксировано lifecycle-тестами)изменение параметров слушателя требует пути структурной пересборки
структурное изменение (эндпоинты/класс) без handoffFAILED с подсказкой о рестартеонлайн-пересборка поддерживает только передачу слота при неизменных эндпоинтах
обновление скрипта сервиса класса customонлайн-перезагрузка не поддерживаетсяу «сырых» ресурсов приложения нет терминального lease (том 4)
провал компиляции скрипта-кандидатаFAILED, старое поколение продолжает обслуживатьсначала компиляция, потом замена — откат есть семантическая гарантия

Разрешённые горячие пути: обновление содержимого скриптов, горячая замена сертификатов (глава 12) и обновление конфигурации на тех же эндпоинтах (передача слота слушателя). Принятые намерения сливаются по latest-wins — старые завершаются как SUPERSEDED, просроченные конфигурации не выстраиваются в очередь.

13.5 Контракт

  • Старт: пять слоёв валидации по порядку, сообщение о самой ранней ошибке, exit=1, без остатка.
  • Сборка: компиляция/Init/bind — транзакция; частичный успех обязательно откатывается.
  • Рантайм: пять классов отклонений reload + latest-wins; у успеха ровно одна форма — «действует полностью».

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

reload FAILED принято за «сервер сломался»
Симптом
После подачи перезагрузки State=FAILED и срабатывает эксплуатационная тревога.
Причина
FAILED — семантика отклонения: старое поколение цело, сервис не прерывался — это значит «изменение не вступило в силу», а не «отказ сервиса». Читайте XS_ReloadResult.Message ради причины.
При быстрых последовательных правках действует только последняя
Симптом
Подряд поданы три перезагрузки — в сервисе видна только последняя.
Причина
Слияние latest-wins: старые намерения того же server вытесняются (SUPERSEDED), предел pending — 64. Это защита от «просроченных конфигураций из очереди».

Упражнения

БазовыйВоспроизвести откат

По кейсу 13.3 займите TLS-порт и запустите; убедитесь, что занятый порт открытого текста откатился (netstat после выхода подтвердит освобождение).

Приёмка: после завершения процесса порт plain немедленно доступен для bind другой программой.
ПродвинутыйОбход списка отклонений

По очереди подайте перезагрузки с изменением workers и recv_limit; зафиксируйте оба сообщения FAILED.

Приёмка: оба Message называют причину неизменяемости; сервис всё время онлайн.
ВызовШторм битых скриптов

Циклом подайте 20 перезагрузок «скрипта с ошибкой компиляции» (см. образец reload-бомбы drill), наблюдая выживание старого поколения и опрос результатов.

Приёмка: все 20 FAILED; ноль перерывов сервиса; терминальное состояние каждого намерения доступно для опроса.

Итоги

ТезисВывод
Пять слоёвструктура→поля→уникальность→ресурсы→семантика, побеждает самая ранняя ошибка
Транзакция сборкичастичный успех обязательно откатывается, полузапущенных остатков нет
Ворота reloadпять отклонений (workers / неизменяемые слушатели / структура / custom / провал компиляции)
Семантика FAILEDотклонено ≠ сломано: старое поколение цело, Message называет причину
latest-winsстарые намерения server уходят в SUPERSEDED, очереди нет
Что дальше по xrt