Шесть этапов лежат на столе по отдельности. Очередь помнит задания (урок 106). Башня-сущность принимает письма одно за другим и не считает дубли (уроки 107 и 108). Квитанция приходит только после обработки (урок 109). Ключ и outbox не дают окошку ни потерять письмо, ни записать его дважды (урок 110). Путь письма помнит пройденные башни и ждёт расписки (урок 111).
Магистраль Гилта держалась на конторе: каждое письмо спрашивало у неё, куда идти. Контора погасла, и встало всё. У линии такого места нет. Задание лежит в хранилище, путь в журнале движка, башня там, куда её поставили шарды. Сломается любой воркер, и задание возьмёт другой. Сегодня это надо собрать в одну линию.
Что нужно линии
Тесты накопительные: идут все прошлые этапы, а сверху новый модуль src/line.ts.
- Откуда и куда. Все письма выходят из
T-1. Путь письма до адресата это['T-1', адресат], письмо на самуT-1проходит одну башню. - Перегон через башню-сущность. Сервис
Hopsиз этапа 6 теперь настоящий: перегон этоDeliverсущностиTowerс кодом башни. Где эта сущность живёт, решают шарды. Беда клиента сущности становитсяHopFailed, и путь повторит перегон сам. - Линия целиком. Пути писем и башни в одном слое
LineLive. Снаружи ему нужны только движок workflow и шардинг: на Node это будет кластер на сокетах и SQL, в тесте всё в памяти. - Насос. Outbox окошка переносится в очередь заданий. Второй проход по тому же outbox ничего не переносит: строки уже отмечены.
- Воркер. Берёт задание и запускает путь письма, но не ждёт расписки. Путь живёт в движке, воркер свободен для следующего задания.
- Сколько писем у башни.
received(tower)спрашивает у башни, какие письма она приняла.
Чек-лист перед сдачей
- Тесты всех прошлых этапов зелёные.
- Путь до
T-3это['T-1', 'T-3'], путь доT-1это['T-1']. - Одно письмо, принесённое в окошко дважды по одному ключу, это одно письмо.
- Насос переносит две строки, второй проход ноль.
- После двух заданий
T-1приняла письма 1 и 2,T-3только письмо 1,T-4только письмо 2. - Расписка завершает путь; повторный запуск того же письма отдаёт тот же итог и ничего не шлёт заново.
- Новое письмо проходит насос, очередь и путь, как первые.
Линия, этап 7
Подумай, пока собираешь
- Почему воркер не ждёт.
CarryLetter.executeбезdiscardвернётся, только когда адресат распишется, а это может быть через неделю. Что станет с очередью, если каждый воркер неделю держит одно задание? Что изменится для задания, если воркер упадёт сразу после запуска пути сdiscard: true? - Где гасятся дубли. Письмо может прийти в линию дважды тремя путями: горожанин нажал дважды, насос перенёс строку и упал до отметки, перегон повторили после потерянного ответа. Найди для каждого случая этап, который его гасит: ключ окошка, id задания,
idempotencyKeyпути или башня, которая не считает дубль. - Чего здесь нет. Тест идёт в одном процессе:
TestRunner.layerэто один раннер, шарды никуда не переезжают. Что из шести этапов останется верным, когда раннеров станет три и один из них погаснет посреди перегона?
Тест собирает линию так, как её соберёт Гекс, только внешние слои свои:
const Line = Layer.mergeAll(
LineLive.pipe(Layer.provideMerge(Layer.mergeAll(TestRunner.layer, WorkflowEngine.layerMemory))),
Layer.effectDiscard(migrate).pipe(Layer.provideMerge(PgliteClient.layer({}))),
PersistedQueue.layer.pipe(Layer.provide(PersistedQueue.layerStoreMemory)),
);
Кластер, движок, база и очередь это четыре внешних слоя. В проде их заменят кластер на сокетах с хранилищем сообщений в SQL, движок workflow на том же кластере, PostgreSQL и очередь в SQL. LineLive не изменится. Слой строится без тестовых сервисов: шардинг ждёт по настоящим часам и под TestClock шардов не дождётся.
Нестабильно. Линия стоит на
effect/cluster,effect/workflowиPersistedQueueизeffect/persistence: в 4.1 всё это помечено@stability unstable, имена и опции могут поменяться в минорной версии. Урок написан на 4.1.0.
Практика
Глубже в архиве
Что можно и чего нельзя обещать системе из многих машин, когда сеть рвётся, разобрано в уроке про CAP и согласованность раздела про бэкенд.
Итог
В шесть утра Патриций отключает Магистраль. Ставни на башнях Гилта закрываются одна за другой, и над городом становится непривычно тихо. Через десять минут в окошко Почтамта приносят первое письмо, через час их сотни. Письма идут по линии Академии: ночуют где положено, приходят по одному разу, а расписки возвращаются в журнал движка. Ни одна башня линии не главная, и ни одну нельзя погасить так, чтобы встало всё.
Ранг за этот заказ: хранитель линии.
Дальше
К полудню Патриций выходит на площадь перед Почтамтом. Он долго смотрит на витрину Чонси, где у всех сорока башен стоят свежие отметки.
Вечером Пондер зовёт тебя в подвал Академии. Там, среди ящиков с перфокартами Гекса, стоит голем Дорфл, глиняный и терпеливый, со свитком в голове. Горожане уже пишут ему вопросы, и он отвечает словами, длинно и вежливо. Только никто, даже Пондер, не может заранее сказать, что он ответит.
Следующий раздел курса это EF15 «Голем»: языковая модель как сервис, ответ по схеме и голем, которого можно проверить. Начинается он с урока 113.