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