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