Грациозная деградация
1) Суть подхода
Грациозная деградация — это управляемый переход системы в более простой, но полезный режим при нехватке ресурсов, сбоях зависимостей или пиках нагрузки. Цель — сохранить ядро ценности для пользователя и устойчивость платформы, пожертвовав второстепенными возможностями и качеством.
Ключевые свойства:- Предсказуемость: заранее определенные сценарии и «лестницы» деградации.
- Ограничение радиуса поражения: изоляция фич и зависимостей.
- Наблюдаемость: метрики, логи и трассировки «какой уровень деградации активен и почему».
- Обратимость: быстрый возврат к норме.
2) Принципы и границы
1. Сохранить главное: свой основной SLA/SLO (напр., «покупка», «логин», «поиск») — приоритет выше второстепенных (аватары, рекомендации, анимации).
2. Fail-open vs fail-closed:- Безопасность, платежи, права — fail-closed (лучше отказ, чем нарушение).
- Кешируемый контент, подсказки, аватары — fail-open с фолбэком.
- 3. Временные бюджеты: таймауты сверху вниз (клиент < шлюз < сервис). По истечении — деградация вместо ретраев до бесконечности.
- 4. Контроль стоимости: деградация должна снижать потребление CPU/IO/сети, а не просто «прятать» ошибки.
3) Уровни деградации
3.1 Клиент/UX
Skeletons/плейсхолдеры и «ленивая» подгрузка второстепенных виджетов.
Partial UI: критичные блоки грузятся, вторичные — скрываются/упрощаются.
Кэш на стороне клиента: last-known-good (LKG) с пометкой «данные могли устареть».
Оффлайн-режим: очередь команд с повтором позже (идемпотентность!).
3.2 Edge/CDN/WAF/API-шлюз
stale-while-revalidate: отдаем кеш, фоном обновляем.
Rate limiting & load shedding: при перегрузке сбрасываем фоновый/анонимный трафик.
Geofence/взвешенный роутинг: трафик уводится в ближайший здоровый регион.
3.3 Сервисный слой
Partial response: возвращаем часть данных + `warnings`.
Read-only режим: временный запрет мутаций (флаги).
Brownout: временное отключение ресурсоемких фич (рекомендации, обогащение).
Adaptive concurrency: динамически уменьшаем параллелизм.
3.4 Данные/стриминг
Кэш как источник истины с TTL (временно): «лучше приблизительно, чем никак».
Пониженная точность моделей/алгоритмов (fast path vs accurate path).
Defer/queue: перенос тяжелых задач в фон (outbox / job queue).
Приоритетные очереди: критичные события — в отдельном классе.
4) «Лестницы» деградации (playbooks)
Пример для поискового API:- L0 (норма) → L1: скрыть персонализацию и баннеры → L2: отключить синонимы/фаззи-поиск → L3: лимитировать размер ответа и таймаут до 300 мс → L4: отдавать результаты из кеша 5 мин → L5: «read-only & cached only» + очередь запросов на перерасчет.
- Триггеры: перегрузка CPU>85% p95>целевого, ошибки>порога, лаг Kafka>порога, флап зависимостей.
- Действия: включить флаг X, снизить concurrency до N, переключить источник Y на кеш.
- Критерии выхода: 10 минут зеленых метрик, запас по ресурсам.
5) Политики принятия решений
5.1 Ошибочный бюджет и SLO
Используйте error-budget burn rate как триггер brownout/шеддинга.
Политика: «если burn-rate > 4× в течение 15 мин — включить L2 деградации».
5.2 Admission control
Ограничиваем входящий RPS на критичных путях, чтобы гарантировать p99 и не допустить коллапса очередей.
5.3 Приоритизация
Классы: interactive > system > background.
Пер-тенант приоритеты (Gold/Silver/Bronze) и справедливость (fair share).
6) Паттерны и реализации
6.1 Load shedding (серверный)
Сбрасывайте запросы до того, как они займут все ресурсы.
Возвращайте `429`/`503` с `Retry-After` и объяснением политики (для клиентов).
Envoy (adaptive concurrency + circuit breaking)
yaml typed_extension_protocol_options:
envoy. filters. http. adaptive_concurrency:
"@type": type. googleapis. com/envoy. extensions. filters. http. adaptive_concurrency. v3. AdaptiveConcurrency gradient_controller_config:
sample_aggregate_percentile: 90 circuit_breakers:
thresholds:
- max_requests: 2000 max_pending_requests: 500 max_connections: 1000
6.2 Brownout (временное упрощение)
Идея: снижать «яркость» (стоимость) фич, когда ресурсы на исходе.
kotlin class Brownout(val level: Int) { // 0..3 fun recommendationsEnabled() = level < 2 fun imagesQuality() = if (level >= 2) "low" else "high"
fun timeoutMs() = if (level >= 1) 150 else 300
}
6.3 Partial response и предупреждения
Поле `warnings`/`degradation` в ответе:json
{
"items": [...],
"degradation": {
"level": 2,
"applied": ["cache_only", "no_personalization"],
"expiresAt": "2025-10-31T14:20:00Z"
}
}
6.4 Stale-while-revalidate на краю (Nginx)
nginx proxy_cache_valid 200 10m;
proxy_cache_use_stale error timeout http_500 http_502 http_504 updating;
proxy_cache_background_update on;
6.5 Read-only переключатель (Kubernetes + флаг)
yaml apiVersion: v1 kind: ConfigMap data:
MODE: "read_only"
The code should check MODE and block mutations with a friendly message.
6.6 Kafka: backpressure и классы очередей
Переключите heavy-консюмеров в меньший `max.poll.records`, ограничьте продюсерские batch-и.
Разделите «критичные» и «bulk» события по топикам/квотам.
6.7 UI: graceful fallback
Прячьте «тяжелые» виджеты, показывайте кеш/скелетон и четко маркируйте устаревшие данные.
7) Конфигурационные примеры
7.1 Istio: outlier + пулы приоритета
yaml outlierDetection:
consecutive5xx: 5 interval: 10s baseEjectionTime: 30s maxEjectionPercent: 50
7.2 Nginx: фоновый трафик под нож первым
nginx map $http_x_priority $bucket { default low; high high; }
limit_req_zone $binary_remote_addr zone=perip:10m rate=20r/s;
limit_req_status 429;
server {
location /api/critical/ { limit_req zone=perip burst=40 nodelay; }
location /api/background/ {
limit_req zone = perip burst = 5 nodelay; # stricter
}
}
7.3 Feature flags / kill-switches
Храните в динамической конфигурации (ConfigMap/Consul), обновление без релиза.
Разделяйте пер-фичу и глобальные флаги, логируйте активации.
8) Наблюдаемость
8.1 Метрики
`degradation_level{service}` — текущий уровень.
`shed_requests_total{route,reason}` — сколько сброшено и почему.
`stale_responses_total` — сколько выдано кеша.
`read_only_mode_seconds_total`.
`brownout_activations_total{feature}`.
Ошибочный бюджет: burn-rate, доля нарушений SLO.
8.2 Трейсинг
Атрибуты спанов: `degraded=true`, `level=2`, `reason=upstream_timeout`.
Линки между ретраями/hedged-запросами, чтобы видеть вклад в хвосты.
8.3 Логи/алерты
События переключений уровней деградации с причинами и владельцем изменения.
Алерты на «залипание» уровня (деградация держится слишком долго).
9) Управление рисками и безопасность
Не деградируйте аутентификацию/авторизацию/целостность данных: лучше отказ.
Маскирование PII сохраняется в любых режимах.
Финансы/платежи: только идемпотентные операции, строгие таймауты и откаты; при сомнениях — read-only/hold.
10) Анти-паттерны
Тихая деградация без подсказки пользователю и без телеметрии.
Ретрай-штормы вместо load shedding и коротких таймаутов.
Глобальные «рубильники» без сегментации — огромный blast radius.
Смешивание prod и «облегченных» путей в одном кэше/очереди.
Вечная деградация: brownout как «новая норма», забытые критерии выхода.
Stale-write: попытки записывать на основе устаревших данных.
11) Чек-лист внедрения
- Определены ядро ценности и критичные пользовательские сценарии.
- Составлены лестницы деградации по сервисам/домейнам с триггерами и выходами.
- Введены таймауты/ограничения и server-side load shedding.
- Настроены rate limits и приоритетные классы трафика.
- Реализованы partial response, read-only, stale-while-revalidate.
- Интегрированы feature flags/kill-switches с аудитом.
- Метрики/трейсинг/алерты для уровней деградации и причин.
- Регулярные game day учения с симуляцией перегрузки/сбоев.
- Документированы SLO и политика error-budget → деградация.
12) FAQ
Q: Когда выбирать brownout, а когда — shedding?
A: Если цель — снизить стоимость запросов без отказов — brownout. Если цель — защитить систему, когда даже упрощение не помогает — shedding входа.
Q: Сообщать ли пользователю о деградации?
A: Для критичных сценариев — да (бейдж «ограниченный режим»). Прозрачность снижает поддержку и недовольство.
Q: Можно ли кэш сделать источником истины?
A: Временно — да, при явных SLA и метках устаревания. Для мутаций — запрещено.
Q: Как не «сломанными» сделать ретраи?
A: Короткие таймауты, экспоненциальный backoff с джиттером, идемпотентность и лимит попыток; ретраит только безопасные операции.
13) Итоги
Грациозная деградация — это архитектурный контракт и набор управляемых режимов работы, включаемых по сигналам метрик и ошибочного бюджета. Правильно спроектированные лестницы, строгие таймауты и шеддинг, кеш-фолбэки и brownout, плюс мощная наблюдаемость — и ваша платформа остается полезной и экономной даже в шторм.