Перейти к содержанию

Reliability & Failure Handling

В распределённых системах происходят частичные отказы: одна зависимость замедляется, один запрос получает timeout или один регион теряет связь, пока остальная система продолжает работать. Надёжность строится на определённом поведении при сбоях, контролируемом восстановлении и данных мониторинга, а не на предположении, что отказы редки.

Timeouts и retries

У каждого удалённого вызова должен быть timeout, рассчитанный с учётом latency budget вызывающей стороны. Без него зависшие вызовы занимают соединения, потоки, память и время пользователя. Timeout создаёт неопределённость: вызывающая сторона знает, что перестала ждать, но не знает, завершилась ли удалённая операция.

Retries помогают при временных сбоях, но также умножают нагрузку и могут повторить side effects. Используйте retry budget, ограниченное число попыток, exponential backoff и jitter. Координируйте уровни retries, чтобы клиент, API и сервис не создали retry storm.

Для одной логической мобильной операции нужно определить слой, который управляет retry policy. UI callbacks, HTTP interceptors, repositories, API gateways, backend services и durable workers не должны независимо умножать попытки. Интерактивный retry безопасного чтения, повтор mutation с idempotency key и долговечный retry после завершения мобильного процесса имеют разные ограничения; владелец зависит от операции, а не от одной универсальной реализации.

Mobile App -> API -> Payment Service -> timeout

Слепой повтор платежа может списать деньги дважды, если первый запрос завершился, но ответ потерялся. Безопасность retry зависит от семантики операции. Idempotency key позволяет сервису распознать одну логическую операцию:

POST /payments
Idempotency-Key: <stable-request-identifier>

Сервис должен атомарно связать ключ с результатом операции в подходящей области и на заданный срок; повторное использование ключа с другим содержимым запроса следует отклонять. Один заголовок сам по себе не обеспечивает идемпотентность.

Изоляция и graceful degradation

Circuit breaker прекращает вызовы зависимости после превышения порога ошибок, ждёт cooldown и разрешает ограниченные пробные запросы перед восстановлением обычного трафика:

stateDiagram-v2
    [*] --> CLOSED
    CLOSED --> OPEN: превышен порог ошибок
    OPEN --> HALF_OPEN: cooldown
    HALF_OPEN --> CLOSED: проба успешна
    HALF_OPEN --> OPEN: проба неуспешна

Он защищает ресурсы и сокращает повторные вызовы отказавшей зависимости, но требует подходящих порогов и нужен не в каждом процессе приложения. Лимиты параллелизма, bulkheads и очереди могут изолировать перегруженную зависимость от несвязанной работы.

Graceful degradation сохраняет основное поведение при отказе необязательной зависимости. Если рекомендации недоступны, продукт может продолжить показывать поиск и сохранённый контент. Fallback должен быть безопасным и честным: устаревшие цены или решения авторизации могут быть хуже явной ошибки.

Redundancy и восстановление

Redundancy устраняет single point of failure, только если реплики не подвержены одному сценарию отказа. Liveness показывает, жив ли процесс настолько, что перезапуск может помочь; readiness определяет, следует ли сейчас направлять трафик на этот экземпляр; startup checks при необходимости защищают медленную инициализацию. Состояние зависимостей тоже важно наблюдать, но readiness check не обязан синхронно проверять каждый downstream service. Если из-за отказа одной общей зависимости все upstream-экземпляры одновременно станут not ready, такая политика может усилить сбой и вызвать каскадный отказ. Replication повышает устойчивость, но может вносить lag и общие ошибки конфигурации.

Учитывайте частичный успех и неопределённый результат. Многошаговому workflow может потребоваться compensating action, reconciliation или видимый статус pending, а не иллюзия атомарности. План восстановления должен охватывать восстановление долговечных данных, обработку backlog, безопасный replay и проверку инвариантов.

Observability как часть надёжности

Logs, metrics, traces и доменные сигналы должны показывать, какая операция завершилась ошибкой, где потрачено время, помогли ли retries и что увидели пользователи. Отслеживайте перцентили latency, error rate, saturation, возраст очереди, состояние зависимостей и бизнес-результаты. Alerts должны вести к конкретному действию, а не сообщать о каждом кратком сбое.

Надёжное поведение является компромиссом между availability, корректностью, latency, стоимостью и операционной сложностью. Сначала определите важный сценарий отказа, затем выберите минимальный механизм, который его покрывает.

См. также