Какие варианты масштабирования системы после стадии MVP существуют
Что проверяют
Умеете ли вы масштабировать по данным, а не по моде: найти узкое место и выбрать самое дешёвое решение. Следом почти всегда звучит: «Когда вы бы начали пилить монолит на микросервисы?» Хороший ответ обычно: позже, чем хочется.
Как отвечать
Стройте ответ как шаги по возрастанию цены:
- измерить: метрики и трейсы (Prometheus, Grafana, OpenTelemetry), нагрузочный тест в Locust, профиль через py-spy;
- вертикально: больше CPU и памяти. Дёшево по времени команды и часто закрывает первый рост;
- stateless: сессии и кэш в Redis, файлы в объектном хранилище. Только тогда копии можно ставить за балансировщик;
- процессы вместо потоков: из-за GIL CPU-нагрузку в CPython масштабируют числом воркеров Gunicorn или Uvicorn, стартовая эвристика
2 × CPU + 1. I/O-нагрузку выгоднее переносить на async (FastAPI,asyncio); - база данных: чаще всего узкое место здесь. Индексы по
EXPLAIN ANALYZE, PgBouncer, реплики на чтение, партиционирование. Шардирование последним, оно дорого в поддержке; - кэш и CDN: Redis для горячих данных, CDN для статики;
- очереди: письма, отчёты, обработка файлов уходят в Celery, RQ или Dramatiq поверх RabbitMQ или Redis;
- выделение сервисов: из монолита выносят компонент с особым профилем нагрузки, а не режут всё сразу.
Закончите мыслью о компромиссах: каждая ступень добавляет сложность в эксплуатации, и к следующей переходят, когда метрики показывают, что предыдущая исчерпана.
Где спотыкаются
Сразу предлагают Kubernetes и микросервисы. Для маленькой команды это месяцы работы над инфраструктурой вместо продукта, а узкое место в базе остаётся.
Поднимают вторую копию, не убрав состояние из памяти процесса. Пользователя выбрасывает из сессии, локальный кэш расходится между копиями.
Забывают про соединения с базой: десять копий по двадцать воркеров упираются в max_connections PostgreSQL, отсюда и нужен пул.