Lambda Architecture¶
Раздел 01 · Уровни и общие гарантии
Сквозной пример банковского антифрода показывает сборку признаков из исторического базиса и оперативного хвоста.
Тип материала: архитектурный разбор. Схемы и примеры иллюстративные, если не указано иное.
Уровень: организация аналитической обработки. Проверка источников: 2026-11-02.
1. Назначение и решаемая проблема¶
Lambda совмещает быстрый предварительный ответ с пересчётом по полной истории. Проблема возникает, когда оперативная витрина должна обновляться до завершения тяжёлой обработки, а окончательные результаты должны учитывать опоздавшие события и исправления. Пример — предварительная сумма оборота против закрываемой отчётности.
Это не AWS Lambda. Паттерн не делает результаты правильными автоматически: нужны полная исходная история, согласованные правила и точная граница между результатами двух путей.
2. Принцип работы¶
Одно логическое множество событий поступает в сохраняемую историю и speed path. Batch layer периодически вычисляет новую версию исторического представления. Speed layer обслуживает ещё не покрытый этой версией хвост. Serving layer выбирает согласованную комбинацию.
Удобно определять покрытие batch вектором source offsets и revision набора, а не только wall clock. Событие с ранним event time может поступить поздно и находиться именно в хвосте. Общая бизнес-логика и тестовые fixtures уменьшают расхождения, но не устраняют разные execution semantics.
3. Архитектурная схема¶
flowchart TB
S["Источники событий"] --> L["Durable ingestion log"]
L --> H["Raw history"]
L --> P["Speed processing"]
H --> B["Batch recomputation"]
B --> V["Versioned batch view и coverage"]
P --> T["Tail view"]
V --> Q["Serving: непересекающееся покрытие"]
T --> Q
Q --> U["API и аналитика"]
4. Walkthrough движения данных¶
- Платёжное событие получает стабильный event_id и source position; оба пути получают его из одного durable ingestion.
- Архив сохраняет исходные данные, схему и revision. Speed processor дедуплицирует и обновляет предварительный показатель.
- Batch строит поколение B2 из явно зафиксированного input snapshot до coverage C. Late event включается по правилам выбранного поколения.
- Контроль сверяет суммы и полноту; serving атомарно переключает указатель с B1 на B2.
- Из speed view выбирается только вклад входных событий вне coverage C. Старый хвост удаляется после безопасного переключения.
Простое сложение готовых агрегатов допустимо не для всех функций. Для distinct count или состояния «последняя версия ставки» нужны mergeable state, event-level reconciliation либо полная замена диапазона. Иначе overlap создаст двойной счёт.
5. Технологии реализации¶
Возможная реализация: Kafka для входа, S3/MinIO с Parquet для истории, Spark для batch, Flink для speed, ClickHouse или отдельный serving service для представлений. Это варианты компонентов, не готовая совместимая сборка.
Нужен metadata store с версиями вычисления и coverage, а также scheduler и механизм публикации поколения. Отдельный deployment batch/speed разумен при существенно разных ресурсных профилях.
6. Консистентность и гарантии обработки¶
Каждый путь может иметь собственные transaction/checkpoint guarantees, но между ними нет общей транзакции по факту наличия двух движков. Видимость batch generation должна быть атомарной для запроса; полнота данных зависит от ingestion и retention.
Принять практическую модель at-least-once input с дедупликацией по event_id. Для изменяемых сущностей дополнительно нужен monotonic revision: последнее по arrival time событие не обязательно последнее по бизнес-версии. Exactly-once внутреннего state не решает двойной учёт между ветвями.
7. Отказы и восстановление¶
| Сбой | Восстановление | Риск для корректности |
|---|---|---|
| Speed processor остановился | Restore checkpoint и replay сохранённого хвоста | До catch-up витрина отстаёт |
| Batch завершился частично | Не публиковать generation; повторить staging build | Нельзя читать смесь старых и новых partitions |
| Переключение coverage произошло раньше view | Публиковать view и manifest одним контролируемым переключением | Возможна дыра в покрытии |
| Две ветви по-разному трактуют refund | Исправить правило, пересчитать оба пути и сверить | Технический replay старой логики не исправляет ошибку |
Потеря raw history за пределами recovery horizon требует восстановления из upstream или признания невосстановимого диапазона; reset offsets сам по себе не возвращает данные.
8. Масштабирование и узкие места¶
Batch расходует storage scan/shuffle, speed — state и checkpoint I/O. Hot merchant или популярный market создаёт skew в обеих ветвях. Масштабировать нужно также serving merge: дорогой query-time union способен обесценить быстрый streaming path.
Исторические расчёты запускать в изолированной очереди с ограничением нагрузки на sink. Производительность оценивать раздельно для live freshness, catch-up и полного rebuild; средняя скорость ingestion не описывает recovery capacity.
9. Преимущества и недостатки¶
| Преимущество | Цена |
|---|---|
| Оперативная предварительная витрина | Два computation paths и сложная публикация |
| Возможность пересчитать историю | Хранение raw и повторные scans |
| Разные ресурсы для batch и speed | Семантический drift и двойная эксплуатация |
Lambda оправдана, если ценность batch-corrected результата перевешивает стоимость двух путей. Она избыточна для простой ежедневной отчётности без оперативного потребителя.
10. Альтернативные решения¶
Kappa сокращает отдельную бизнес-логику, но требует пригодного replay и достаточной мощности rebuild. Streaming-first задаёт приоритет live-пути и может содержать Lambda. Только batch дешевле в эксплуатации, если freshness позволяет ждать. Medallion определяет quality layers и совместим с любым из этих способов расчёта.
Критерии и сценарии — в сравнении трёх подходов.
11. Реальные примеры и границы подтверждения¶
FACT: LinkedIn в статье от 2020-12-01 прямо описывает Lambda в Who Viewed Your Profile и последующий переход к Lambda-less для снижения сложности. Это подтверждённый исторический пример и источник trade-off, а не рекомендация копировать прежнюю систему. LinkedIn Engineering.
UNKNOWN: статья не является доказательством того, что любая современная система LinkedIn использует ту же архитектуру.
12. Применение в FinTech и iGaming¶
FinTech: предварительный merchant turnover обновлять потоком, закрытый период пересчитывать из зафиксированной истории с fees и reversals. Операционное разрешение списания при этом остаётся в ledger/OLTP.
iGaming: live exposure может быть предварительным, а аналитический итог пересчитан после corrected results. Приём ставки и wallet debit нельзя разрешать по eventual analytics view. Решение о том, какие состояния допускаются как предварительные, принадлежит бизнес-контракту.
13. Типичные ошибки проектирования¶
- Фильтровать хвост только по event time и терять поздно пришедшее событие.
- Публиковать batch partitions постепенно без общего manifest.
- Суммировать пересекающиеся snapshots и incremental contributions.
- Держать разные правила валюты, refund или market status в двух ветвях.
- Обещать финальность отчёта лишь потому, что batch job завершился.
14. Вопросы технического интервью с ответами¶
Почему один batch cutoff timestamp недостаточен? При одинаковом event time события могут прибыть в разные моменты и partitions. Нужны точное input coverage и правила late arrivals.
Можно ли получить exactly-once, включив checkpoint у speed processor? Это регулирует state этого движка при выполнении его условий. Serving merge, batch publication и внешние эффекты требуют отдельных доказательств.
Когда оставить Lambda? Когда полная batch-обработка действительно нужна и не может быть разумно заменена unified replay, а польза оперативного хвоста измерима.
15. Практическое упражнение¶
HYPOTHETICAL: даны e1: +100, e2: +40, e3: reversal e1 (-100); e2 доставлен дважды. Batch B1 покрывает только e1, B2 — e1/e2; e3 пришёл поздно по event time.
Опишите два поколения coverage и tail так, чтобы итог после всех событий был 40. Представьте контрпример для query «batch sum + весь speed sum». Критерий приёмки: каждый event_id вносит вклад один раз, смена generation не даёт промежуточного 140+140, late reversal не теряется. Сначала проверьте вручную на множестве IDs; выполнение на движках — отдельная работа Этапа 7.
Дополнительные источники¶
LinkedIn: Lambda-less, 2020; Jay Kreps: альтернативный взгляд, 2014.