Три подводных камня и лучшие практики для паттерна Saga в микросервисах

Saga — это наиболее распространенный паттерн для обеспечения согласованности данных между сервисами. Он разделяет глобальную транзакцию на несколько подтранзакций, и если все операции подтранзакции успешны, то успешна и глобальная транзакция, а если одна подтранзакция после повторных попыток все еще не удалась, то все выполненные подтранзакции компенсируются.

Приведенный выше процесс прост и понятен, но в распределенной среде могут возникнуть аномалии. Убедиться в том, что транзакция Saga работает правильно и данные остаются согласованными, — нелегкая задача. Мы обсудим различные аномалии, которые могут возникнуть, и способы управления ими.

Для целей обсуждения мы будем называть прямое действие транзакции филиала в Saga Action, а компенсирующее действие — Compensation.

Три типа аномалий

Обычно Action выполняется перед Compensation в транзакциях Saga, если происходит откат. Но из-за сетевых задержек или пауз в процессе, возможно, что Compensation выполняется первым, а Action выполняется позже или не выполняется вообще.

Этот сценарий приводит к двум аномалиям в распределенных транзакциях:

  1. Нулевая компенсация: Когда Compensation выполняется, соответствующее Action не было выполнено, поэтому Compensation должен определить, что Action не было выполнено, проигнорировать обновления бизнес-данных и вернуться напрямую.
  2. Зависающее действие: Когда Action выполняется, Compensation был выполнен, поэтому Action должен определить, что Compensation был выполнен, проигнорировать обновления бизнес-данных и вернуться напрямую.

Другой распространенной аномалией, с которой необходимо бороться в распределенных транзакциях, является Duplicated Requests. Когда процесс терпит крах и Saga повторяет попытку обращения к службе, служба может быть вызвана несколько раз, поэтому необходима идемпотентная обработка.

Эти три типа аномалий требуют тщательного управления, иначе могут возникнуть несоответствия данных и нарушить работу предприятия.

Лучшая практика

https://github.com/dtm-labs/dtm разработал технику под названием Sub-transaction Barrier для одновременного управления этими тремя типами аномалий. Для шаблона Saga принцип заключается в следующем.

  1. Создайте таблицу dtm_barrier.barrier в локальной базе данных, с уникальным индексом gid-branchid-branchop.
  2. Начните локальную транзакцию
  3. В Action и Compensation вставить игнорирование строки gid-branchid-action|compensation, если количество затронутых строк равно 0 (в случае Duplicate Requests, Hanging Action), зафиксировать напрямую и вернуться
  4. В Compensation, вставьте игнорирование дополнительной строки gid-branchid-action, если количество затронутых строк равно 1 (в случае Null Compensation), зафиксируйте непосредственно и вернитесь обратно.
  5. Выполнить бизнес-логику и зафиксировать, или откатиться назад, если возникли ошибки

Дублированные запросы

Из-за уникального индекса, указанного выше, вставка в дублирующихся запросах гарантированно игнорируется, а бизнес-обработка пропускается

Компенсация нуля

Когда происходит компенсация нуля, успешная вставка шага 3 и проигнорированная вставка шага 4 будут зафиксированы напрямую и возвращены, пропуская бизнес-логику.

Зависающее действие

Когда возникает Hanging Action, компенсация была выполнена и вставлена строка gid-branchid-action, поэтому в Action будет обнаружено, что вставка на шаге 3 была проигнорирована, а затем будет выполнен прямой возврат, минуя бизнес-логику.

Перекрытие действий и компенсаций

Если Action и Compensation перекрываются по времени выполнения, то и Compensation и Action вставят одну и ту же строку gid-branchid-action. Из-за конфликта уникальных индексов только одна из этих двух операций будет успешной, в то время как другая будет ждать завершения транзакции, удерживающей блокировку, а затем вернется.

  • Сценарий 1: Action терпит неудачу, а Compensation успешно вставляет gid-branchid-action. Это типичный сценарий Null Compensation и Hanging Action, и оба Action и Compensation будут игнорировать бизнес-логику и возвращаться непосредственно в соответствии с приведенным выше алгоритмом.
  • Сценарий 2: Action успешна, а Compensation неудачна при вставке gid-branchid-action. Порядок выполнения транзакции — Action перед Compensation, и никакой аномалии не происходит. Согласно приведенному выше алгоритму, бизнес в Action и Compensation будет выполняться упорядоченно
  • Сценарий 3. Если база данных выйдет из строя во время перекрытия, операции будут повторно опробованы и в конечном итоге перейдут к сценарию 1 или 2.

В результате рассмотрения вышеприведенных сценариев, Sub-transaction Barrier способен правильно управлять аномалиями и обеспечивать согласованность данных.

Описанный выше алгоритм также применим к распределенным транзакциям TCC. Даже если вы используете механизм рабочего процесса, такой как Candence, Camunda, для обработки транзакций Saga, алгоритм также применим.

Код

Описанная выше техника Sub-transaction Barrier, при использовании в сочетании с фреймворком распределенных транзакций https://github.com/dtm-labs/dtm, была доступна в SDK нескольких языков, со следующим примером кода на Go.

app.POST(BusiAPI+"/SagaBTransOut", dtmutil.WrapHandler(func(c *gin.Context) interface{} {
  barrier := MustBarrierFromGin(c)
  return barrier.Call(txGet(), func(tx *sql.Tx) error {
    return SagaAdjustBalance(tx, TransOutUID, -reqFrom(c).Amount, "")
  })
}))
app.POST(BusiAPI+"/SagaBTransOutCom", dtmutil.WrapHandler(func(c *gin.Context) interface{} {
  barrier := MustBarrierFromGin(c)
  return barrier.Call(txGet(), func(tx *sql.Tx) error {
    return SagaAdjustBalance(tx, TransOutUID, reqFrom(c).Amount, "")
  })
}))
Вход в полноэкранный режим Выход из полноэкранного режима

В сервисе TransOut первой строкой создается барьер из http-запроса, а следующие несколько строк вызывают бизнес внутри барьера.

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

Полный пример можно найти здесь: https://github.com/dtm-labs/dtm-examples. После установки dtm вы можете запустить полный пример, выполнив следующую команду:

Заключение

В этой статье предлагается алгоритм обработки аномалий в паттерне Saga для микросервисов. Это элегантный и эффективный алгоритм, который может быть применен к шаблону TCC, а также ко многим рабочим процессам.

Приглашаем вас посетить сайт https://github.com/dtm-labs/dtm. Это проект с открытым исходным кодом, призванный облегчить разработку распределенных транзакций.

Оцените статью
Procodings.ru
Добавить комментарий