Ориентация главы

Второй проект — центр конфигурации (configd): централизованное управление конфигурацией группы сервисов — JSON-конфигурация поставляется файлом или по HTTP, поддерживается послойное переопределение по окружениям (трёхслойное слияние: умолчания/разработка/продакшен), валидация (типы и ограничения), опциональное раскрытие переменных шаблона ({$env} — внедрение фактов окружения) и горячая замена (перечитывание по изменению файла). Отличие формы от Проекта 1 (глава 138, одноразовый инструмент) принципиально: конфигурация — долгоживущее состояние — её загружают, кэшируют, по ней спрашивают, она может обновляться — управление жизненным циклом и конкурентный доступ впервые становятся главной темой. Эта глава — глава проектирования: требования и приёмка, выбор модели данных, слоистость и выбор модулей, семантика горячей замены и конкурентности, политика валидации и безопасность шаблона — всё фиксируется до написания реализации (глава 140). Методологическая позиция проектной главы: код — исполнение проекта, а переделки чаще всего растут из проектной нечёткости.

139.1引入

Проблема конфигурации выглядит тривиально — «прочитать JSON, чего там». В реальной системе сложность конфигурации приходит из четырёх измерений: разнообразие источников (файл / HTTP-интерфейс / переменные окружения / командная строка — должно уметь комбинироваться); послойное переопределение (умолчания перекрываются слоем окружения, слой окружения — слоем инстанса — семантика слияния должна быть явной); запаздывающая валидация (ошибочная конфигурация взрывается лишь при первом использовании — перехватывать надо уже на старте); распространение обновлений (какие компоненты перечитают изменившуюся конфигурацию, что делать с операциями, которые используют старые значения). Нечёткость в любом измерении превращается в продакшен-инцидент: перепутан порядок слияния — продакшен работает на значениях разработки; валидации нет — порт 0 «успешно стартует»; гонка горячей замены даёт двум потокам несогласованную половину конфигурации.

Эта глава превращает каждое из четырёх измерений в явное проектное решение — у каждого решения есть отвергнутые альтернативы и обоснование (методология таблицы выбора главы 138, поднятая на архитектурный уровень). Реализация следующей главы исполнит эти решения построчно — в этом приёмочность проектной главы: когда реализация закончена, сверка возвращается к каждой строке — каждое решение либо соблюдено, либо явно пересмотрено.

139.2概念

Требования и критерии приёмки

Пользовательские сценарии configd (форма user story): UC1 загрузка — при старте сервиса конфигурация вытягивается из config.json (или HTTP-эндпойнта); при отказе — явная ошибка (молчаливо работать с пустой конфигурацией не разрешается). UC2 слоистость — глубокое слияние двух слоёв default.json → config.{env}.json (вложенные объекты рекурсивно, массивы заменяются целиком — семантика замены объявлена явно); UC3 запрос — взятие значения по пути (server.port, db.pool.max) с ожиданием типа (запросили целое — получили целое, несовпадение типа — ошибка); UC4 валидация — полная проверка на этапе загрузки (обязательные существуют / типы верны / значения разумны), ошибка несёт путь в конфигурации (db.pool.max: must be > 0 — применение модели ошибок главы 4); UC5 шаблон — строковые значения поддерживают раскрытие {$ENV_HOME} (внедрение фактов окружения в конфигурацию); UC6 горячая замена — при изменении файла перезагрузка, атомарная передача старой и новой конфигурации (в любой момент запрос видит либо всё старое, либо всё новое — смеси не существует).

Критерии приёмки (в главе реализации сверяются построчно): (1) у каждого из шести UC есть позитивные и негативные случаи; (2) у семантики слияния есть тестовые векторы (вложенное переопределение / замена массива / конфликт типов); (3) тест гонки горячей замены (согласованность конкурентных запросов во время обновления — threads-форма главы 133); (4) все ошибки валидации несут путь; (5) OOM по каждой точке (загрузка конфигурации содержит выделения памяти — дисциплина главы 134); (6) ноль следов при неудачной загрузке (половинная конфигурация не просачивается наружу — атомарность).

Выбор модели данных: динамический value или специальная структура

Первое и самое важное решение — что такое конфигурация в памяти:

ВариантФормаПреимуществаНедостатки
A: динамический value (глава 31, xvalue)весь JSON загружается в дерево valueсовместим с любой схемой (эволюция конфигурации — ноль правок кода); запросы идут общими путями; JSON-десериализация за один шагмедленные запросы (поиск по строковому пути); нет проверки на этапе компиляции
B: специальная структураstruct config { int Port; struct db Db; }самый быстрый доступ, типобезопасностьизменение схемы требует править структуру и код загрузки; слияние многих источников пишется вручную
C: загрузка в value + снимок в структурузагрузка/слияние/валидация в домене value, после загрузки — разовое извлечение в структуруоба мира: гибкость на этапе загрузки + скорость на этапе использованиякод извлечения требует сопровождения (зеркало схемы)

Решение: C. Обоснование: у конфигурации разные частота эволюции (схема меняется часто — в домене value это бесплатно) и частота чтения (горячий путь — возможен запрос на каждый запрос — структура даёт O(1)) — сопоставление по этапам. B отвергнуто: слияние и шаблон в домене структур писать вручную слишком тяжело; A отвергнуто: строковый поиск запросов попадает в горячий путь. Зеркальная цена C держится под контролем макросов извлечения (одна строка объявления на поле — реализация в главе 139).

Слоистость и выбор модулей

1Слой источниковфайл (чтение xfile, глава 45) / HTTP (клиент xhttp, главы 98-100)
2Слой разбораJSON (глава 32) -> дерево xvalue; XLON (глава 33) зарезервирован для расширения
3Слой слиянияглубокое слияние двух деревьев value (объекты рекурсивно / массивы заменой — семантика явная)
4Слой шаблонараскрытие переменных окружения в строковых значениях (строки главы 25 + окружение главы 43)
5Слой валидациинабор правил (обязательные/типы/диапазоны) -> ошибки с путём (глава 4)
6Слой снимкаvalue -> структура, разовое извлечение (точка атомарной передачи)
7Слой запросовпрямой доступ к структуре (O(1))

Каждый из семи слоёв — главная тема какой-то предыдущей главы — выбор модулей целиком из существующих глав (без новых зависимостей): очередное подтверждение тезиса «проект = рекомбинация существующих слоёв». Контракты между слоями: поток байтов → дерево value → слитое дерево value → прошедшее валидацию дерево value → снимок в структуре — каждая стрелка — чистое преобразование данных (без побочных эффектов, отказ атомарен).

Горячая замена и конкурентность: семантика атомарной передачи

Ядро проектирования горячей замены — протокол передачи старой и новой конфигурации. Выбор configd: неизменяемый снимок + атомарная замена указателя. Продукт загрузки — неизменяемая структура (после загрузки её никто не меняет — потокобезопасность даром, конфигурационная версия неизменяемого разделения главы 115); сторона запросов держит указатель с подсчётом ссылок (общая ссылка главы 5 — Ref взял одну, после использования Destroy); обновление = построить новый снимок → атомарно заменить глобальный указатель (атомарная операция главы 9) → старый снимок освобождается сам, когда счёт ссылок доходит до нуля. Гарантия семантики: в любой момент любой спрашивающий видит целиком согласованную конфигурацию (либо старую, либо новую — смеси не бывает); спрашивающий внутри долгой операции продолжает пользоваться старым снимком до её конца (graceful-передача — без принудительного выдёргивания). Отвергнутые альтернативы: глобальная блокировка чтения-записи (блокировка входит в горячий путь запросов — отвергнуто); обновление изменяемой структуры на месте (конкурентный ад — отвергнуто).

Слои валидации: семантическая правильность за пределами синтаксической законности

JSON-парсер гарантирует синтаксис (глава 32); слой валидации отвечает за семантику: существование (обязательные ключи — нет ключа, есть ошибка); тип (значение ожидаемого типа — server.port обязан быть целым); диапазон (port 1..65535, pool.max > 0 — бизнес-ограничения). Все три выполняются один раз на этапе загрузки (перехват на старте — «работать с ошибочной конфигурацией» не разрешается); формат ошибки 路径: 消息 (путь: сообщение) (db.pool.max: must be > 0) — ошибки с путём избавляют разборщика от листания всего JSON. Выражение правил валидации: небольшой декларативный массив (путь ключа + тип + обратный вызов ограничения) — правила как данные: новое поле — одна строка правила (та же экономика сопровождения, что у макросов извлечения).

Момент раскрытия шаблона и граница безопасности внедрения

Раскрытие {$VAR} происходит после слияния, до валидации — обоснование: продукт шаблона может влиять на валидацию (валидировать надо порт уже после раскрытия); а слияние происходит в исходном домене ({$BASE} слоя умолчаний не раскрывается до перекрытия слоем окружения — переопределение приоритетнее внедрения). Границы безопасности: раскрывать только переменные окружения (источник из белого списка — глава 43); не раскрывать рекурсивно (если в значении {$A} снова {$B} — не раскрывается; защита от цепочки внедрения); неопределённая переменная — ошибка (не молчаливая пустая строка — конфигурация ссылается на несуществующее окружение, это ошибка развёртывания). Эти три правила — контракт безопасности слоя шаблона, конфигурационная версия дисциплины паролей главы 77: плоскость внедрения защищается на каждой границе «строка становится конфигурацией».

139.3示例

Первая полная программа: минимальный контур загрузки конфигурации

Программа ниже из examples/data/json — загрузка JSON в value и запросы (учебниковая форма слоя разбора configd):

examples/data/json/main.c源码 ↗





































 */

#include <stdio.h>

#include <xrt.h>







 */
static xjsonvisitaction printJsonEvent(
	const xjsonevent* pEvent,
	ptr pUserData
)
{
	(void)pUserData;
	if ( pEvent->HasName ) {
		printf("member: %.*s\n", (int)pEvent->Name.Size, pEvent->Name.Data);
	}
	return XJSON_VISIT_NEXT;
}



int main(void)
{
	xjsonreadconfig ReadConfig;
	xjsonwriteconfig WriteConfig;
	xjsonwriter* pWriter;
	xvalue* pRoot;
	xvalue* pName;
	xstrview Name;
	str sText;
	size_t iSize;

	/* ---- 1) DOM:解析 → 随机访问 → 美化序列化 ---- */
	pRoot = xrtJsonParse(XRT_STR_LITERAL(
		"{\"name\":\"xrt\",\"features\":[\"json\",\"http\"]}"
	));
	if ( pRoot == NULL ) {
		return 1;
	}

	/* ObjectGet 返回借用指针(树拥有它);GetString 取出视图。 */
	pName = xrtValueObjectGet(pRoot, XRT_STR_LITERAL("name"));
	if ( !xrtValueGetString(pName, &Name) ) {
		xrtValueRelease(pRoot);
		return 2;
	}
	printf("name = %.*s\n", (int)Name.Size, Name.Data);

	/* Stringify 产物是拥有式字符串(xrtFree);之后树即可释放。 */
	sText = xrtJsonStringify(pRoot, true, &iSize);
	xrtValueRelease(pRoot);
	if ( sText == NULL ) {
		return 3;
	}
	printf("%.*s\n", (int)iSize, sText);
	xrtFree(sText);

	/* ---- 2) SAX:同一份输入按事件流走一遍,不建树 ---- */
	xrtJsonReadConfigInit(&ReadConfig);
	if (
		xrtJsonVisit(
			XRT_STR_LITERAL("{\"code\":200,\"ok\":true}"),
			&ReadConfig,
			printJsonEvent,
			NULL
		) != XJSON_VISIT_DONE
	) {
		return 4;
	}

	/* ---- 3) Writer:增量构建 {"code":200,"message":"OK"} ---- */
	xrtJsonWriteConfigInit(&WriteConfig);
	pWriter = xrtJsonWriterCreate(&WriteConfig);
	if (
		(pWriter == NULL) ||
		!xrtJsonWriterObject(pWriter) ||                     /* 开对象 */
		!xrtJsonWriterName(pWriter, XRT_STR_LITERAL("code")) ||
		!xrtJsonWriterInt(pWriter, 200) ||
		!xrtJsonWriterName(pWriter, XRT_STR_LITERAL("message")) ||
		!xrtJsonWriterString(pWriter, XRT_STR_LITERAL("OK")) ||
		!xrtJsonWriterEnd(pWriter) ||                       /* 闭对象 */
		!xrtJsonWriterFinish(pWriter)                       /* 完整性校验 */
	) {
		xrtJsonWriterFree(pWriter);
		return 5;
	}

	/* Take 移交文本(xrtFree 释放);Take 后 writer 仍需 Free。 */
	sText = xrtJsonWriterTake(pWriter, &iSize);
	xrtJsonWriterFree(pWriter);
	if ( sText == NULL ) {
		return 6;
	}
	printf("%.*s\n", (int)iSize, sText);
	xrtFree(sText);
	return 0;
}
$ gcc -O1 -DXRT_MODULE_ALL -I single impl.c examples/data/json/main.c -lws2_32 -liphlpapiname = xrt{ "name": "xrt", "features": [ "json", "http" ]}member: codemember: ok{"code":200,"message":"OK"}

Что только что произошло. (1) JSON-текст → дерево value → запрос поля → сериализационный круговорот — полный поток данных слоя разбора проходит в десятке строк. (2) Представление value, возвращаемое запросом, — ровно вход слоя валидации configd (проверка типов делается по нему). (3) Этот замкнутый контур — стартовая точка реализации главы 140: снаружи надеть «слой источников (файл/HTTP) → слой слияния → слой шаблона» — и получится полный конвейер загрузки — у каждого слоя, выбранного в проектной главе, есть такой исполняемый атом (архитектурная версия «образец как шлюз» главы 138: проектирование не бумажное занятие — у каждого выбора есть проверенная деталь).

Вторая полная программа: загрузка шаблонного файла

Вторая программа из examples/template/file — комбинация шаблона и файла (материальная опора слоя шаблона):

examples/template/file/main.c源码 ↗

















 */

#include <stdio.h>

#include <xrt.h>



int main(void)
{
	xtemplate* pTemplate = NULL;
	xvalue* pData = NULL;
	str sOutput = NULL;
	int iResult = 1;

	pTemplate = xrtTemplateCompileFile("examples/template/file/page.tpl");
	pData = xrtValueObject();
	if ( (pTemplate == NULL) || (pData == NULL) ||
		 !xrtValueObjectSetNew(
			pData,
			XRT_STR_LITERAL("name"),
			xrtValueString(XRT_STR_LITERAL("Alice"))
		 ) ) {
		goto cleanup;
	}

	sOutput = xrtTemplateRender(pTemplate, pData, NULL);
	if ( sOutput == NULL ) {
		iResult = 2;
		goto cleanup;
	}
	printf("%s\n", sOutput);
	iResult = 0;

cleanup:
	xrtFree(sOutput);
	if ( pData != NULL ) {
		xrtValueRelease(pData);
	}
	if ( pTemplate != NULL ) {
		xrtTemplateRelease(pTemplate);
	}
	return iResult;
}
$ gcc -O1 -DXRT_MODULE_ALL -I single impl.c examples/template/file/main.c -lws2_32 -liphlpapiHello Alice

Что только что произошло. (1) CompileFile = чтение файла + компиляция за один шаг — комбинация слоя источников и слоя шаблона уже существует (глава 35): раскрытие {$VAR} в configd может переиспользовать шаблонизатор (семантика внедрения переменных та же) или упроститься до специализированной замены строк (меньше) — обе альтернативы на столе, глава реализации выберет по бюджету размера. (2) Контекст переменных шаблона приходит из дерева value (внедрение контекста главы 35) — в комбинации с переменными окружения (глава 43) это полная форма «конфигурация ссылается на окружение». (3) Проектный вывод: выбор слоя раскрытия не блокирует остальные слои — выигрыш слоистости снова налицо: смена реализации раскрытия — замена одного слоя.

Форма проектного документа: эта глава как шаблон

В какой форме писать проектную главу? Не большая UML-диаграмма, а список решений — у каждого решения пять элементов: проблема (что решаем), варианты (минимум две альтернативы), решение (что выбрали), обоснование (почему победило), проверка (как узнать, что решение исполнено). Демонстрация этой главы: модель данных (выбор из трёх, обоснование — сопоставление частот по этапам, проверка — число строк макросов извлечения); протокол передачи (выбор из трёх, обоснование — структура уничтожает состояние гонки, проверка — threads-тест без смешанных состояний); семантика слияния (явное объявление, проверка — тестовые векторы); безопасность шаблона (три границы, проверка — случаи внедрения). Почему не UML: у диаграмм большое пространство двусмысленностей (из двух диаграмм каждый читает своё), а у списка решений каждая строка сверяема — та же философия, что у шлюзов BOOK_SPEC (машинная проверяемость важнее человеческой интерпретации). Этот проект Проекта 2 будет построчно цитироваться в начале главы реализации (глава 140) — читатель увидит, что отображение «решение → код» и есть настоящая поставка этой главы: не документ, а сверяемый набор ограничений. Проектная глава чат-сервиса (глава 141) наследует тот же шаблон — переиспользование проектной методологии опережает переиспользование кода.

От инструмента к сервису: повторный проход уроков Проекта 1

Семь уроков ретроспективы главы 138 проходятся заново в контексте configd — форма другая, уроки те же: приёмка прежде (шесть UC и шесть критериев против пяти — приёмка сервиса обязана покрывать конкурентность и жизненный цикл, инструмента — нет); форма решает контейнер (дерево value против массива счётчиков — «эволюционность» данных становится новым измерением: форма данных инструмента фиксирована, у сервиса может эволюционировать); представления живут одним кадром (слой валидации работает по представлению value, извлечение — только в слое снимка — без раннего закрепления); слоистая заменяемость (слой источников файл/HTTP заменяем против семейства Reader — та же дисциплина, поднятая до семи слоёв); факты платформы входят в проект (факты платформы сервиса — модель конкурентности и соглашения о расположении файлов конфигурации); бюджет прежде (загрузка один раз + горячий путь запросов — бюджет указывает необходимость слоя снимка); тестирование в четырёх квадрантах (сервис добавляет квадранты threads и гонки горячей замены). Все семь пройдены заново, два усилены — таков учебный замысел последовательности проектов: не разные проекты, а углубление одной методологии в разных ограничениях.

139.4契约

  • Приёмка прежде: шесть пользовательских сценариев и шесть критериев приёмки — глава реализации сверяет построчно; без сверки нет завершения.
  • Решение о модели: домен загрузки в value + домен снимка в структуре — сопоставление по этапам частоты эволюции и частоты чтения.
  • Семантика слияния: объекты сливаются рекурсивно, массивы заменяются целиком — семантика объявлена явно и закреплена тестовыми векторами.
  • Атомарная передача: неизменяемый снимок + подсчёт ссылок + атомарная замена указателя — целиком согласованно в любой момент.
  • Graceful-передача: долгая операция держит старый снимок до конца — без принудительного выдёргивания.
  • Три слоя валидации: существование / тип / диапазон — одна полная проверка на этапе загрузки, перехват на старте.
  • Ошибки с путём: db.pool.max: must be > 0 — ошибка локализована путём в конфигурации.
  • Безопасность шаблона: только переменные окружения, без рекурсии, неопределённая — ошибка; плоскость внедрения защищена.
  • Момент раскрытия: после слияния, до валидации — переопределение приоритетнее внедрения, валидация смотрит на финальные значения.
  • Детали проверены: у выбора каждого слоя есть исполняемый атом (два образца этой главы) — проектирование не бумажное занятие.

Дополнительный проект HTTP-источника (сетевая расширенная форма UC1)

Помимо файлового источника, UC1 допускает HTTP-эндпойнт — три пункта дополнительного проекта: сборка клиента (easy-слой главы 98 или конструктор главы 99: вытягивание конфигурации — редкая операция, easy достаточно; верификатор обязателен — «не проверять — не вариант» главы 85 в конфигурационной сцене ещё строже: захваченный источник конфигурации равен захваченным всем нисходящим потребителям); семантика отказа (неудача вытягивания — ошибка по UC1 — но можно спроектировать «при отказе жить с прошлым снимком + тревога» — стратегия деградации есть решение развёртывания: первый старт (нет старого снимка) обязан закончиться отказом, отказ обновления на ходу терпим со старой конфигурацией — два момента, две стратегии); граница кэша (семантика кэширования HTTP-ответа — режим only-if-cached автокэша главы 101 — ровно «в оффлайне работать с кэшированной конфигурацией» — точное отображение четырёх режимов на конфигурационную сцену). HTTP-источник поднимает configd с «локального файлового инструмента» до «сетевого клиента конфигурации» — но в семислойном конвейере меняется только слой источников (третье исполнение дисциплины слоистой замены главы 138).

Типичные ошибки

Ловушка 1: двусмысленная семантика слияния (массивы и глубокие объекты):Ловушка 1: двусмысленная семантика слияния (массивы и глубокие объекты)

Симптом: слой окружения хотел «переопределить только db.pool.max», а заменил весь объект pool (min вернулся к умолчанию — число продакшен-соединений резко меняется); или наоборот, хотели заменить весь массив, а он слился поэлементно со слоем умолчаний.

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

错误写法
/* поведение слияния не вписано в контракт: слияние массивов — как вздумается реализации */
merge(a, b);   /* массивы поэлементно? целиком заменить? — никто не знает */
正确写法
/* контракт прямо: объекты рекурсивно, массивы заменой + закреплено тройкой тестовых векторов */
merge_objects_recursive(); replace_arrays_whole();
Ловушка 2: валидация в «первом использовании» вместо этапа загрузки:Ловушка 2: валидация в «первом использовании» вместо этапа загрузки

Симптом: ошибочная конфигурация успешно стартует — взрывается в три часа ночи при первом обращении к той функции (худшее время отказа).

Причина: ленивая валидация откладывает отказ до момента, когда разбор труднее всего. Полная проверка на этапе загрузки = перехват на старте — отказ вскрывается ещё при развёртывании (основа критерия (3)).

错误写法
int get_port(void) {
	return cfg_get_int("server.port");  /* взрыв при первом использовании: три часа ночи */
}
正确写法
/* полная валидация в конце загрузки: существование/тип/диапазон — отказ старта с путём */
if ( !cfg_validate(&tree, rules) ) { fail_with_path(); }
Ловушка 3: горячая замена правкой изменяемой структуры на месте:Ловушка 3: горячая замена правкой изменяемой структуры на месте

Симптом: в момент обновления конкурентные читатели видят наполовину новую конфигурацию — server.port уже новый, а server.tls ещё старый (новый порт со старым TLS — рукопожатие падает).

Причина: изменяемая структура + запись на месте без синхронизации = состояние гонки. Протокол атомарной передачи (неизменяемый снимок + замена указателя) уничтожает «смешанное состояние» структурно — не дисциплиной блокировок, а невозможностью.

错误写法
struct config g_cfg;            /* глобально изменяемая */
void hot_update(struct config* n) {
	g_cfg.port = n->port;        /* окно наполовину нового: конкурентный читатель видит смесь */
	g_cfg.tls = n->tls;
}
正确写法
cfg_snapshot* fresh = build_snapshot(&tree);  /* новый снимок (неизменяемый) */
atomic_swap(&g_current, fresh);               /* атомарная замена указателя */
/* читатель: Ref одну -> читать -> Destroy — целиком согласованно */
Сопоставление с реальными системами конфигурации:Сопоставление с реальными системами конфигурации

Сопоставление проекта configd с индустриальными формами проясняет границы компромиссов. etcd/Consul: распределённое KV-хранилище + watch-пуш — у односнимочной модели configd нет watch и протокола согласованности (это главная тема распределённых систем — за пределами книги, но интерфейс зарезервирован: атомарная замена указателя снимка и есть упрощённая форма «локального watch»). Spring profiles: активация и комбинация многих профилей — гибче и сложнее фиксированного двухслойного слияния configd; двухслойное переопределение закрывает 90% сценариев (умолчания + окружение), больше слоёв — делу расширения. Производные переменных окружения (12-factor): вся конфигурация внедряется из окружения — слой шаблона configd ровно мост к файловой конфигурации (гибрид: скелет файла + внедрение окружения — практический компромисс двух мировоззрений). Вывод сопоставления: configd — «минимальная полнота, которой достаточно» — каждый сложный признак реальной системы отображается в какую-то точку расширения этого проекта (слой источников / число слоёв слияния / глубина шаблона), и это ещё одна приёмка полноты проекта.

Практические задания

Базовое: вывод векторов слияния вручную

Напишите три группы тестовых векторов слияния (слой умолчаний + слой окружения → ожидание): частичное переопределение вложенных объектов, замена массива целиком, конфликт типов (умолчание int, окружение даёт string — ожидается ошибка с путём). Критерий приёмки: вручную выведенный результат становится файлом ожиданий тестов главы реализации.

Продвинутое: диаграмма последовательности протокола передачи

Нарисуйте последовательность горячей замены: поток загрузки (построить новый снимок → атомарная замена) и два потока запросов (Ref → чтение → Destroy) в трёх переплетениях — что каждый видит до / во время / после замены. Критерий приёмки: видимость каждого случая выводится из «неизменяемость + атомарный указатель» — смешанных состояний нет.

Вызов: проектирование набора правил

Для правдоподобной конфигурации (три секции server/db/log) напишите полный набор правил валидации (путь + тип + ограничение диапазона) и три намеренно нарушающих входа. Критерии приёмки: нарушающие входы все перехвачены на старте, ошибки содержат путь; число строк правил того же порядка, что число полей конфигурации (проверка экономики сопровождения).

Итоги

ТемаКратко
Позиция проектной главырешения прежде кода — у каждого альтернативы и обоснование
Модельдомен загрузки в value (эволюция) + домен снимка в структуре (чтение) — сопоставление по этапам
Семислойный конвейеристочники → разбор → слияние → шаблон → валидация → снимок → запросы — чистые преобразования данных
Семантика слиянияобъекты рекурсивно / массивы заменой — явное объявление + тестовые векторы
Протокол передачинеизменяемый снимок + подсчёт ссылок + атомарная замена указателя — целиком согласованно в любой момент, graceful-передача долгих операций
Три слоя валидациисуществование / тип / диапазон — один раз на этапе загрузки, перехват на старте
Ошибки с путёмпуть + сообщение — разбор без листания всего файла
Безопасность шаблонатолько переменные окружения / без рекурсии / неопределённая — ошибка
Момент раскрытияпосле слияния до валидации — переопределение приоритетнее внедрения, валидация по финальным значениям
Детали провереныу выбора каждого слоя исполняемый атом — проект приземлён на факты
HTTP-дополнениеeasy-слой + верификатор обязателен; первый старт обязан падать, отказ обновления — деградация

📖 Справочники API в этой главе:value (159 записей)template (51 записей)core (25 записей)

Полные сигнатуры, ограничения параметров, возвращаемые значения и ошибки