Утренняя трасса из прошлого урока лежит на столе. Книгу спросили про три башни, она ответила про две. Третью, T-5, пачка из этапа 11 по своему правилу назвала несуществующей: её не было в ответе. А на T-5 живёт Ринцвальд.
Пондер нашёл, в чём дело. У книги Магистрали есть второй ответ, GET /towers/checked, и в нём два списка: башни, до которых книга дозвонилась, и башни, до которых нет. Старый ответ второй список молча выбрасывал. Почтамт с первого дня читал только первую половину правды, а Магистраль так отвечала всем уже год.
Новых модулей Effect сегодня нет. Есть журнал из урока 98, трасса из урока 99, счётчики из урока 100, пачки и кэш из урока 101 и Почтамт из урока 97. Их надо сложить в одну книгу, которой можно верить, и в Почтамт, который не врёт горожанам.
Что нужно Почтамту
Тесты накопительные: идут все прошлые этапы, а сверху новый модуль src/honest.ts.
- Книга, которой верят только проверенное.
honestBook(baseUrl)спрашивает/towers/checkedпачкой, как этап 11. Башня в ответе: ответ. Башня в списке недозвона: бедакнига не проверила. Башни нет нигде:нет такой башни. - Срок ответа. Пачка ждёт книгу две секунды. Дольше это
книга не отвечаету каждого вопроса пачки. - Кэш только правды. Ответ живёт минуту. Беда не живёт вовсе: про ту же башню книгу спросят снова.
- Всё видно. Поверх книги журнал, трасса и счётчики: недозвон пишется в журнал как
Warn, закрывает спан провалом и прибавляет единицу к своему счётчику. - Горожанину правду. «Нет такой башни» остаётся ответом 422. Любая другая беда книги становится 503 с причиной: новая проблема
BookUnavailableнаHttpApiError.Problemиз урока 91. - Почтамт целиком.
PostOfficeHonest({ towersUrl, archiveDir })собирается какPostOfficeLive, только окошко писем проверяет адрес честно, а книга башен честная. Снаружи ему нужны те же провод, книги, файлы и пути.
Чек-лист перед сдачей
- Тесты всех прошлых этапов зелёные.
- Три письма разом: один запрос к книге.
- Повторное письмо на ту же башню: ни одного запроса.
- Недозвон: 503 с
towerиreason, письмо не принято, следующий вопрос снова идёт к книге. - Молчание книги: 503 через две секунды, а не вечная очередь.
- Недозвон в журнале, в трассе и в счётчике.
- Выдача писем работает на честной книге.
Почтамт, этап 12
Подумай до кода
- Куда ставить срок. На вопрос одной башни или на запрос пачки? В пачке вопросы разных писем. Если книга молчит, ждать две секунды должен каждый из них или все вместе один раз?
- Что кэшировать. В уроке 101 ответ жил минуту, беда не жила. Почему недозвон тем более нельзя кэшировать? Что услышат горожане минуту спустя, если книга уже дозвонилась?
- Порядок обёрток.
loggedBook,tracedBookиmeteredBookпринимают книгу и отдают книгу, их можно надеть в любом порядке. Тест ждёт журнал и счётчик у каждого вопроса, а не у каждой пачки. Где тогда должен быть кэш: снаружи обёрток или внутри? Сколько строк журнала даст вопрос, который ответил кэш? - Ошибка, которой нет в договоре. Окошко
sendв договоре знает толькоUnknownAddress.BookUnavailableтам нет, и вписать её туда нельзя: старый договор не переписывают. Но сервер отдаёт проблему с её статусом, даже если она пришла дефектом. Чем это лучше голого 500 из урока 97 и чем хуже настоящей ошибки в договоре?
Тест собирает Почтамт так, как его соберёт Гекс, только слои снаружи свои: провод к книге Гилта подменён, база это PGlite в памяти, подвал это карта строк, журнал пишет в массив, спаны ловит трассировщик в памяти.
const App = HttpApiBuilder.layer(PostOfficeApi).pipe(
Layer.provide(PostOfficeHonest({ towersUrl: 'http://book.ankmor', archiveDir: '/srv/pochtamt' })),
Layer.provide(
Layer.mergeAll(
Magistral,
PgliteClient.layer(),
MemoryFs,
Path.layer,
HttpServer.layerServices,
journalLayer((entry) => journal.push(entry)),
MemoryTracer,
),
),
);
const { handler, dispose } = HttpRouter.toWebHandler(App);
journalLayer и MemoryTracer стоят снаружи, рядом с проводом и базой. Почтамт не знает, куда пишется его журнал и кто собирает его спаны: в проде здесь встанут логгер в файл и экспорт трасс, а PostOfficeHonest не изменится.
Нестабильно. Окошки Почтамта стоят на
effect/httpиeffect/http-api:HttpApiError.Problem,HttpApiBuilderиHttpRouter.toWebHandlerв 4.1 помечены@stability unstable. Журнал, трассы, счётчики,RequestResolverиCacheстабильны. Урок написан на 4.1.0.
Сюжетный квест
Третья, последняя улика квеста «Проглоченная ошибка Гилта». Первые две лежат в боссе обработчика кадров и в боссе ретранслятора. Найдёшь все три, и на дашборде квест раскроется.
Практика
Глубже в архиве
Сроки, повторы и предохранители для зависимостей, которые отвечают медленно или не отвечают вовсе, разобраны в уроке про паттерны устойчивости раздела про бэкенд.
Итог
Через неделю трасса лежит на столе Патриция. Не одна, а все за неделю, и рядом выписка из книги Магистрали за год, которую Пондер поднял по ответу /towers/checked: каждый недозвон, который в старом ответе пропадал без следа, а в отчёте Магистрали становился зелёной строкой «башня проверена». Напротив Патриция сидит Гилт. Отчёт у него в руках тоже зелёный.
Больше Патриций ничего не говорит. Рыдкулл выходит из дворца с бумагой, на которой стоит печать Казимира: Академия получает звание лекторов. Ранг за этот заказ: лектор недавних рун.
Дальше
Утром в лавке напротив Почтамта лавочница Чонси вешает на витрину большой лист: все сорок башен города, у каждой отметка «открыта», «закрыта» или «книга не дозвонилась», и время последней проверки. Горожане останавливаются у витрины чаще, чем у окошек. Им больше не нужно верить ни Магистрали, ни Почтамту: они хотят видеть сами.
Следующий раздел курса это EF13 «Витрина»: Effect в браузере, состояние на атомах и страница, которая сама следит за башнями. Начинается он с урока 103.