Что есть сейчас
ГенераторOpenApi.СформироватьСпецификацию собирает документ целиком на каждый GET /v3/api-docs:
Функция СформироватьСпецификацию(Запрос = Неопределено) Экспорт
Сборка = Новый СборкаСпецификации();
...
Спецификация.Вставить("paths", СобратьПути(Сборка, ЕстьЗащищенныеМаршруты));
Обход всех маршрутов, транслитерация имён, построение схем по рефлексии типов, сериализация. Кэша нет ни на каком уровне.
Почему это можно кэшировать
От запроса зависит ровно один раздел — servers, он выводится из заголовков X-Forwarded-*. Всё остальное — info, paths, tags, components — на одних и тех же маршрутах даёт побайтово одинаковый результат: это уже проверено тестом на двенадцать одновременных запросов.
Значит документ собирается один раз, а servers подставляется поверх на каждый запрос.
Инвалидация
Маршруты после старта приложения не меняются: все три места регистрации — СборщикМаршрутов, РегистраторМаршрутовOpenApi, РегистраторКаталогаФайлов — отрабатывают рогатками при ЗапуститьПриложение, до того как поднимется слушатель.
Если считать регистрацию в рантайме возможной, кэш сбрасывать в Маршрутизатор.ДобавитьМаршрут.
Чем хранить кэш
АтомарнаяСсылка из sfaqer/atomic — 0.1.1, классы АтомарноеЧисло, АтомарноеБулево, АтомарнаяСсылка.
Обычная переменная желудя тут не годится: желудь один на приложение, а запросы за спецификацией приходят одновременно. На первом запросе двое собрали бы документ и записали каждый свой — ничего не сломается, но работа делается дважды. Заводить ради этого семафор тяжелее самой задачи.
Атомарная ссылка закрывает и то, и другое: чтение свободно от блокировок, а первую сборку укладывает СравнитьИУстановить — кто успел, тот и записал, остальные берут записанное.
// АтомарнаяСсылка - собранный документ без раздела servers.
// Неопределено - ещё не собран.
Перем КэшДокумента;
// В СформироватьСпецификацию
Документ = КэшДокумента.Получить();
Если Документ = Неопределено Тогда
КэшДокумента.СравнитьИУстановить(Неопределено, СобратьДокумент());
Документ = КэшДокумента.Получить();
КонецЕсли;
Раздел servers подставляется поверх копии на каждый запрос: сам кэш остаётся неизменяемым, иначе параллельные запросы затирали бы друг другу адрес.
Сброс, если решим считать регистрацию в рантайме возможной, — Установить(Неопределено) в Маршрутизатор.ДобавитьМаршрут, тоже без семафора.
Зависимость новая: atomic в packagedef пока нет.
Что это заодно снимает
Вопрос о стоимости обходов в генераторе. СодержитПараметр и ИмеетТелоЗапроса проходят Действие.Параметры циклом с ВРег — около восьми раз на точку маршрута. Сейчас это повторяется на каждый запрос за спецификацией, при кэшировании отработает один раз за жизнь приложения, и оптимизировать там будет нечего.
Откуда взялось
Предлагал CodeRabbit нитпиком в #124. Я отклонил с обоснованием «документ зависит от запроса» — формально верно, по существу нет: зависит только servers.
🤖 Generated with Claude Code
Что есть сейчас
ГенераторOpenApi.СформироватьСпецификациюсобирает документ целиком на каждыйGET /v3/api-docs:Обход всех маршрутов, транслитерация имён, построение схем по рефлексии типов, сериализация. Кэша нет ни на каком уровне.
Почему это можно кэшировать
От запроса зависит ровно один раздел —
servers, он выводится из заголовковX-Forwarded-*. Всё остальное —info,paths,tags,components— на одних и тех же маршрутах даёт побайтово одинаковый результат: это уже проверено тестом на двенадцать одновременных запросов.Значит документ собирается один раз, а
serversподставляется поверх на каждый запрос.Инвалидация
Маршруты после старта приложения не меняются: все три места регистрации —
СборщикМаршрутов,РегистраторМаршрутовOpenApi,РегистраторКаталогаФайлов— отрабатывают рогатками приЗапуститьПриложение, до того как поднимется слушатель.Если считать регистрацию в рантайме возможной, кэш сбрасывать в
Маршрутизатор.ДобавитьМаршрут.Чем хранить кэш
АтомарнаяСсылкаиз sfaqer/atomic — 0.1.1, классыАтомарноеЧисло,АтомарноеБулево,АтомарнаяСсылка.Обычная переменная желудя тут не годится: желудь один на приложение, а запросы за спецификацией приходят одновременно. На первом запросе двое собрали бы документ и записали каждый свой — ничего не сломается, но работа делается дважды. Заводить ради этого семафор тяжелее самой задачи.
Атомарная ссылка закрывает и то, и другое: чтение свободно от блокировок, а первую сборку укладывает
СравнитьИУстановить— кто успел, тот и записал, остальные берут записанное.Раздел
serversподставляется поверх копии на каждый запрос: сам кэш остаётся неизменяемым, иначе параллельные запросы затирали бы друг другу адрес.Сброс, если решим считать регистрацию в рантайме возможной, —
Установить(Неопределено)вМаршрутизатор.ДобавитьМаршрут, тоже без семафора.Зависимость новая:
atomicвpackagedefпока нет.Что это заодно снимает
Вопрос о стоимости обходов в генераторе.
СодержитПараметриИмеетТелоЗапросапроходятДействие.Параметрыциклом сВРег— около восьми раз на точку маршрута. Сейчас это повторяется на каждый запрос за спецификацией, при кэшировании отработает один раз за жизнь приложения, и оптимизировать там будет нечего.Откуда взялось
Предлагал CodeRabbit нитпиком в #124. Я отклонил с обоснованием «документ зависит от запроса» — формально верно, по существу нет: зависит только
servers.🤖 Generated with Claude Code