Перейти к содержимому
К программе курса
Архитектура высоконагруженных веб-приложений
1 / 119%

Триггер паники: ваше приложение упало при росте в 10 раз

60 минут

Добро пожаловать в ад

Вы — Senior Backend Engineer в стартапе электронной коммерции. Сегодня среда, 14:00. Вы пьете кофе и планируете спокойный день.

Вдруг:

14:03 — Slack: @channel URGENT: сайт тормозит
14:04 — PagerDuty: CRITICAL: API P99 latency > 5000ms
14:05 — CEO звонит: "КЛИЕНТЫ НЕ МОГУТ ОФОРМИТЬ ЗАКАЗ!"
14:06 — Grafana: RPS вырос с 200 до 2000 за 10 минут

Что произошло? Ваш продукт попал в топ ProductHunt. Органический трафик x10. Маркетинг счастлив. Инфраструктура — нет.

Бюджет: $10,000/месяц на инфраструктуру. У вас есть кредитка для экстренных случаев.

Время до полного краха: 30 минут, если ничего не делать.

Для кого этот урок: Для всех, кто думает, что highload — это теория. Это симуляция реального кризиса. Вы будете принимать решения под давлением, считать деньги и последствия. Ошибки будут стоить виртуальных денег и реального понимания.


Текущее состояние системы (14:06)

Архитектура "как есть"

┌─────────────┐
│   Nginx     │  ← 1 instance (t3.small, 2 vCPU, 2GB RAM)
│  (LB + SSL) │     CPU: 85%, connections: 950/1024
└──────┬──────┘

       ├─────────────────┬─────────────────┐
       ▼                 ▼                 ▼
   ┌────────┐        ┌────────┐        ┌────────┐
   │ API-1  │        │ API-2  │        │ API-3  │
   │t3.medium        │t3.medium        │t3.medium
   │4vCPU,8GB│       │4vCPU,8GB│       │4vCPU,8GB│
   │CPU: 95% │       │CPU: 92% │       │CPU: 89% │
   └────┬───┘        └────┬───┘        └────┬───┘
        │                 │                 │
        └─────────────────┴─────────────────┘

                 ┌─────────────────┐
                 │   PostgreSQL    │
                 │   (db.t3.large) │
                 │   4vCPU, 16GB   │
                 │   CPU: 98%      │
                 │   Connections:  │
                 │   95/100        │
                 └─────────────────┘

Метрики критичности (live)

МетрикаНормаСейчасСтатус
RPS2002,000🔴 CRITICAL
API P50 latency50ms800ms🔴 CRITICAL
API P99 latency200ms5,200ms🔴 CRITICAL
Error rate0.1%12%🔴 CRITICAL
DB CPU30%98%🔴 CRITICAL
DB Connections20/10095/100🟡 WARNING
Nginx CPU15%85%🟡 WARNING
Estimated revenue loss$0$450/min💰 BLEEDING

Топ-5 медленных запросов (за последние 5 минут)

-- #1: 3,500ms avg (было 45ms)
SELECT * FROM orders
WHERE user_id = $1
ORDER BY created_at DESC
LIMIT 20;
-- Выполнено: 15,000 раз за 5 минут
-- Индекс: ОТСУТСТВУЕТ на (user_id, created_at)
 
-- #2: 2,800ms avg (было 30ms)
SELECT p.*, r.avg_rating
FROM products p
LEFT JOIN (
  SELECT product_id, AVG(rating) as avg_rating
  FROM reviews GROUP BY product_id
) r ON p.id = r.product_id
WHERE p.category = $1;
-- Выполнено: 22,000 раз за 5 минут
-- Проблема: N+1 в агрегации, нет кэша
 
-- #3: 1,900ms avg (было 15ms)
SELECT * FROM users WHERE email = $1;
-- Выполнено: 35,000 раз за 5 минут
-- Индекс: ЕСТЬ, но табличка раздулась (10M строк, не VACUUM)
 
-- #4: 1,200ms avg (было 80ms)
INSERT INTO analytics_events (user_id, event_type, payload)
VALUES ($1, $2, $3);
-- Выполнено: 18,000 раз за 5 минут
-- Проблема: синхронная вставка в аналитику
 
-- #5: 900ms avg (было 25ms)
UPDATE sessions SET last_activity = NOW() WHERE session_id = $1;
-- Выполнено: 45,000 раз за 5 минут
-- Проблема: обновление в БД на каждый запрос

Ваши действия: Что делать ПРЯМО СЕЙЧАС?

У вас есть 30 минут до того, как система полностью ляжет.

Каждое решение имеет:

  • Стоимость (деньги)
  • Время реализации (минуты)
  • Эффект (снижение нагрузки, latency)
  • Риски (что может пойти не так)

Доступные действия

ВАЖНО: Вы не можете сделать всё сразу. Выбирайте приоритеты. Каждое действие отнимает время и деньги. Неправильный выбор может усугубить ситуацию.

Действие A: Вертикальное масштабирование БД

Что: Апгрейд PostgreSQL с db.t3.large (4vCPU, 16GB) на db.r6g.2xlarge (8vCPU, 64GB)

Стоимость:

  • Было: $140/месяц
  • Станет: $460/месяц
  • Дельта: +$320/месяц

Время: 15 минут (downtime 5 минут во время переключения)

Эффект:

  • ✅ CPU БД: 98% → ~40%
  • ✅ Latency топ-5 запросов: -30%
  • ✅ Connections headroom: больше
  • ⚠️ Но запросы всё равно медленные (индексов нет!)

Риски:

  • 🔴 5 минут downtime = $2,250 потерь
  • 🟡 Если проблема не в CPU, деньги потрачены зря

Действие B: Добавить индексы в БД

Что: Создать индексы на orders(user_id, created_at) и users(email)

Стоимость:

  • $0 (только время инженера)

Время: 10 минут (создание индексов на 10M строк)

Эффект:

  • ✅ Запросы #1 и #3: 3,500ms → 50ms, 1,900ms → 15ms
  • ✅ DB CPU: 98% → ~60%
  • ✅ P99 latency: 5,200ms → ~1,800ms

Риски:

  • 🟡 Создание индекса заблокирует таблицу на 10 минут (можно CONCURRENTLY, но дольше)
  • 🟡 Если БД уже перегружена, индекс может не создаться

Действие C: Горизонтальное масштабирование API

Что: Добавить 3 дополнительных инстанса API (итого 6 вместо 3)

Стоимость:

  • Было: 3 × $70/месяц = $210/месяц
  • Станет: 6 × $70/месяц = $420/месяц
  • Дельта: +$210/месяц

Время: 8 минут (деплой через auto-scaling group)

Эффект:

  • ✅ API CPU: 95% → ~50%
  • ✅ RPS per instance: снижается в 2 раза
  • ⚠️ Но БД всё равно bottleneck (CPU 98%)

Риски:

  • 🔴 Если БД не выдержит, API будет простаивать
  • 🟡 Увеличение connections к БД (может упереться в лимит 100)

Действие D: Добавить Redis для кэша

Что: Поднять Redis кластер (3 ноды) и кэшировать топ-запросы

Стоимость:

  • Redis: $180/месяц (cache.r6g.large × 3)
  • Дельта: +$180/месяц

Время: 20 минут (провижн + код для кэширования топ-запросов)

Эффект:

  • ✅ Запросы #2 (продукты): 2,800ms → 5ms (кэш на 5 минут)
  • ✅ DB нагрузка: -40%
  • ✅ P99 latency: 5,200ms → ~2,000ms

Риски:

  • 🟡 20 минут — долго, система может не дождаться
  • 🟡 Кэш может быть stale (пользователи видят старые данные)

Действие E: Async обработка аналитики

Что: Вынести INSERT INTO analytics_events в очередь (Redis + worker)

Стоимость:

  • $0 (используем существующие серверы)

Время: 15 минут (код + деплой)

Эффект:

  • ✅ Запрос #4: 1,200ms → 10ms (async)
  • ✅ DB CPU: -15%
  • ✅ P99 latency: -20%

Риски:

  • 🟡 Аналитика придет с задержкой (eventual consistency)
  • 🟡 Нужна очередь (можно использовать Redis)

Действие F: Перенести сессии в Redis

Что: Хранить сессии в Redis вместо БД

Стоимость:

  • $0 (используем Redis из действия D, если он уже есть)
  • Если Redis нет, нужно сначала создать → см. действие D

Время: 10 минут (код + деплой)

Эффект:

  • ✅ Запрос #5: 900ms → 2ms
  • ✅ DB CPU: -20%
  • ✅ P99 latency: -15%

Риски:

  • 🔴 Если Redis нет, нужно сначала создать (время × 2)

Действие G: Rate Limiting на Nginx

Что: Ограничить RPS на уровне Nginx (например, 500 RPS)

Стоимость:

  • $0

Время: 5 минут (конфиг + reload)

Эффект:

  • ✅ Защита от перегрузки
  • ✅ Система стабилизируется
  • ⚠️ Но часть пользователей получит 429 (Too Many Requests)

Риски:

  • 🔴 Бизнес будет недоволен (отказ клиентам)
  • 🟡 Нужно правильно выбрать лимит

Действие H: Включить CDN для статики

Что: Включить CloudFront/Fastly для статики (images, CSS, JS)

Стоимость:

  • $50/месяц (при текущем трафике)
  • Дельта: +$50/месяц

Время: 10 минут (конфиг)

Эффект:

  • ✅ Nginx CPU: 85% → ~40%
  • ✅ Connections: освобождаются
  • ⚠️ Не влияет на API latency

Риски:

  • 🟡 Минимальный эффект на основную проблему (БД)

Действие I: Connection Pooling (pgbouncer)

Что: Установить pgbouncer перед БД

Стоимость:

  • $0 (поднимаем на существующих серверах)

Время: 12 минут

Эффект:

  • ✅ DB connections: более эффективное использование
  • ✅ Защита от connection exhaustion
  • ⚠️ Не влияет на медленные запросы

Риски:

  • 🟡 Требует настройки pool size

Действие J: Убить аналитику (временно)

Что: Отключить запись аналитики (analytics_events)

Стоимость:

  • $0

Время: 2 минуты (feature flag)

Эффект:

  • ✅ DB CPU: -15%
  • ✅ Latency: -10%

Риски:

  • 🔴 Потеря данных аналитики (навсегда)
  • 🟡 Маркетинг будет недоволен

Ваш выбор: Составьте план спасения

У вас есть $1,000 экстренного бюджета и 30 минут.

Задание: Выберите действия и обоснуйте

Заполните таблицу:

ПорядокДействиеВремяСтоимостьОбоснование
1?? мин$?Почему первым?
2?? мин$?Почему вторым?
3?? мин$?Почему третьим?
Итого? мин$?Укладываетесь ли в 30 минут и $1,000?

Вопросы для самопроверки

  1. Какая главная проблема? CPU БД, медленные запросы, отсутствие кэша, или всё сразу?

  2. Что даст максимальный эффект быстрее всего?

    • Индексы (10 мин, $0, -70% latency)
    • Масштабирование БД (15 мин, $320/мес, -60% CPU)
    • Кэш (20 мин, $180/мес, -40% нагрузка)
  3. Можете ли вы позволить себе downtime?

    • 5 минут downtime = $2,250 потерь
    • Vs. медленная работа = $450/мин потерь
  4. Какие действия дадут долгосрочный эффект?

    • Индексы — навсегда
    • Масштабирование — временно (пока трафик не вырастет еще)
    • Кэш — пока данные актуальны

Моё решение: Как я бы спасал систему

Важно: Это один из вариантов. Правильных ответов может быть несколько. Главное — обоснование.

План действий (30 минут)

ПорядокДействиеВремяСтоимостьНакопленное время
1G: Rate Limiting5 мин$05 мин
2B: Индексы10 мин$015 мин
3J: Убить аналитику2 мин$017 мин
4C: +3 API8 мин$210/мес25 мин
5I: pgbouncer5 мин$030 мин
Итого30 мин$210/мес

Обоснование каждого шага

1️⃣ Rate Limiting (5 минут, $0)

Зачем первым?

  • Немедленная защита от полного краха
  • Система перестанет принимать больше, чем может обработать
  • Лимит: 800 RPS (текущая capacity × 1.5)
  • Да, часть пользователей получит 429, но система выживет

Эффект:

  • RPS: 2,000 → 800
  • DB CPU: 98% → ~60%
  • API CPU: 95% → ~50%
  • Error rate: 12% → 2% (контролируемый отказ)

Минусы:

  • ~1,200 запросов/сек отклоняются
  • Потери: $450/мин → $300/мин (улучшение!)

2️⃣ Индексы (10 минут, $0)

Зачем вторым?

  • Максимальный эффект при минимальных затратах
  • Решает root cause (медленные запросы)
  • Запросы #1 и #3 ускоряются в 70-100 раз

Эффект:

  • DB CPU: 60% → 30%
  • P99 latency: 5,200ms → 1,200ms
  • Error rate: 2% → 0.5%

Риск:

  • Создание индекса занимает 10 минут
  • Используем CREATE INDEX CONCURRENTLY (не блокирует, но дольше)

3️⃣ Убить аналитику (2 минуты, $0)

Зачем третьим?

  • Быстрая победа
  • Освобождаем БД от ненужной нагрузки
  • Аналитика не критична для checkout

Эффект:

  • DB CPU: 30% → 20%
  • Latency: -10%

Минусы:

  • Потеря данных аналитики
  • Нужно будет вернуть async (действие E) позже

4️⃣ Горизонтальное масштабирование API (8 минут, $210/мес)

Зачем четвертым?

  • БД уже не bottleneck (20% CPU)
  • Распределяем нагрузку по API
  • Готовимся к следующему скачку трафика

Эффект:

  • API CPU: 50% → 25%
  • Headroom для роста

5️⃣ pgbouncer (5 минут, $0)

Зачем пятым?

  • Оптимизация connections
  • Защита на будущее
  • Легко и быстро

Эффект:

  • Connection pooling работает
  • Готовность к росту

Результат через 30 минут

МетрикаБыло (14:06)Стало (14:36)Улучшение
RPS2,000800 (rate limited)Контролируемо
API P99 latency5,200ms400ms-92%
Error rate12%0.3%-97%
DB CPU98%20%-80%
Revenue loss$450/min$90/min-80%
Total cost$210/месОкупится за 1 день

Что НЕ делали и почему

Действие A (Вертикальное масштабирование БД) — не нужно, индексы решили проблему дешевле

Действие D (Redis кэш) — нет времени (20 минут), сделаем позже

Действие E (Async аналитика) — просто убили аналитику, вернем async позже

Действие F (Сессии в Redis) — нужен Redis, которого нет

Действие H (CDN) — не критично прямо сейчас


Что дальше: Post-Incident Action Items

Сегодня вечером (после инцидента)

  1. Написать постмортем

    • Что случилось
    • Почему
    • Что сделали
    • Lessons learned
  2. Вернуть аналитику через очередь (Действие E)

    • Redis + worker
    • Async processing
  3. Добавить мониторинг

    • Alert: DB CPU > 70%
    • Alert: P99 latency > 1000ms
    • Alert: Error rate > 1%

На этой неделе

  1. Добавить Redis кэш (Действие D)

    • Кэширование топ-запросов
    • Cache warming для популярных продуктов
  2. Перенести сессии в Redis (Действие F)

    • Разгрузка БД
    • Быстрее обновления
  3. CDN для статики (Действие H)

    • Разгрузка Nginx

В следующем месяце

  1. Read replicas для БД

    • Разделение read/write
    • Масштабирование чтений
  2. Auto-scaling для API

    • Автоматическое масштабирование при росте
  3. Chaos engineering

    • Регулярные тесты отказоустойчивости
    • Kill random pod every day

Уроки из этого инцидента

1. Индексы — это бесплатная производительность

ROI: $0 вложений, -70% latency, -80% DB CPU

Почему не было индексов?

  • Никто не проверял медленные запросы
  • Нет pg_stat_statements
  • Нет регулярного аудита

Что изменить:

  • ✅ Включить pg_stat_statements
  • ✅ Еженедельный аудит топ-10 запросов
  • ✅ Автоматический alert на missing indexes

2. Мониторинг должен быть ДО инцидента

Что было:

  • Узнали о проблеме от CEO, не от мониторинга
  • Нет alertов на критичные метрики

Что нужно:

  • ✅ Alert: P99 latency > 1000ms
  • ✅ Alert: Error rate > 1%
  • ✅ Alert: DB CPU > 70%
  • ✅ Runbook для каждого alert

3. Rate Limiting — это не зло, это защита

Заблуждение: "Мы не можем отказывать клиентам"

Реальность:

  • Лучше отказать 40% клиентам с 429, чем всем с 500
  • Контролируемая деградация > полный крах

Стратегия:

  • ✅ Rate limiting по умолчанию
  • ✅ Приоритизация (authenticated users > anonymous)
  • ✅ Graceful degradation

4. Вертикальное масштабирование — не всегда ответ

Соблазн: "Давайте просто купим больше CPU"

Реальность:

  • $320/месяц за +4 vCPU БД
  • VS $0 за индексы
  • Индексы дали больше эффекта

Правило:

  • Сначала оптимизируй
  • Потом масштабируй

5. Async — твой друг

Проблема: Аналитика блокирует основной поток

Решение:

  • Всё, что не критично для ответа → в очередь
  • Аналитика, email, push notifications — async

6. Стоимость downtime vs стоимость решения

Расчёт:

  • Downtime 5 минут = $2,250
  • Индексы + rate limiting = $0, 15 минут, без downtime
  • Потери за 15 минут = $6,750 (vs $2,250)

Но:

  • Индексы решили проблему навсегда
  • Вертикальное масштабирование — временно

Вывод: Иногда стоит потерпеть 15 минут, чтобы решить root cause


7. Бюджет — это constraint, который делает тебя лучше

С деньгами: "Давайте купим самую большую БД" Без денег: "Давайте оптимизируем запросы"

Результат:

  • С деньгами: проблема вернется через месяц
  • Без денег: проблема решена навсегда

Практика: Ваш инцидент

Задание 1: Постмортем

Напишите постмортем для этого инцидента по шаблону:

# Postmortem: API Latency Spike (ProductHunt Traffic)
 
**Date:** 2024-12-15
**Duration:** 30 минут
**Impact:** 12% error rate, $13,500 revenue loss
**Severity:** SEV-1
 
## Timeline
 
- 14:03 — Первое упоминание в Slack
- 14:04 — Alert: P99 > 5000ms
- 14:06 — Началось расследование
- ...
 
## Root Cause
 
...
 
## Resolution
 
...
 
## Action Items
 
1. [ ] ...
2. [ ] ...
 
## Lessons Learned
 
...

Задание 2: Ваш план

Решите инцидент своим способом. Может быть, вы выберете другие действия?

Что, если:

  • У вас $5,000 бюджета, а не $1,000?
  • У вас 60 минут, а не 30?
  • У вас уже был Redis?
  • У вас не было прав создавать индексы (нужно согласование DBA)?

Задание 3: Автоматизация

Напишите alerting rules для Prometheus, чтобы такое не повторилось:

# alerting_rules.yml
groups:
  - name: api_critical
    rules:
      - alert: HighLatency
        expr: ???
        for: ???
        annotations:
          summary: "???"
          runbook: "???"

Задание 4: Стоимостный анализ

Посчитайте ROI каждого действия:

ДействиеСтоимостьЭффект (снижение latency)ROI
Индексы$0-70%♾️
Вертикальное$320/мес-30%???
Кэш$180/мес-40%???

Что дальше

Вы пережили свой первый инцидент. Поздравляю!

Но это только начало. В следующих уроках мы разберем:

  • Урок 01: Метрики и компромиссы — как правильно измерять и что игнорировать
  • Урок 02: Горизонтальное масштабирование — когда добавлять серверы, а когда нет
  • Урок 03: Вертикальное масштабирование — как выжать максимум из одной машины
  • Урок 04: Кэширование — стратегии, стоимость, ROI
  • И так далее...

Но прежде чем идти дальше, убедитесь, что вы поняли:

  1. ✅ Индексы — первое, что проверяешь
  2. ✅ Мониторинг — обязательный минимум
  3. ✅ Rate limiting — защита, не зло
  4. ✅ Async — всё, что можно
  5. ✅ Стоимость — считается всегда
  6. ✅ Root cause > симптомы

Готовы к следующему уроку? Там будет теория, метрики, и глубокое погружение в каждый из аспектов, которые вы только что видели в боевых условиях.

Не готовы? Пройдите практику еще раз. Попробуйте другие сценарии. Highload — это не про знание, это про умение принимать решения под давлением.


Поздравляю! Вы прошли триггер паники. Теперь вы знаете, как выглядит реальный highload. Дальше будет проще — мы разберем каждый аспект детально, без паники.

Триггер паники: ваше приложение упало при росте в 10 раз — Архитектура высоконагруженных веб-приложений — Potapov.me