Команда предлагает использовать двухфазный коммит между микросервисами для атомарного изменения данных. Какой главный компромисс такой интеграции должен оценить архитектор?
Двухфазный коммит обеспечивает атомарность распределённой операции, но ценой сильной временной и операционной связанности сервисов. Участники должны удерживать подготовленные изменения до финального решения координатора, поэтому сбой координатора или сети может блокировать ресурсы и снижать доступность системы.
Для микросервисов обычно предпочитают локальные транзакции в каждом сервисе, событийную интеграцию и саги с компенсирующими действиями. Двухфазный коммит оправдан только при действительно обязательной атомарности, контролируемом наборе участников и принятом риске блокировок.
Двухфазный коммит появился как механизм согласования распределённых транзакций, когда несколько независимых менеджеров ресурсов должны были прийти к единому решению: либо зафиксировать изменения у всех участников, либо отменить их у всех.
Исходная проблема заключалась в том, что локальные транзакции не гарантируют атомарность всей операции. Например, запись могла успешно сохраниться в одной системе, а другая система могла отказать до завершения своей транзакции.
Предположим, операция изменяет данные в двух сервисах, и промежуточное состояние недопустимо. Если каждый сервис подтвердит свою локальную транзакцию независимо, возможен частичный успех: один участник зафиксировал изменения, второй — нет.
Двухфазный коммит устраняет такой класс расхождений, но участники не могут окончательно освободить ресурсы до решения координатора. При сетевом разделении или отказе координатора они могут оставаться в состоянии подготовки, удерживая блокировки и ограничивая обработку новых запросов.
Неверно оценённый компромисс приводит к росту задержек, каскадным тайм-аутам и снижению доступности. Кроме того, система становится зависимой от стабильности координатора, протокола восстановления и корректного поведения каждого участника.
В первой фазе координатор просит участников подготовиться. Каждый участник проверяет локальные ограничения, записывает необходимую информацию для восстановления и сообщает, способен ли он зафиксировать изменения. После успешной подготовки участник обычно не может самостоятельно отменить решение.
Во второй фазе координатор принимает общее решение. Если все участники готовы, он отправляет команду фиксации; если хотя бы один не готов, отправляет команду отката. Участники сохраняют результат и освобождают ресурсы.
Главная особенность механизма — согласованность решения достигается через ожидание. Если участник подготовился, но не получил итоговую команду, он не всегда может безопасно выбрать фиксацию или откат самостоятельно: такой выбор может нарушить атомарность относительно остальных участников.
Поэтому нужно оценить:
Сага решает задачу иначе: каждый сервис фиксирует локальный результат, а при последующем отказе выполняется компенсирующее действие. Это не даёт мгновенной атомарности, зато лучше соответствует автономности микросервисов и сохраняет доступность. Компенсация должна быть бизнес-операцией, а не простым техническим откатом: например, возврат платежа не стирает факт уже проведённой операции.
Если операция требует только надёжной публикации факта после локальной записи, вместо двухфазного коммита применяют паттерн исходящих сообщений. Изменение и сообщение сохраняются в одной локальной транзакции, после чего отдельный процесс доставляет сообщение потребителям. Это решает проблему потери события, но само по себе не делает изменения в нескольких сервисах атомарными.
Сервис оформления заказа должен создать заказ и одновременно уменьшить доступный остаток товара. Команда рассмотрела три варианта.
Двухфазный коммит давал строгую атомарность, но требовал длительного удержания ресурсов и связывал доступность заказа с доступностью сервиса складских остатков. При проблемах сети оформление могло блокировать большое число заказов.
Прямая независимая запись в оба сервиса была проще, но создавала риск частичного успеха без понятного восстановления. Такой вариант отвергли, поскольку расхождение между заказом и резервом имело заметные финансовые последствия.
Выбрали сагу: заказ переходил в состояние ожидания, затем запускалось резервирование. При отказе резервирования заказ отменялся, а при последующей проблеме оплаты выполнялось освобождение резерва. Пользователь видел промежуточный статус, а повторная доставка сообщений обрабатывалась идемпотентно.
Решение не обеспечивало мгновенную атомарность, зато сохраняло автономность сервисов, позволяло повторять незавершённые шаги и явно моделировало бизнес-результат. Такой выбор был оправдан тем, что предметная область допускала короткое состояние ожидания и компенсирующие действия.
Вопрос: Делает ли двухфазный коммит систему полностью устойчивой к сетевым сбоям?
Ответ: Нет. Он помогает сохранить единое решение о фиксации, но не устраняет сетевые разделения и отказы. Участник может подготовить изменения и потерять связь с координатором, из-за чего операция останется незавершённой, а ресурсы — заблокированными. Нужны журналирование решений, восстановление после сбоев, тайм-ауты, мониторинг зависших транзакций и понятная политика ручного вмешательства. Тайм-аут не всегда означает безопасный откат: координатор мог уже принять решение о фиксации.
Вопрос: Почему увеличение числа участников ухудшает практические свойства двухфазного коммита?
Ответ: Общая операция зависит от готовности каждого участника. Чем их больше, тем выше вероятность, что хотя бы один будет медленным или временно недоступным. Это увеличивает время подготовки, число удерживаемых ресурсов и вероятность блокирующего сценария. Кроме того, растёт сложность диагностики: нужно сопоставлять состояния координатора и всех участников, а также обрабатывать несовместимые версии протокола или проблемы восстановления.
Вопрос: В чём принципиальная разница между откатом в распределённой транзакции и компенсацией в саге?
Ответ: Откат возвращает локальную транзакцию к состоянию до фиксации, пока это технически возможно. Компенсация выполняется уже после зафиксированного бизнес-действия и создаёт новое действие с обратным смыслом. Например, проведённый платёж нельзя надёжно стереть локальным откатом после его подтверждения; для него нужна отдельная операция возврата. Поэтому компенсация может быть неполной, задержанной или требовать участия оператора, а бизнес-инварианты нужно проектировать с учётом таких состояний.