Раздел 46 · Effect: Тихая Академия

Босс: линия без хозяина

middle-senior~80 мин

без входаВойди, чтобы отмечать прогресс, вести заметки и решать задачи в редакторе. войти

Шесть этапов лежат на столе по отдельности. Очередь помнит задания (урок 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) спрашивает у башни, какие письма она приняла.

Чек-лист перед сдачей

  1. Тесты всех прошлых этапов зелёные.
  2. Путь до T-3 это ['T-1', 'T-3'], путь до T-1 это ['T-1'].
  3. Одно письмо, принесённое в окошко дважды по одному ключу, это одно письмо.
  4. Насос переносит две строки, второй проход ноль.
  5. После двух заданий T-1 приняла письма 1 и 2, T-3 только письмо 1, T-4 только письмо 2.
  6. Расписка завершает путь; повторный запуск того же письма отдаёт тот же итог и ничего не шлёт заново.
  7. Новое письмо проходит насос, очередь и путь, как первые.

Линия, этап 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.