---
title: Статья 09 — Согласованность и распределённые транзакции
source: подготовлено 2026-09-17, TASK-45.49; каркас — brief.md §09; развёртка knowledge-base.md §7; источники — DDIA 2-е изд. гл. 8, 10 (TASK-45.39); отсылки — статьи 05, 07, 08, 10, 11 серии, классические разборы classic-designs.md
---

# Статья 09: согласованность и распределённые транзакции

Раздел о том, **на каком языке мы договариваемся о гарантиях данных**. Статья 05 дала механику: реплики, лаг, кворумы, failover. Статья 06 — копии в кэше. Здесь появляется то, что над этими механизмами: **модели согласованности** (какие аномалии пользователь может увидеть), **транзакции в распределённой системе** (как несколько систем пишут вместе) и **консенсус** (как система вообще может одинаково договориться о чём-то). Критерий «умею, если» из брифа: **объясняю, какую согласованность выбираю для конкретного кейса и чем плачу**.

Верхний уровень глубины для интервью — именно он заявлен: «уметь упомянуть на правильном уровне», а не прочитать мини-курс по CAP. Что это значит для нас: **не сдавать раздел лекцией, а продавать выбором**. Ответ на «какую согласованность выбираете?» строится по трём шагам: (1) назвать нужную гарантию, (2) назвать цену, (3) сказать, где явно от неё отказываемся и чем компенсируем. Формула раздела: **«кейс → гарантия → цена»**. Глубина живёт в [`../knowledge-base.md`](../knowledge-base.md) §7; полные конспекты глав — [`../materials/ddia-2ed/ch-08-transactions.md`](../materials/ddia-2ed/ch-08-transactions.md) и [`../materials/ddia-2ed/ch-10-consistency-consensus.md`](../materials/ddia-2ed/ch-10-consistency-consensus.md); эталоны, где выбор звучит целиком, — [`../classic-designs.md`](../classic-designs.md).

Место раздела в секции: на этапе 4 (детали и БД) мы выбираем модель данных и хранилище — и обязаны сказать, какую согласованность даёт выбранная схема; на этапе 6 (эксплуатация) отвечаем, что происходит при отказах нод с данными. Раздел 09 — язык для обоих. Он же пропитывает весь дизайн: мульти-ДЦ из раздела 08 почти всегда означает «между ДЦ — AP, внутри — C»; очереди из 07 — «at-least-once + идемпотентность» вместо распределённых транзакций.

## 1. Словарь: три оси, на которых стоят все гарантии

Прежде чем говорить о конкретных механизмах, три рамки, в которые укладывается всё, что дальше.

**Ось 1 — модели согласованности** (что видит читающий). От самой строгой к самой слабой: **linearizability** (система выглядит как одна копия: после завершения записи все читают новое) → **sequential consistency** (порядок операций один и тот же для всех наблюдателей, но не обязан совпадать с реальным временем) → **causal consistency** (причинно связанные операции видят все в одном порядке, параллельные — как получится) → **eventual consistency** (когда-нибудь догонит). Сессионные гарантии (read-your-writes, monotonic reads, consistent prefix) — не отдельная модель, а ослабление, которое приложение может обеспечить само; статья 05 §3 уже это разобрала — здесь хватает ссылки.

**Ось 2 — изоляция транзакций** (что делает БД внутри одной системы): read committed → snapshot isolation → serializable. Это про честность конкурентных транзакций, разбираем в §4. Часто путают с осью 1 — и это ловушка (§3): linearizability про свежесть, serializability про порядок.

**Ось 3 — атомарность многих систем** (как несколько сервисов/БД пишут «вместе»): атомарный коммит (2PC), сага, outbox (§5).

Каждая ось отвечает свой вопрос; неумение их разделять — главный источник «каши» на секции. Простой способ держать их в голове: ось 1 про **чтение**, ось 2 про **одну БД**, ось 3 про **несколько систем**.

## 2. CAP и PACELC: формулировки без мифов

CAP знают все, но на секции он часто звучит как заклинание. Правильная подача — две фразы.

**CAP буквально:** при **сетевом разделении** (partition — P случается всегда, это не опция, это сеть) система должна выбрать: либо **C**onsistency — то есть linearizable чтения/записи (все ноды видят одну свежую копию), либо **A**vailability — каждая нода продолжает принимать запросы (даже если отвечает устаревшим состоянием). Вне разделения CAP молчит — и поэтому бесполезен в обычной жизни: разделения редки (в исследованиях сетевые партиции — это малая доля инцидентов), а плата за согласованность идёт всегда.

**PACELC честнее:** **P**artition → **A**vailability vs **C**onsistency; в оставшееся время (**E**lse) **L**atency vs **C**onsistency. То есть даже когда сеть цела, мы платим **латентностью** за строгую согласованность: линейзуемость требует синхронного кворума или round-trip к одной точке, а это дополнительные ходы по сети. Причём это не инженерный выбор, а теоретический предел (Attiya–Welch): линейзуемость по своей природе стоит минимум RTT — строгая согласованность принципиально медленнее слабой, предел существует даже для RAM многоядерного CPU.

Формулировки для доски (из KB §7), которые звучат в разы большим пониманием, чем «возьмём AP»:

- **«В одном ДЦ почти всегда C»**: партиции внутри ДЦ редки и кратковременны, а за линейзуемость внутри ДЦ мы платим десятки миллисекунд — приемлемо. Цена в проде: мульти-ДЦ «active-active» с синхронной репликацией между континентами — каждая запись ждёт RTT между ДЦ (Москва–NY ~120 мс). Внутри ДЦ C дёшев, между ДЦ — дорог.
- **«Между ДЦ — вынужденно AP с eventual»**: тот же сюжет из статьи 08 §9 — между ДЦ допускаем отставание реплик (split brain лечим fencing'ом, а не синхронным кворумом). Отсюда же классика: глобальный сервис с локальными копиями данных (CDN, кэш по регионам) — это осознанное AP.
- **«Консистентность — это обычно компромисс двух осей сразу»**: внутри ДЦ выбрали C и платим латентностью; между ДЦ выбрали доступность и платим свежестью и сложностью конфликтов.

Лакмус понимания CAP/PACELC на секции: если интервьюер спрашивает «а что выберете — C или A?», ответ не должен быть «C!» — должен быть «при разделении я выбираю…, а в остальное время…; вот мой кейс». Готовая реплика: «для денежных операций — в одном ДЦ C с полусинхронной репликацией и внятным failover; для лайков — eventual + идемпотентные счётчики, пользователь простит лаг в секунды, если счётчик не капризничает каждый раз».

## 3. Linearizability vs eventual: лестница и что на ней случается

Самый частый сюжет раздела — выбор между «свежо всегда» и «свежо когда-нибудь». Чтобы выбор был осознанным, надо видеть не два конца, а лестницу с промежуточными ступенями:

| Модель | Что гарантирует | Цена | Где живёт на нашей схеме |
|--------|-----------------|------|--------------------------|
| **Linearizability** | Система как одна копия: запись завершилась → все последующие чтения видят новое значение | Синхронный кворум/одна точка записи; латентность ≥ RTT; единственная точка при шардировании | Деньги, инвентарь, лидерство, uniqueness (замок, ключ) |
| **Sequential** | Порядок операций одинаков для всех, время может врать | Дёшевле линейзуемости, но семантика «порядок есть, свежести нет» почти нигде не нужна сама по себе | Теоретическая ступень; на практике упоминается как «знаю, что есть» |
| **Causal** | Причинно связанные записи видят все в одном порядке; параллельные — в произвольном | Лаги, упорядочение по причинности (ключам/партициям) | Лента друзей: комментарий-ответ после комментария-вопроса; «что я писал, вижу в правильном порядке» |
| **Eventual** | Когда-нибудь догонит; никакой гарантии момента | Дешёвая репликация, высокая доступность; нужны сессионные гарантии отдельно | Лайки, просмотры, профили, поисковые проекции |

Важные оговорки, которые превращают перечисление в инженерное знание:

- **Eventual не значит «вечно рассинхронно»**: eventual подразумевает, что при остановке новых записей все реплики в итоге сойдутся. Это свойство, которое надо обеспечивать (read repair, anti-entropy из статьи 05 §4), а не подразумевать. Фраза: «eventual — это контракт „когда-нибудь“, но „когда“ я делаю предсказуемым: лаг в секунды, иначе пользователь видит аномалии».
- **Каждая более слабая ступень — это более сильные требования к приложению**: eventual означает, что приложение готово видеть аномалии (потерянный лайк, старая версия, откат комментария). Если на этом чтении бизнес делает решающее действие (списать, зарезервировать) — eventual недостаточен.
- **Сессионные гарантии — дешёвый способ съесть разницу между ступенями**: статья 05 §3 показывает, что read-your-writes и monotonic reads обеспечиваются маршрутизацией, а не консенсусом. Именно поэтому «лента» (из статьи 11) может честно говорить: «лента eventual, но свои посты читаю с лидера, а порядок ленты прибит к партиции пользователя — аномалии, которые пользователь может поймать, сводятся к почти нулю».
- **Версии и часы** (кратко, на уровне «упомянуть»): сравнение «кто свежее» по wall-clock — это last-write-wins, который **тихо теряет данные** (часы врут, NTP прыгает — статья 08 §2); параллельные версии детектируют **vector clocks** (счётчики по узлам — дороже по размеру), total order дают Lamport/HLC. Достаточно на секции сказать: «LWW по времени машины — знаю, что теряет данные; параллельность детектируют векторные часы; конфликт отдаём пользователю или мерджим». Это уже «правильный уровень», дальше — только если интервьюер сам полезет.

### 3.1 Вопрос-ловушка: linearizability ≠ serializability

Вопрос, который ставят кандидату, чтобы проверить, не механически ли выучены термины: **«линеаризуемость и сериализуемость — это одно и то же?»** Правильный ответ — нет:

- **Linearizability** — гарантия **свежести** одной операции: как только запись завершилась (или чтение началось после него), все последующие чтения видят новое значение. Про транзакции и их внутренний порядок ничего не говорит.
- **Serializability** — гарантия **изоляции транзакций**: результат эквивалентен какому-то последовательному (одна за другой) исполнению — какой именно порядок, не важно. Про время и свежесть не говорит: транзакции могут быть «свежими» и при этом давними.
- Вместе — **strict serializability** (Spanner, FoundationDB): и изоляция, и свежесть — транзакции имеют общий порядок, согласованный с реальным временем.

Следствие, которое любит интервьюер: **кворумы W+R>N НЕ дают linearizability** (обсуждено в статье 05 §4): они дают свежесть после завершённой записи, но параллельные записи и read repair не сериализуют. А single-leader репликация даёт линейзуемость **только пока лидер настоящий** (и жив) — как только включается failover со split brain, гарантия исчезает. Из этого следует, где linearizability реально нужна: лок/выборы лидера, uniqueness-ограничения, «cross-channel timing» (запись в одном канале, проверка в другом). А где не нужна: лайки, ленты, кэши — там важны дешевизна и доступность, а не общий порядок, и платить RTT за свежесть каждой записи нечем.

## 4. Изоляция транзакций: второй эшелон глубины

Уровень, на который уходит интервьюер, когда спрашивает «а что у вас внутри БД?» или «а как вы решаете гонки?». Не обязателен для каждого дизайна, но обязателен как словарь — чтобы не называть snapshot-изоляцию «сериализуемостью». Лестница:

- **Read committed** — минимум, обещают почти все СУБД: нет dirty read (не видим незакоммиченное) и dirty write (не перезаписываем чужое незакоммиченное). Реализация: **MVCC** (читатели видят последнюю закоммиченную версию) + row-lock на запись до коммита.
- **Snapshot isolation** (в Postgres фактически это repeatable read) — транзакция читает **согласованный снапшот на момент старта**: все чтения внутри транзакции видят одну версию мира, даже если другие транзакции параллельно коммитят. Дёшево, потому что ничего не блокирует читателей.
- **Serializable** — самый строгий: результат как при каком-то последовательном исполнении. Двумя путями: **2PL** (блокировки до конца транзакции — просто, но дедлоки и низкая конкурентность) или **SSI** (serializable snapshot isolation — MVCC + отслеживание опасных rw-антизависимостей, abort вместо блокировок; современный стандарт — Postgres 9.1+, CockroachDB, TiDB, FoundationDB).

Три гонки — называть по имени и с лечением:

| Гонка | Что происходит | Лечение |
|-------|----------------|---------|
| **Lost update** | Две транзакции делают read-modify-write одного счётчика, обе записывают, одна перезаписывает другую | Атомарная запись (`UPDATE ... SET x = x + 1`), `SELECT ... FOR UPDATE`, **CAS** (`UPDATE ... WHERE old_value`), автоматический конфликт-детект в MVCC |
| **Write skew** | Две транзакции читают **пересекающийся** набор строк и пишут **разные** строки — инвариант сломан (оба врача сняли себя с дежурства, думая, что второй останется) | Serializable, материализация конфликта (общая строка-счётчик), constraint (как правило — только serializable честно решает) |
| **Phantom** | Предикатный запрос возвращает разные строки внутри транзакции (кто-то вставил подходящую строку) | Snapshot закрывает фантомы чтений; для serializable — предикатные/range-lock или SSI |

Формулировка для секции: «дефолт — snapshot isolation: дёшево и честно для чтения; гонки счётчиков — атомарной записью/CAS; там, где инвариант бизнеса (деньги, лимиты, дежурства), а не просто арифметика — serializable через SSI или материализованный конфликт». Уровень изоляции — только про одну БД: как только операция затрагивает несколько систем, мы переходим в раздел 5.

## 5. Распределённые транзакции: 2PC, сага, outbox

Вопрос «как атомарно записать в несколько систем?» имеет три ответа — и порядок выбора на секции примерно такой: **сначала outbox (двух систем не атомарно — делаем одну), потом сага (атомарность не нужна — нужна согласованность шагов), 2PC — избегаем умышленно и говорим почему**.

### 5.1 2PC: механизм существует, но в high-load мы его избегаем

**Two-phase commit**: координатор спрашивает у всех участников **prepare** (заморозились, гарантируем коммит), потом **commit** всем. Красиво в теории — атомарность в распределённой системе достигнута. На практике три причины, почему на секции правильнее сказать «избегаю»:

1. **In-doubt транзакции**: участник, получивший prepare и упавший до commit, **держит лока** (а вместе с ними читатели и конкурентные писатели) — «зависшие» транзакции разбирают вручную, когда координатор придёт в себя. Это реальность XA в проде.
2. **Координатор — SPOF и точка синхронизации**: упал координатор — вся транзакция стоит; «heuristic decisions» (участник сам решил, коммитить или откатывать) **ломают атомарность тихо** — ремонт дороже, чем отказ.
3. **Цена блокировок на весь срок транзакции** — в распределённой транзакции он непредсказуем (сеть, очередь, паузы GC — статья 08 §2), а блокировки держатся до конца.

Об этом есть честная, готовая фраза: **«2PC — это атомарность ценой точки отказа и блокировок; в высоконагруженных системах я избегаю 2PC и применяю саги»** — и сразу перевернуть в конструктив: «вместо атомарности многих систем — одна атомарность (БД + outbox), остальное — компенсации».

### 5.2 Сага: не атомарно, но согласованно

**Saga** — последовательность **локальных транзакций** (каждая в своей системе), где на каждый шаг есть **компенсирующее действие**: order → reserve → charge → ship; откат — cancel → refund → restock. Ключевое отличие от 2PC: атомарности **нет** — на короткое время состояние «частично выполнено» видно наружу, но благодаря компенсациям в итоге система приходит в согласованное состояние. Цена — **компенсации надо проектировать с первого дня** (не бывает «отменить резерв» задним числом) и **идемпотентность повторных шагов** (ретраи в саге — норма).

Два способа управлять сагой — называть оба и выбирать по кейсу:

- **Оркестрация**: центральный координатор (сага-менеджер) вызывает шаги по очереди и компенсирует при отказе. Плюс — всё видно в одном месте, простое мышление о бизнес-процессе. Минус — координатор сам становится узким местом/SPOF (но это stateless-оркестратор с перезапуском процесса — дешевле, чем 2PC-координатор, потому что не держит блокировки).
- **Хореография**: шаги связаны **событиями** через шину: «OrderCreated → PaymentService резервирует → ReservedEvent → ShipmentService…». Плюс — нет центральной точки, сервисы слабо связаны. Минус — поток процесса размазан по событиям, читать и отлаживать его сложнее, компенсации тоже события. Для простых цепочек (2–3 шага) хореография избыточна.

Уровень для интервью — не «как реализовать сагу», а «**когда нужна сага и чем плачу**»: «мульти-сервисная запись без атомарности: шаги локально транзакционны, откат — компенсации, повтор — идемпотентность; оркестрация для управляемого процесса, хореография для слабосвязанных сервисов».

### 5.3 Transactional outbox: стандартный ответ вместо двойной записи

Механизм из статьи 07 §10 — здесь он превращается в ответ на 2PC-соблазн: «БД записала, событие потерялось» решается не распределённой транзакцией, а композицией. Событие пишется **в ту же транзакцию, что и данные** (outbox-таблица), публикатор (воркер или CDC — Debezium) доставляет в Kafka и помечает прочитанным. Атомарность не между системами, а в одной БД — чего достаточно, потому что **точно-once между системами не существует**: есть at-least-once + дедупликация.

Фраза, которой закрывается любой вопрос «а как две системы пишут вместе?»: **«двойную запись не делаю: данные и событие — в одной транзакции через outbox; доставка — at-least-once + idempotency key; если нужен согласованный бизнес-процесс из нескольких шагов — сага с компенсациями; 2PC с XA избегаю»**. Рядом — обязательный пас в статью 07 §5: идемпотентность потребителя (таблица обработанных message-ID в локальной транзакции) — это и есть «exactly-once без распределённых транзакций», так делает Kafka Streams.

Упомянуть одним словом (по запросу): **NewSQL** (Spanner, CockroachDB, TiDB, FoundationDB) — системы, которые дают внутренние распределённые транзакции через реплицированный координатор на консенсусе — без XA-болей, но с ценой в консенсусе на каждый коммит и ограничением географии (Spanner — синхронные реплики в пределах региона/континента). На секции: «если БД сама умеет распределённые транзакции (NewSQL) — атомарность многошардовая возможна, но дорогая; выбираю её только там, где бизнес реально требует атомарности шире одной шарда».

## 6. Консенсус: как несколько нод договариваются одинаково

Консенсус — фундамент, о котором на секции достаточно сказать правильные вещи на **правильном уровне**: зачем, что гарантирует, как устроен, какие сервисы дают его готовым. Не больше.

**Зачем.** Консенсус — класс эквивалентных задач: выбор единственного значения, линейзуемый CAS, **shared log** (total order broadcast — «все ноды видят одно и то же в одном порядке»), atomic commit, fetch-and-add. На практике системы дают shared log, а поверх — state machine replication (все реплики применяют одну последовательность команд → одинаковое состояние). Из этого вырастает: **линейзуемые транзакции** (etcd-подход), **fencing tokens** (эпоха), **leader election**, координация вообще. Для секции достаточно связи: «консенсус необходим там, где нужен общий порядок или общее единственное значение — выборы лидера из 05 §5, fencing из 08 §8, разделяемый лок».

**Свойства.** Три safety-свойства держатся всегда: **agreement** (все договорились об одном значении), **integrity** (дважды не договариваются), **validity** (значение не выдумано). Плюс **termination** (liveness) — «система в итоге договорится», и вот оно — **требует живого большинства**: 3 ноды терпят 1 отказ, 5 нод — 2. Это та самая арифметика majority из статьи 08 §9, здесь она называется своим именем.

**Механика в одну фразу.** Raft: **term/эпоха** — уникальный лидер эпохи; **два голосования** — выборы лидера и коммит записи — на **пересекающихся кворумах** (разные большинства в разные моменты обязательно пересекаются); каждая запись синхронно реплицируется в кворум, поэтому не теряется. Отличие от 2PC в одной фразе: **«2PC требует согласия всех (и умирает от одного отказа), консенсус — кворума; поэтому консенсус даёт и отказоустойчивость, и линейзуемость»**. Цена: консенсус синхронен (каждая запись — round-trip к кворуму), таймауты чувствительны к вариативности сети, больше нод — медленнее кворум.

**Практика.** Никто не пишет свой Raft на секции: **координационные сервисы** — ZooKeeper, etcd, Consul (по образцу Chubby) — аутсорсят консенсус на 3–5 нод для тысяч шардов. Что они дают из коробки: **locks/leases + fencing tokens** (таймаут-аренды, защита от зомби-лидера — статья 08 §8), failure detection (сессии, ephemeral nodes), change notifications, service discovery (который, кстати, **кэшировать**, а не читать синхронно — линейзуемость для discovery не нужна, KB §13.5). Классика применения в наших схемах: лидерство фоновых воркеров (fan-out ленты, пересчёт кэша), дедуп критичных операций, fencing при failover. Распределённые локи: Redis/Redlock — быстро, но компромисс по надёжности; ZK/etcd — кворумные и надёжные, но дороже; при локах всегда проговаривать **fencing token** и «лок ≠ транзакция».

**Альтернативы консенсусу** — назвать, чтобы показать, что мы умеем выбирать: **single-leader репликация даёт линейзуемость, пока лидер жив и настоящий** (дешевле консенсуса — несинхронный кворум не нужен; но failover уже нелинейзуем — статья 05 §5); **кворумы W+R>N дают свежесть, не linearizability** (статья 05 §4, доказательство — DDIA гл. 10). Вывод-фраза: «консенсус — последняя инстанция: беру, когда без общего порядка никак (выборы, fencing, атомарный CAS, общий лог); в остальном хватает single-leader и кворума с известными оговорками».

## 7. Мини-таблица: кейс → уровень согласованности → чем плачу

Ядро раздела — критерий брифа. Таблица — не «наизусть», а **мысленный шаблон**: для любого кейса провести строку — что гарантирую, каким механизмом, чем плачу. Примеры разобраны по эталонам (§8) и по типам данных секции.

| Кейс | Уровень согласованности | Механизм | Чем плачу |
|------|-------------------------|----------|-----------|
| **Деньги: баланс, списание, перевод** | Linearizable на операцию + serializable в БД | Одна БД с лидером (single-writer), snapshot/SSI, идемпотентность (idempotency key для платёжных вызовов) | Латентность записи, одна точка записи на шард; мульти-ДЦ — только active–passive (запись живёт в одном ДЦ) |
| **Инвентарь/лимиты (бронь, остатки)** | Атомарная запись с условием (CAS) | `UPDATE ... SET n = n − 1 WHERE n > 0`, `SELECT ... FOR UPDATE` | Конкурентность: конфликты → ретраи и UX «попробуйте ещё раз» |
| **Лента (посты друзей)** | Eventual + причинный порядок по подпискам; свои посты — read-your-writes | Async-репликация + маршрутизация «своё с лидера» (05 §3) | Лаг в секунды; «лента на момент открытия» — пользователь прощает |
| **Счётчики (лайки, просмотры, followers)** | Eventual, в итоге догонит; допускается временный рассинхрон | Async-инкремент, агрегация в фоне (таблица счётчиков или агрегатор на Kafka, 07), идемпотентность по (user, post) | Счётчик «врёт» в моменте; повторы гасятся дедуп-ключом |
| **Профиль пользователя (имя, аватар, настройки)** | Read-your-writes своей записи, остальным eventual | Свои данные — с лидера; чужие — реплики/кэш с TTL | Латентность свежести чужих профилей; кэш-инвалидация |
| **Поисковый индекс** | Eventual, лаг минуты; консистентность индекса не требуется | CDC/проекция (07 §10) | Индекс отстаёт; search работает на «почти свежих» данных — норма |
| **Уникальность: username, short link, order id** | Linearizable (fetch-and-add) — уникальный ключ обязан быть уникален всегда | Single-source: лидер/последовательность в БД, уникальный индекс, либо консенсус (ZK/etcd) | Централизация или консенсус; распределённые ID (снежинка) — только если уникальность без порядка (статья 10) |
| **Выборы лидера / лидерство фонового воркера** | Strong (линейзуемый CAS/выборы) + fencing | ZK/etcd: ephemeral-узел/лидер-эпоха; fencing token при каждом действии | Внешняя зависимость: кластер ZK/etcd из 3–5 нод и его латентность на каждое действие |
| **Сессия пользователя/кэш** | Устаревшая версия допустима — eventual с TTL | Кэш + TTL + инвалидация (06) | Промах = лишний round-trip, а не ошибка |
| **Аналитика/метрики** | Eventual, потеря части допустима (best-effort) | Очередь + батчи (07), потеря при переполнении чистая | Минус точности метрик |

Правило чтения таблицы: строка означает **осознанный выбор**, а не «вот это всегда так». Изменение кейса меняет строку: если бизнесу нужна мгновенная глобальная консистентность счётчиков — строка «Счётчики» переезжает на сильный уровень, и цена вырастает на порядок. Три аксиомы, которые стоят за таблицей:

1. **Слабее согласованность — шире класс аномалий, которые видит пользователь; выбирать её можно только с пониманием, какие именно аномалии допустимы.**
2. **Каждая сильная гарантия — это где-то больше латентности/меньше доступности (PACELC) — называть цену всегда.**
3. **Идемпотентность — общая валюта**: в каждой строке, где появляются ретраи и повторы (платёж, лайк, бронь), идемпотентный приём повторов дешевле, чем linearizability ради отсутствия повторов.

## 8. Прикладное: как это звучит в эталонах

**Лента новостей (главный билет — [`../classic-designs.md`](../classic-designs.md) §2).** Классика «какую согласованность выбрали»: свой пост — линеаризуемая запись + read-your-writes (виден сразу — читаем с лидера), ленты подписчиков — **eventual с лагом в секунды** через fan-out в асинхронные проекции; счётчики лайков — eventual с дедупом; посты селебрити — fan-out on read. При сбое — деградация «лента стареет» (08 §7), а не строгая запись. Цена названа явно: «пользователь не видит пост друга секунду-две — терпимо; видит вечно исчезающие счётчики — нет, поэтому лайки идемпотентны». Это и есть ответ на «какую согласованность выбираете».

**Мессенджер (§3).** Порядок сообщений — фактически **causal по переписке** (порядок в пределах чата): чат прибит к одной партиции по chat_id, порядок — порядок в партиции, по построению; офлайн-доставка — eventual-проекция. Уникальность message id — без консенсуса (snowflake — статья 10). Отдельная нота: «порядок сообщений — это не linearizability, а согласованность в пределах беседы; глобального порядка нет, и он не нужен».

**URL shortener (эталон Яндекса, §1).** Чтение доминирует, ничего строгого нет: KV-зеркало + кэш — eventual (статья 11), запись короткой ссылки — попадает в БД (leader) и проекцией в KV; уникальность короткого ключа обязана быть — поэтому генерация ключа на центральном сервисе/последовательности с уникальным индексом (линеаризуемо для ключа, остальное eventual). Согласованность «стоит» только на ключе — и это осознанный размен: «99 % запросов — чтение; свежесть ключа нужна только через секунду после создания — её и гарантируем».

**Медиа-хостинг (§4).** Метаданные записи — one leader БД; сам файл в S3, его «последняя версия» — eventual по CDN, и это единственный допустимый режим мульти-регион; подтверждение «файл загружен» — по записи в БД, а не по немедленной доступности с CDN. Никаких распределённых транзакций в пайплайне: outbox для событий «upload → transcode → publish», сага для компенсаций при падении транскодера (07 §10/08 §12).

Сквозной приём — **после выбора согласованности почти всегда задать второй вопрос себе: «а что видит пользователь, если гарантия не выполнилась?»** Если ответ «ничего страшного» — выбор правильный; если «баг» — гарантия сильнее или компенсация.

## 9. Ошибки этого раздела

| Ошибка | Как выглядит | Противоядие |
|--------|--------------|-------------|
| CAP как заклинание | «Возьмём AP» без объяснения, в каком разделении и зачем | CAP — только при разделении; PACELC честнее; формула «кейс → гарантия → цена» (§2) |
| Не названа цена | «Нужна strong consistency» — а платить нечем | Цена всегда: латентность/блокировки/меньше доступности/дороже инфраструктура (§2, §7) |
| Linearizability = serializability | Слил два термина в «это когда всё сразу свежее и правильно» | Развести: свежесть vs порядок; strict serializability — оба (§3.1) |
| «У нас eventual» как флаг | Куда ни ткни — «eventual», без выбора кейсов | Таблица §7: eventual — осознанное решение для конкретного типа данных с перечислением аномалий |
| 2PC «по умолчанию» | «Запишем атомарно через 2PC» на эталонном дизайне | Избегаю + три причины; конструктив — outbox + сага (§5.1–5.3) |
| Сага без компенсаций | «Сделаем сагу» — а компенсации не спроектированы | Компенсирующие действия — часть дизайна с первого дня; идемпотентность шагов (§5.2) |
| Кворум = linearizability | «W+R>N — значит сильная согласованность» | Кворум даёт свежесть; linearizability — консенсус/single-leader (§6, 05 §4) |
| Консенсус = «сложно, не надо» | Никогда не упоминается, хотя выборы лидера есть в схеме | Назвать, где консенсус в схеме фактически: failover, fencing, лидерство воркеров (§6) |
| «Поставим свой Raft» | Рисуем свой консенсус в дизайне ленты | Координационные сервисы (ZK/etcd) — готовый консенсус, не изобретаем (§6) |
| Без сессионных гарантий | Eventual, а пользователь видит «потерянный» свой пост | Своё — с лидера, монотонные чтения маршрутизацией (05 §3) — дёшево, а эффект велик |
| LWW по часам | «Свежее по таймстампу» — и тихая потеря данных | Vector clocks или отказ от параллельной записи (§3) |

## 10. Что назвать на секции (чек-лист раздела)

Обязательные фразы и действия. Полный чек-лист — [`../methodology.md`](../methodology.md) §7; канон — [`../knowledge-base.md`](../knowledge-base.md) §7.

**На этапе 1 (требования):** из нефункциональных требований вытащить консистентность: «какую свежесть допускает пользователь?» — это часть требований, а не наш вкус. Зафиксировать на доске рядом с доступностью и latency-SLO.

**На этапе 4 (детали + БД):**
- Назвать выбор согласованности **явно с ценой** для каждой таблицы/проекции: «посты линейзуемо (свой — с лидера), лента eventual — лаг секунды, счётчики eventual + идемпотентность». Ссылаться на таблицу §7.
- Если в схеме есть асинхронная проекция/событие — сказать «outbox в одной транзакции с данными, доставка at-least-once, потребитель идемпотентен» (§5.3, подробно 07).
- Если несколько систем пишут процесс — «сага, шаги локально транзакционны, компенсации спроектированы, оркестрация» (§5.2).
- Изоляция: «snapshot по умолчанию; счётчики — атомарной записью; инварианты — serializable/SSI» (§4).

**На этапе 6 (эксплуатация/отказы):**
- Выборы и fencing: «детект кворумом → выборы самой свежей реплики → fencing-токен от зомби» (05 §5/08 §8) — и назвать консенсус под этим: «перевыборы лидера — это консенсус на ZK/etcd, эпоха — это fencing».
- Мульти-ДЦ: «внутри ДЦ C, между ДЦ — AP с eventual; синхронная запись между континентами недопустима — латентность» (§2, 08 §9).
- На «кворумы дают strong» — поправить: «свежесть чтения дают, линейзуемость нет — там консенсус/single-leader» (§6).

**Сквозные формулы раздела:**
- «Кейс → гарантия → цена: называю уровень согласованности, механизм и чем плачу в каждом узле».
- «2PC избегаю: точка блокировки и отказа; вместо атомарности многих систем — одна атомарность (БД + outbox), остальное — сага с компенсациями и идемпотентностью».
- «Linearizability — свежесть; serializability — порядок; строгие обе гарантии только вместе (strict serializable)».
- «Консенсус — там, где нужен общий порядок: выборы, fencing, атомарный CAS; всё остальное — single-leader + кворум с известными оговорками».
- «Между ДЦ — AP; внутри ДЦ — C; за любую C платим латентностью (PACELC)».

## 11. Самопроверка «умею, если»

Критерий раздела из брифа: **объясняю, какую согласованность выбираю для конкретного кейса и чем плачу**. Проверка с закрытыми материалами: взять три кейса из таблицы §7 (деньги, лента, уникальность ID) и для каждого вслух провести формулу «гарантия → механизм → цена» за 60 секунд; затем ответить на три зонда: (1) «чем linearizability отличается от serializability?», (2) «почему не 2PC?», (3) «где у вас консенсус в схеме?». Маркеры готовности: (1) на «какую согласованность выберете?» вперёд вылетает строка «кейс → гарантия → цена», а не название системы; (2) определение PACELC произносится автоматически вместе с ценой; (3) анкета «а что видит пользователь при рассинхроне?» — обычный ход, а не сюрприз; (4) опасные места (2PC, LWW, кворум ≠ linearizability) называются предупреждающе, без подсказки. Протокол тренировок и рубрика 0–3 — [`../methodology.md`](../methodology.md) §6; сверка с эталонами — [`../classic-designs.md`](../classic-designs.md) §1–4. Если таблица §7 «проходит» по любым трём кейсам за минуту каждый и разговор про согласованность всегда заканчивается ценой — раздел готов.

## Связанные документы

- [`00-overview.md`](00-overview.md) — обзорная статья по всем 12 разделам брифа (TASK-45.40); §09 — карта раздела
- [`01-interview-framework.md`](01-interview-framework.md) — фиксация требований (свежесть как NFR) на этапе 1; возврат к листу на этапе 6
- [`02-estimations.md`](02-estimations.md) — латентности для оценки цены согласованности (RTT, меж-ДЦ)
- [`05-replication-sharding.md`](05-replication-sharding.md) — механика: лог, sync/async/semi-sync, replication lag и сессионные гарантии (§3), кворумы ≠ linearizability (§4), failover/выборы/fencing (§5)
- [`07-queues-streams.md`](07-queues-streams.md) — outbox/CDC (§10), семантики доставки и идемпотентность (§5, §7)
- [`08-fault-tolerance.md`](08-fault-tolerance.md) — отказы нод с данными, fencing, majority-коммит и мульти-ДЦ (§8–9)
- [`11-reference-designs.md`](11-reference-designs.md) — эталоны, где выбор согласованности звучит целиком (лента §3, мессенджер §4, медиа §5, shortener §2)
- [`../brief.md`](../brief.md) — бриф: раздел 09 с критерием «умею, если»
- [`knowledge-base.md`](../knowledge-base.md) — §7 консистентность и распределённые транзакции (CAP/PACELC, модели, изоляции, 2PC/saga/outbox, консенсус, часы), §13.5 распределённые локи
- [`methodology.md`](../methodology.md) — §4 топ-10 ошибок, §6 протокол тренировок, §7 карточка на секцию
- [`classic-designs.md`](../classic-designs.md) — §1 shortener, §2 лента, §3 мессенджер, §4 медиа
- [`materials/ddia-2ed/ch-08-transactions.md`](../materials/ddia-2ed/ch-08-transactions.md) — источник: изоляции, гонки, 2PC, NewSQL, zero-2PC (TASK-45.39)
- [`materials/ddia-2ed/ch-10-consistency-consensus.md`](../materials/ddia-2ed/ch-10-consistency-consensus.md) — источник: linearizability vs serializability, CAP/PACELC, консенсус, координационные сервисы, fencing (TASK-45.39)
- Соседние статьи: `08-fault-tolerance.md` (отказы и fencing — переиспользует язык консенсуса) · `10-components-patterns.md` (распределённая генерация ID — обходит консенсус) · `12-operations-observability.md` (метрики рассинхрона: replication lag, лаг проекций)