Согласованность и распределённые транзакции
Теорема CAP, модели согласованности, двух- и трёхфазный коммит, паттерны TCC и SAGA и идемпотентность распределённых операций.
8 вопросов
JuniorТеорияОчень частоЧто на самом деле утверждает теорема CAP о компромиссе в распределённой системе?
Что на самом деле утверждает теорема CAP о компромиссе в распределённой системе?
CAP говорит, что во время сетевого разбиения (partition) ты выбираешь между согласованностью (Consistency) и доступностью (Availability) — быть CP (блокировать или отказывать, оставаясь корректным) или AP (отвечать, рискуя устаревшими данными). Это поведение ВО ВРЕМЯ разбиения, а не «выбери 2 из 3»; компромисс настраивают на каждую операцию.
Типичные ошибки
- ✗Трактовать CAP как «выбери 2 из 3» в покое, а не как выбор, возникающий только во время разбиения
- ✗Называть систему CA, будто можно сохранить и C, и A, пока разбиение реально происходит
- ✗Забывать, что компромисс C-vs-A настраивают для каждой операции, а не фиксируют раз для всей системы
Уточняющие вопросы
- →Во время разбиения когда платёжный сервис осознанно выберет CP вместо AP?
- →Как модели вроде read-your-writes располагаются между строгой и конечной?
JuniorТеорияОчень частоВ чём разница между строгой и конечной согласованностью и чего стоит каждая?
В чём разница между строгой и конечной согласованностью и чего стоит каждая?
Строгая согласованность значит, что каждое чтение сразу видит последнюю запись, будто копия одна (linearizable). Конечная согласованность допускает краткое расхождение реплик, но они сходятся, когда записи прекращаются. Строгая стоит латентности и доступности, конечная даёт масштаб.
Типичные ошибки
- ✗Считать конечную согласованность «поломкой», а не осознанным разменом ради доступности и масштаба
- ✗Считать строгую согласованность бесплатной и забывать, что она стоит латентности и доступности
- ✗Путать определения — утверждать, что конечные чтения всегда видят последнюю запись
Уточняющие вопросы
- →Где на этом спектре находятся read-your-writes и monotonic reads?
- →Как теорема CAP (consistency-availability-partition) связана с этим выбором?
JuniorТеорияЧастоКак двухфазный коммит (2PC) координирует транзакцию по нескольким сервисам?
Как двухфазный коммит (2PC) координирует транзакцию по нескольким сервисам?
Координатор шлёт PREPARE каждому участнику, и каждый голосует «да» или «нет». COMMIT он шлёт, только если все согласны, иначе ABORT. Альтернатива без блокировок — паттерн SAGA: цепочка локальных транзакций со своей компенсацией.
Типичные ошибки
- ✗Думают, что 2PC коммитит без голосования — фаза
PREPAREнужна именно чтобы сперва собрать «да/нет» от всех. - ✗Считают 2PC неблокирующим — если координатор умер после
PREPARE, участники зависают, держа блокировки до его возврата. - ✗Путают
SAGAс 2PC — уSAGAнет глобального коммита, она отменяет работу компенсациями, а не откатом.
Уточняющие вопросы
- →Почему 2PC называют блокирующим протоколом и какой сбой вызывает блокировку?
- →Когда для межсервисного процесса вы выберете паттерн
SAGAвместо 2PC?
MiddleДизайнЧастоПоток заказа в e-commerce проходит через четыре сервиса со своими базами — billing списывает деньги с карты, inventory резервирует товар, orders сохраняет заказ, notifications шлёт письмо покупателю, — и каждый шаг это отдельный сетевой вызов. Поток должен оставаться согласованным без глобальной блокировки на все сервисы, а inventory часто падает посреди потока, когда товар кончился, уже после того как billing списал деньги. Вызовы могут повторяться и выполниться дважды. Спроектируйте, как держать данные корректными во всех четырёх сервисах: как моделируете каждый шаг и его откат, что происходит (и в каком порядке), когда inventory падает после успешного billing, как координируете шаги, как повторные операции остаются безопасными и как события публикуются надёжно. Какую гарантию согласованности это даёт?
Поток заказа в e-commerce проходит через четыре сервиса со своими базами — billing списывает деньги с карты, inventory резервирует товар, orders сохраняет заказ, notifications шлёт письмо покупателю, — и каждый шаг это отдельный сетевой вызов. Поток должен оставаться согласованным без глобальной блокировки на все сервисы, а inventory часто падает посреди потока, когда товар кончился, уже после того как billing списал деньги. Вызовы могут повторяться и выполниться дважды. Спроектируйте, как держать данные корректными во всех четырёх сервисах: как моделируете каждый шаг и его откат, что происходит (и в каком порядке), когда inventory падает после успешного billing, как координируете шаги, как повторные операции остаются безопасными и как события публикуются надёжно. Какую гарантию согласованности это даёт?
Смоделируйте поток как SAGA: каждый шаг (billing, затем inventory, затем orders, затем notifications) — локальная транзакция с компенсирующим действием. Если inventory падает, прогоните компенсации назад — верните оплату — и глобальной блокировки не держите. Управляйте оркестратором, делайте каждый шаг и компенсацию идемпотентными через ключ идемпотентности, события публикуйте через outbox — итог eventual consistency.
Типичные ошибки
- ✗Хвататься за two-phase commit и глобальную блокировку вместо локальных транзакций с компенсациями
- ✗Забывать, что компенсации идут назад — вернуть billing, когда inventory падает после списания
- ✗Пропускать ключи идемпотентности, и повторная компенсация возвращает деньги или товар дважды
Уточняющие вопросы
- →Оркестрация или хореография здесь, и чего каждая стоит в эксплуатации?
- →Почему паттерн outbox лучше публикации события прямо внутри той же транзакции?
MiddleТеорияИногдаПочему двухфазный коммит (2PC) называют блокирующим, если координатор умирает после фазы prepare?
Почему двухфазный коммит (2PC) называют блокирующим, если координатор умирает после фазы prepare?
Участники не могут разрешить голос сами. После PREPARE они держат блокировки в ожидании COMMIT или ABORT; если координатор умирает в этом окне, они зависают с блокировками, тормозя прочие транзакции. Координатор также ждёт самого медленного участника, и хвостовая латентность зависит от него.
Типичные ошибки
- ✗Думать, что падение координатора после PREPARE безвредно, ведь голоса уже собраны
- ✗Считать, что участники могут сами закоммитить или откатить после голоса «да»
- ✗Полагать, что 2PC неблокирующий, и игнорировать удержание блокировок весь раунд
Уточняющие вопросы
- →Как трёхфазный коммит (3PC) делает это неблокирующим и какой ценой?
- →Почему долгое удержание блокировок в 2PC усиливает конкуренцию за изоляцию транзакций?
MiddleТеорияИногдаКак работает паттерн распределённых транзакций try-confirm-cancel (TCC)?
Как работает паттерн распределённых транзакций try-confirm-cancel (TCC)?
Координатор вызывает try на каждом сервисе, чтобы зарезервировать ресурс (например, заморозить средства); если все успешны — он вызывает confirm на всех для финализации, иначе cancel на всех для освобождения. Каждый сервис предоставляет try/confirm/cancel, хранит состояние резервации и обязан быть идемпотентным, чтобы повторы были безопасны.
Типичные ошибки
- ✗Путать TCC с
2PC— у TCC нет глобальной блокировки, вместо голосования и коммита есть шаг резервации - ✗Забывать, что каждый сервис обязан хранить состояние резервации, чтобы
confirm/cancelзнали, что финализировать - ✗Пропускать идемпотентность, из-за чего повторный
confirmилиcancelприменяет изменение дважды
Уточняющие вопросы
- →Почему
try/confirm/cancelв TCC обязаны быть идемпотентными? - →Чем TCC отличается от паттерна оркестрации saga с компенсирующими транзакциями?
SeniorДизайнИногдаДля оформления заказа с оплатой и резервом товара, охватывающего сервис биллинга и сервис склада, нужно избежать блокирующих локов протокола распределённого коммита 2PC (two-phase commit). Бизнес требует, чтобы деньги и товар удерживались атомарно в момент старта оформления, чтобы никакой другой заказ не забрал удержанный товар и чтобы при частичном сбое не осталось ни списания, ни уменьшения остатка. Выберите между паттерном прикладной транзакции TCC (try-confirm-cancel) и паттерном длинной транзакции SAGA и обоснуйте, какой из них подходит под эти требования к изоляции и резервированию.
Для оформления заказа с оплатой и резервом товара, охватывающего сервис биллинга и сервис склада, нужно избежать блокирующих локов протокола распределённого коммита 2PC (two-phase commit). Бизнес требует, чтобы деньги и товар удерживались атомарно в момент старта оформления, чтобы никакой другой заказ не забрал удержанный товар и чтобы при частичном сбое не осталось ни списания, ни уменьшения остатка. Выберите между паттерном прикладной транзакции TCC (try-confirm-cancel) и паттерном длинной транзакции SAGA и обоснуйте, какой из них подходит под эти требования к изоляции и резервированию.
Бери TCC: его фаза try резервирует деньги и товар заранее (удержание), поэтому ни один параллельный заказ не видит этот товар, затем confirm финализирует резерв, а cancel его освобождает — почти изоляция за два round trip ценой того, что каждый сервис обязан идемпотентно поддерживать try/confirm/cancel. SAGA же коммитит каждый локальный шаг и затем компенсирует, открывая промежуточное состояние «списано, но не зарезервировано», которое нарушает требование удержания. Оба избегают блокирующих локов 2PC.
Типичные ошибки
- ✗Считать TCC и SAGA взаимозаменяемыми — только фаза
tryу TCC даёт предварительное удержание-резерв. - ✗Полагать, что SAGA прячет промежуточное состояние; её закоммиченные-затем-компенсируемые шаги видны снаружи.
- ✗Думать, что TCC держит локи в базе как 2PC; он держит прикладной резерв, а не блокировку строк в базе.
Уточняющие вопросы
- →Как сохранить идемпотентность
confirmиcancelв TCC при повторах? - →Что будет с SAGA, если сама компенсирующая операция упадёт на полпути?
MiddleТеорияРедкоЗачем трёхфазный коммит добавляет фазу PRE-COMMIT к 2PC (two-phase commit) и почему его редко применяют на практике?
Зачем трёхфазный коммит добавляет фазу PRE-COMMIT к 2PC (two-phase commit) и почему его редко применяют на практике?
3PC вставляет фазу PRE-COMMIT между голосованием и commit. Увидев PRE-COMMIT, участник знает, что все проголосовали «да», и может закоммитить сам, если координатор упадёт — он не блокируется на блокировках, как 2PC. Применяют редко: лишний round trip стоит латентности, а корректность держится только в синхронной сети.
Типичные ошибки
- ✗Называть 3PC бесплатной заменой 2PC, забывая, что его неблокирующая гарантия держится на синхронной сети и ломается при реальных партициях.
- ✗Утверждать, что 3PC полностью убирает блокировку, тогда как он спасает лишь от падения координатора и платит лишним round trip.
- ✗Путать PRE-COMMIT с записью данных, а не с соглашением о том, что все участники проголосовали «да».
Уточняющие вопросы
- →Почему неблокирующая гарантия 3PC рушится в асинхронной сети с партициями?
- →Как протоколы консенсуса Paxos или Raft решали бы ту же задачу атомарного коммита иначе?