Конспект первой главы DDIA: reliability, fault vs failure, перцентили, tail latency amplification, SLO/SLA/SLI.

Глава 1 — Надёжность

Система надёжна, если продолжает работать корректно, даже когда что-то идёт не так.

Клеппман начинает с важной мысли: современные приложения редко упираются в мощность процессора. Узкое место — объём данных, сложность их структуры и скорость изменений. Такие системы называют data-intensive, и строятся они из типовых деталей: базы данных, кэши, поисковые индексы, очереди сообщений.

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

Три свойства data-intensive приложений

  • Reliability — система продолжает работать корректно, даже когда что-то ломается.
  • Scalability — система справляется с ростом нагрузки: больше данных, больше запросов, больше пользователей.
  • Maintainability — систему можно долго сопровождать: понимать, менять, чинить и развивать.

Fault и failure — это не одно и то же

Клеппман настаивает на чётком различении двух понятий, которые по-русски часто переводят одним словом «сбой».

fault   → отказ отдельного компонента
failure → отказ всей системы целиком

Задача надёжной системы — не предотвратить fault (это невозможно: диски умирают, сети рвутся, люди ошибаются), а не дать fault перерасти в failure.

Хорошая система умеет переживать отдельные сбои, не превращаясь в полностью нерабочую.

Пример: почему один упавший диск — это ещё не катастрофа

Представь базу данных на трёх серверах, где данные реплицируются. Ночью один из дисков выходит из строя — это fault.

  • Если система спроектирована правильно — она просто переключается на реплику, а администратор утром меняет диск. Пользователи ничего не замечают. Failure не случился.
  • Если репликации нет, а бэкапов тоже нет — утром бизнес теряет данные и простаивает. Fault превратился в failure.

Разница между этими двумя сценариями — не в том, «повезло или нет», а в том, была ли система спроектирована с учётом сбоев.

Три источника проблем

Клеппман делит всё, что может пойти не так, на три категории.

  • Аппаратные сбои — диски, память, сеть, питание. Случаются постоянно, но предсказуемо решаются резервированием: RAID, реплики, запасные серверы.
  • Программные ошибки — баги, которые проявляются сразу на всех репликах. Опасны тем, что от них не спасает дублирование железа: если код сломан, он сломан везде.
  • Человеческие ошибки — по статистике, именно люди становятся причиной большинства отказов. Опечатка в конфиге может положить весь кластер быстрее, чем откажет диск.

Отсюда практический вывод: надёжность — это не только про железо, но и про процессы. Хорошие системы проектируют так, чтобы ошибаться было безопасно: песочницы, поэтапные выкатки, быстрый откат, подробные логи.

Как измерять производительность: перцентили

Когда говорят «среднее время отклика — 200 мс», это почти всегда бесполезная цифра. Среднее скрывает выбросы: если 99% запросов выполняются за 50 мс, а 1% — за 5 секунд, среднее всё равно будет выглядеть прилично. Но именно этот 1% пользователей и будет страдать.

Поэтому на практике смотрят на перцентили:

  • p50 (медиана) — половина запросов быстрее этого значения, половина медленнее. Хорошая метрика «типичного» опыта.
  • p95 — 95% запросов быстрее. Уже начинает показывать проблемы.
  • p99 — 99% запросов быстрее. Именно здесь живут самые медленные пользователи, и именно они чаще всего уходят.
  • p999 — 0.1% самых медленных запросов. Важно для систем с высокой нагрузкой: даже редкие выбросы складываются в заметную долю общего времени.
«Хвост» распределения — самая интересная его часть. Среднее врёт, а перцентили говорят правду.

Усиление хвостовой задержки (Tail Latency Amplification)

Отдельная ловушка — когда один пользовательский запрос внутри системы вызывает несколько сервисов параллельно. Даже если каждый сервис сам по себе имеет хороший p99, вероятность, что хотя бы один из вызовов окажется медленным, резко растёт с их количеством.

Пример: пользователь открывает страницу, которая собирается из 10 внутренних сервисов. Если у каждого p99 = 1%, то вероятность, что хотя бы один попадёт в медленный хвост, уже около 10%. А если запросов 100 — почти гарантированно.

P(хотя бы один медленный) = 1 - (1 - p)^n

n = 10,  p = 1%  →  ~9.6%
n = 100, p = 1%  →  ~63%

Поэтому в реальных системах стараются:

  • ограничивать fan-out — не дёргать по 100 сервисов на запрос;
  • делать таймауты и fallback — лучше отдать частичный ответ, чем ждать;
  • изолировать медленные вызовы, чтобы они не «съедали» общий бюджет времени.

SLO, SLA и SLI

Перцентили и надёжность превращаются в конкретные цифры через три аббревиатуры, которые пришли из SRE-практики.

  • SLI (Service Level Indicator) — метрика, которую мы измеряем. Например: «доля успешных запросов», «p99 времени отклика», «доступность за месяц».
  • SLO (Service Level Objective) — внутренняя цель по этой метрике. Например: «99.9% запросов должны укладываться в 200 мс».
  • SLA (Service Level Agreement) — внешнее обещание клиентам, часто с юридическими или финансовыми последствиями за нарушение. Обычно SLA мягче, чем SLO, чтобы оставлять запас.

Разница на примере:

SLI:  p99 времени отклика = 180 мс
SLO:  p99 должен быть < 200 мс   (внутренняя цель)
SLA:  гарантируем p99 < 300 мс  (обещание клиенту)

Если система нарушает SLO, но ещё укладывается в SLA — это сигнал инженерам, что пора что-то чинить, но клиенты пока не страдают. Если нарушается SLA — начинаются разговоры про компенсации.

SLO — это то, к чему мы стремимся. SLA — это то, за что мы отвечаем.

Что запомнить

  • Надёжность — это не «отсутствие сбоев», а «умение их переживать».
  • fault — норма жизни, failure — то, чего мы стараемся избежать.
  • Смотри на перцентили, а не на среднее: правда живёт в хвосте.
  • Чем больше внутренних вызовов на запрос, тем сильнее «усиливается» хвостовая задержка.
  • SLI — измеряем, SLO — стремимся, SLA — обещаем.
  • Хорошая эксплуатация может компенсировать плохой софт, но не наоборот.