Microservices with capacity data: API Gateway, Cache, Kafka, PostgreSQL, MongoDB. Bottleneck detection, latency paths, and scaling recommendations.
Capacity planning отвечает на практический вопрос: сколько ресурсов нужно системе, чтобы выдержать ожидаемую нагрузку, пик, отказ части инфраструктуры и рост бизнеса без лишнего сжигания бюджета. Это не то же самое, что грубая back-of-envelope оценка на интервью. Back-of-envelope помогает быстро прикинуть порядок величин. Capacity planning превращает эти величины в решение: сколько реплик, какой headroom, где bottleneck, какие лимиты у базы, когда масштабировать вертикально, когда шардировать, где нужен cache, а где async pipeline.
Без capacity planning команда обычно живет в одном из двух режимов. Первый — недопровиженинг: обычный день работает, Black Friday или viral spike кладет базу, autoscaler не успевает, P99 растет, retry storm добивает downstream. Второй — перепровиженинг: сервисы работают на 5 процентов utilization, но cloud bill постоянно растет. Хороший план ищет середину: достаточно headroom для отказов и пиков, но без бессмысленного 10x запаса в каждом слое.
В production важны не только RPS. Нужно учитывать latency P50/P99, concurrency по Little's law, connection limits, storage growth, replication factor, network egress, cache hit ratio, broker throughput, consumer lag, failover capacity и реальные workload-пики. Именно поэтому диаграмма с capacity data полезнее абстрактной схемы микросервисов: она показывает, где система сломается первой.
Mental model: capacity plan — это цепочка baseline -> forecast -> peak factor -> replication -> headroom -> node count. Сначала измеряем текущую нагрузку и latency. Затем прогнозируем рост. Потом умножаем на пик: день распродажи, спортивное событие, зарплатный день, viral traffic. После этого добавляем replication для HA и headroom для failover, deploy и неожиданных всплесков.
Еще одна базовая модель — Little's law: L = lambda * W. Если сервис получает 10000 RPS, а средняя latency 80 ms, внутри одновременно находится около 800 запросов. Значит, connection pools, worker pools, file descriptors и queue capacity должны быть рассчитаны на эту concurrency, а не на красивое среднее CPU.
Третья модель — bottleneck thinking. Throughput системы ограничен самым слабым обязательным участком request path. Если API Gateway держит 50K RPS, Redis держит 100K RPS, Kafka держит 200K events/s, а PostgreSQL primary держит 5K writes/s, то вся write-path упирается в PostgreSQL. Добавление API replicas не исправит базу.
Диаграмма строит реалистичную микросервисную систему: API Gateway, User Service, Order Service, Product Service, Redis primary/replica, Kafka brokers, PostgreSQL primary/replica и MongoDB primary. На узлах и ребрах заданы capacity hints: maxRps, replicas, latency P50/P99, messageSize и фактический RPS на связях.
Главная ценность диаграммы — явное обнаружение bottleneck. PostgreSQL primary имеет capacity около 5000 RPS, но получает user writes и order writes суммарно около 15000 RPS. MongoDB primary имеет 3000 RPS capacity, но получает около 10000 RPS. PostgreSQL replica получает product reads около 14000 RPS при capacity 8000 RPS. При этом Redis и Kafka остаются в норме. Это показывает важную мысль: scaling plan должен быть адресным, а не одинаковым для всех компонентов.
Диаграмма также показывает смешение sync и async путей. API Gateway вызывает сервисы синхронно. Order Service пишет в Postgres и MongoDB, а событие order.created отправляет в Kafka. Kafka может decouple часть обработки, но не спасает синхронные writes, если они остаются в request path.
Normal Load показывает steady-state: API Gateway принимает запрос, User Service проверяет session в Redis, Product Service берет данные из cache, Order Service пишет заказ в Postgres и MongoDB, а затем публикует событие в Kafka. Урок: нормальный сценарий нужен как baseline. Без baseline невозможно понять, какие компоненты имеют запас, а какие уже близки к пределу.
Peak Load показывает Black Friday-style всплеск: 45K RPS входит в API Gateway, трафик распределяется по сервисам, и bottlenecks становятся видимыми. PostgreSQL primary работает примерно на 300 процентов от capacity, MongoDB — на 333 процента, PostgreSQL replica перегружена reads. Redis и Kafka при этом держатся. Урок: пик ломает не всю систему равномерно, а конкретные shared resources.
Scale-out Plan показывает не просто “добавьте серверов”, а набор решений: добавить read replicas и PgBouncer для Postgres, маршрутизировать read traffic в pool реплик, шардировать MongoDB или перейти к replica set с правильным распределением, вынести часть writes через CQRS и Kafka, контролировать скорость consumer через back-pressure. Урок: capacity mitigation часто меняет архитектуру, а не только количество pod.
ADR: Scale out API vs remove database bottleneck. Если перегружена база, добавление API Gateway replicas ухудшит ситуацию: больше callers создадут больше connections и queueing у primary. Решение должно идти к bottleneck: read replicas, write batching, schema/index tuning, partitioning, sharding, cache или async processing.
ADR: Headroom 30 процентов vs 50 процентов vs 100 процентов. Малый headroom дешевле, но опасен при failover и deploy. 50 процентов часто разумны для distributed services: потеря одной зоны или rollout не сразу съедают весь запас. 100 процентов может быть оправдан для unpredictable workloads, но дорого. Для batch с предсказуемыми окнами можно держать выше utilization, если missed deadline не равен outage.
ADR: Reactive autoscaling vs pre-warmed capacity. Reactive scaling дешевле в спокойные периоды, но имеет лаг: метрика должна сработать, новые instances должны стартовать, images скачаться, caches прогреться. Для предсказуемых пиков нужен predictive или scheduled scaling. Для внезапных всплесков нужны load shedding, queues, back-pressure и заранее заданный emergency mode.
ADR: Cache vs database scaling. Cache снижает read load и latency, но добавляет invalidation, stale reads и memory cost. Если workload read-heavy и допускает eventual consistency, cache может дать огромный выигрыш. Если проблема в write throughput или transactional constraints, cache ее не решит.
ADR: Read replicas vs sharding. Read replicas помогают, когда bottleneck — reads. Они не увеличивают write capacity primary и могут добавить replication lag. Sharding увеличивает write capacity и storage scale, но усложняет queries, transactions, rebalancing и hot-key mitigation. Решение зависит от доминирующего workload.
ADR: Queue/CQRS vs synchronous writes. Async pipeline помогает сгладить пики и контролировать скорость записи в storage. Но если user должен сразу получить подтверждение durable commit, полностью убрать sync write нельзя. Тогда нужен idempotency, reservation model, transactional outbox или четкое разделение command accepted и command completed.
E-commerce готовится к Black Friday месяцами: load testing, pre-warming, traffic forecasts, regional failover drills, database connection pool audits. Stripe публично рассказывал о больших peak factors и подготовке к сезонным пикам. Live events вроде крупных трансляций требуют заранее поднятой capacity, потому что reactive autoscaling не успевает за миллионами одновременных viewers.
В cloud-системах network egress может стать главным cost driver. Видео, file downloads и API с большими responses должны считать не только CPU и storage, но и outbound GB, CDN hit ratio и региональные тарифы. В storage-heavy системах replication factor, backups, indexes, WAL и retention policy часто дают больший рост, чем application servers.
Для баз данных реальные лимиты часто не совпадают с CPU. Postgres может упереться в connections, locks, WAL fsync, index bloat или hot row. Kafka может упереться в partition count, disk throughput или ISR replication. Redis может упереться в memory, single-thread CPU или network. Capacity planning требует измерять конкретный bottleneck.
Самая опасная ошибка — планировать по average load. Среднее скрывает P99 peak, сезонность и региональные события. Система, рассчитанная на средние 10K RPS, может умереть на 100K RPS за минуты. Вторая ошибка — линейно экстраполировать scaling. Universal Scalability Law напоминает: contention и coherence делают “добавим еще нод” ограниченным решением.
Третья ошибка — забыть connection limits. Десятки реплик приложения с большими pool size легко создают тысячи connections к Postgres. Иногда PgBouncer важнее, чем еще одна database replica. Четвертая ошибка — не учитывать failover. Если три AZ работают на 80 процентов, потеря одной зоны означает перегрузку оставшихся.
Пятая ошибка — верить autoscaler как единственной защите. Он не отменяет warmup, cold starts, image pulls, cache misses и downstream limits. Шестая ошибка — не проводить load test. Бумажный plan без нагрузочного теста почти всегда завышает реальную capacity.
Еще один anti-pattern — оптимизировать не bottleneck. Команда ускоряет Product Service, хотя P99 определяется PostgreSQL primary. Или добавляет Kafka brokers, когда lag возникает из-за медленного consumer. Capacity work должен начинаться с измерения path latency и utilization по каждому shared resource.
Не нужно строить сложную capacity-модель для маленького внутреннего инструмента с десятками запросов в день и дешевым ручным масштабированием. Достаточно простых лимитов, мониторинга и понятного owner.
Capacity planning не заменяет performance profiling. Если код делает N+1 queries, держит глобальный lock или сериализует большие payloads, сначала нужно исправить waste. Иначе план будет покупать железо под плохой алгоритм.
Не стоит применять тяжелые forecasting-модели, если стоимость ошибки мала. Linear trend с seasonal multiplier часто лучше, чем ML-инфра без owner. Сложные модели оправданы, когда overprovisioning или outage действительно стоят дорого.
back-of-envelope — быстрые оценки RPS, storage и bandwidth.latency-vs-throughput — почему throughput и P99 надо считать вместе.back-pressure — контроль скорости consumer при перегрузке.load-shedding — как деградировать вместо полного outage.hot-key-mitigation — когда средняя capacity нормальная, но один shard горит.caching-patterns — read capacity и trade-off stale data.message-queues — durable buffering, lag и async processing.