Serverless patterns: FaaS (AWS Lambda + API Gateway + DynamoDB + Aurora via RDS Proxy), event-driven (S3 → SQS → Lambda batch with DLQ), Step Functions orchestrated saga (ChargeCard → ReserveInventory → Compensate Refund), EventBridge cron, Cloudflare Workers at the edge with KV + D1 + Durable Objects, Cloud Run container serverless. Covers cold start vs warm pool vs provisioned concurrency, RDS Proxy connection pooling, anti-pattern of Lambda + direct RDS connection storm. Includes ADR on serverless vs containers vs k8s cost/scale crossover.
Serverless — это не «без серверов», а «без хостов, которые я бутстраплю и патчу». Платформа держит warm pool рантаймов и тарифицирует тебя по invocations/CPU-time/память. Главный sales pitch — scale-to-zero и идеальное соответствие cost = usage: cron, который дергается раз в сутки, не платит за 24 часа idle EC2; webhook receiver, который ловит 1 событие в час, не платит за running pod.
Цена платится тремя вещами: (1) cold start — Lambda на Node.js init ~100-200ms, Java/Spring до 600ms, Cloud Run контейнер 1-3s; (2) жёсткие лимиты рантайма — 15 мин execution на Lambda, 250MB unzipped пакет, 10GB на /tmp; (3) новый класс антипаттернов — connection storm на RDS, chatty inter-Lambda RPC, vendor lock-in через managed event services.
Паттерны, которые покрывает диаграмма: синхронный API (API GW + Lambda + DynamoDB), event fan-out (S3 → SQS → Lambda batch), orchestration (Step Functions saga + compensation), scheduled jobs (EventBridge cron), edge compute (Cloudflare Workers V8 isolates), container serverless (Cloud Run) и canonical анти-паттерн (Lambda + RDS connection storm).
Думай о serverless не как о замене k8s, а как о дереве выбора по трём осям:
Внутри FaaS-модели есть три состояния environment:
И ключевое разграничение: Lambda = ephemeral compute, никакого state между invocations. State уходит в managed services (DynamoDB, S3, SQS) или в durable workflow (Step Functions сохраняет execution context между Task'ами). Попытка держать state в Lambda-переменной приведёт к багу, который воспроизводится только на следующий день.
Clients — мобила, браузер с статической SPA, и отдельный uploader, который шлёт файлы напрямую в S3 через presigned URLs (не через Lambda — избегаем 6MB payload limit и платы за compute на upload).
AWS Region — основной AWS-стек, делится на четыре подгруппы:
order-api (sync API), thumbnail-Fn (S3-trigger), sqs-worker-Fn (batch=10), dlq-handler-Fn (alerting на poison pill).checkout-saga плюс 4 Task'а: ChargeCard, ReserveInventory, CreateShipment, RefundCard (compensation).Cloudflare Edge — Worker (V8 isolate, <5ms cold start), Workers KV (eventually consistent, ~50K rps), D1 (SQLite at edge с Durable Objects для strong consistency).
Alternatives — Cloud Run (container serverless, scale-to-zero, ~1-3s cold) и внешний Stripe API (provider платежей).
Базовый sync-flow: mobile → CloudFront → API Gateway → Lambda → DynamoDB + RDS Proxy → Aurora. Показывает cold start (~120ms Firecracker init), JWT валидацию на API GW, и почему RDS Proxy не optional: без него каждый из 1000 параллельных Lambda откроет свой connection в Aurora и выжрет cap=200.
Сравнивает три режима: cold (800ms, p99 страдает), warm (50ms, env переиспользуется ~5-15 мин), provisioned concurrency (pre-warmed, платим даже idle). В конце переключаемся на Cloudflare Worker, где cold start <5ms — V8 isolate стартует без microVM, и проблема просто исчезает.
Event-driven pipeline без сервера: uploader → presigned PUT в S3 → ObjectCreated триггерит thumbnail-Fn напрямую (для image/* prefix) И параллельно кладёт событие в SQS → sqs-worker-Fn long-polls батчами по 10 (экономия invocations). Любое сообщение, провалившееся 3 раза, уходит в DLQ → dlq-handler-Fn шлёт PagerDuty + сохраняет для manual replay.
Distributed transaction через Step Functions: ChargeCard (Stripe) → ReserveInventory (DynamoDB ConditionalCheck). Inventory фейлится (товар разобран) → Catch state ловит ошибку → переход в Compensate ветку → RefundCard возвращает деньги. State machine ends в FAILED, но деньги вернули, бизнес-инвариант сохранён. Это и есть saga compensation pattern, описанный без явного кода координатора.
Happy path той же саги, но с Parallel state: ReserveInventory и CreateShipment запускаются одновременно (max concurrency=2), Step Functions ждёт join. Underлинит цену — $25 за 1M state transitions, что становится заметно при высокой проходимости (рассмотри Temporal/durable execution на собственной инфре).
EventBridge cron заменяет EC2-хост с crontab: rule срабатывает каждые 6 часов → async invoke Lambda → batch DeleteItem expired sessions. Стоимость: $0.20 за 1M invocations. Сравнение с традиционным cron: EC2 24/7 для job'а раз в 6 часов = 99.9% idle.
Edge rendering: request попадает в ближайший PoP (~10ms ack по сети), V8 isolate спавнится за 5ms, читает Workers KV (~5ms на edge). Total ~25ms из Сиднея против ~200ms через regional API GW. KV miss → fallback в D1 (SQLite на edge с Durable Object для strong consistency) → fill KV с TTL=3600.
Когда Lambda не подходит: 5-минутный ffmpeg transcode. Длинный task роутится в Cloud Run, контейнер cold-start 1-3s, request stays open, scale-to-zero когда idle. Use cases: custom runtime, deps >250MB лимита Lambda, GPU, long-running.
Каноничный death spiral: 1000 concurrent requests → 1000 cold Lambda envs → каждый открывает new TCP connection в Aurora → Aurora max_connections=200 exhausted → "too many connections" → cascading failures. Четыре фикса показаны последовательно: RDS Proxy, Provisioned Concurrency, миграция на DynamoDB, reserved concurrency cap.
Подробный ADR в data.decisions ноды aws, но кратко — где проходит crossover:
| Профиль | Выбор | Почему |
|---|---|---|
| <1 RPS sustained, bursty/sparse | Lambda + API GW | Pay-per-invoke выигрывает у любого always-on host. Cold start 100-200ms приемлем |
| >500 RPS sustained 24/7 | k8s + HPA | Reserved EC2/EKS дешевле Lambda на 60-80% на постоянной нагрузке |
| >15 минут или custom runtime / GPU / large image | Cloud Run / Fargate | Не упираемся в 15-min hard limit Lambda |
| Edge / global low-latency (<50ms p99) | Cloudflare Workers | V8 isolates без microVM cold start |
| Stateful workloads | НЕ serverless | RDS/EKS StatefulSet, serverless = ephemeral |
Crossover точка по цене (типичный 200ms/512MB Lambda): ~2.5M req/мес → Lambda $30 vs k8s pod $70. При 100M req/мес → Lambda $1200 vs k8s ~$200. Дальше серверлесс становится дороже компьюта, но всё ещё экономит SRE-часы — нужно считать total cost включая людей.
Когда Step Functions проигрывает: $25/1M state transitions ощутимо при >100M transitions/мес. Альтернатива — Temporal / durable execution на собственной инфре (фиксированный cost EC2 vs per-transition).
Chatty Lambda + RDS — основная причина миграций назад на EC2 у тех, кто прыгнул в serverless без RDS Proxy. Покрыт в сценарии anti-pattern. Symptom: периодические "too many connections" под пиком, выглядит как DB-баг, на самом деле — архитектурный.
Lambda → Lambda → Lambda цепочки (chatty RPC) — каждый sync invoke добавляет cold start risk и платится дважды (waiting Lambda тоже тарифицируется). Если есть chain >2 шагов — это Step Functions kandидат.
Real-time WebSocket fan-out на Lambda — платим за каждое открытое соединение через API GW WebSocket, плюс Lambda invoke на каждое сообщение. На 100K connected clients = death by a thousand invocations. WebSocket-сервер на EC2/k8s остаётся дешевле и проще.
ML inference с большими моделями — модель 2GB не влезает в Lambda layer (250MB unzipped), cold start с pull image из ECR = 10-30s. SageMaker Endpoint или GPU-инстанс лучше.
Lambda как cron на 100K разных расписаний — EventBridge rules limited, и cost per rule ощутим. Один Lambda с собственным in-process scheduler дешевле.
Vendor lock-in через managed event services — DynamoDB Streams, Kinesis, EventBridge — все proprietary. Миграция в GCP/Azure = переписать event layer. Mitigation: абстракция через интерфейсы в коде, признание что 80% serverless кода всё равно portable (handlers — обычный код).
::concept{slug="caching-patterns"} — без кеша (CloudFront, KV, Cache-Control) cold paths делают serverless дорогим
::concept{slug="rate-limiting"} — API Gateway throttling и Lambda reserved concurrency как rate-limiting механизмы
::concept{slug="cap-theorem"} — DynamoDB on-demand (AP), Aurora (CP), Workers KV (eventually consistent), D1 Durable Objects (strong consistency) — каждый managed service занимает свою точку CAP
::case{slug="twitter-system-design"} — fanout-on-write через event queues, классический паттерн для serverless event-driven
::case{slug="uber-system-design"} — Step Functions для дрив-state machine (ride lifecycle) или Temporal для high-throughput orchestration