TGViewer
.NET Разработчик .NET Разработчик @netdeveloperdiary · 6.74K subscribers
Post #2370 2.61K
День 1960. #ЗаметкиНаПолях
Что Такое Идемпотентность в Программных Системах? Окончание

Начало

Как достичь идемпотентности?
Паттерн «Исходящие» (Outbox) обеспечивает согласованность, подобную базе данных, между операциями обмена сообщениями (как получением входящего сообщения, так и отправкой исходящих сообщений) и изменениями бизнес-данных в базе. Опираясь на транзакцию БД, мы превращаем гарантию доставки «хотя бы раз» брокера сообщений в гарантию ровно одной обработки.

Для реализации паттерна Outbox логика обработки сообщений разделена на две фазы:
1. Фаза обработки сообщения
Мы не отправляем исходящие сообщения немедленно брокеру сообщений, а храним их в памяти до завершения работы обработчика сообщений. На этом этапе мы сохраняем все накопленные исходящие сообщения в таблицу БД, используя ту же транзакцию, что и для записи бизнес-данных, и Id сообщения в качестве первичного ключа.
2. Фаза отправки
Все исходящие сообщения физически отправляются брокеру сообщений. Если всё идет хорошо, исходящие сообщения отправляются, а входящие обрабатываются. Но здесь ещё возможно возникновение проблемы и отправка не всех сообщений, что вынудит нас повторить попытку. Так возникнут дублирующие сообщения, но так и задумано.

Паттерн «Исходящие» связан с паттерном «Входящие», поэтому при обработке любого повторяющегося сообщения (или повторной попытке обработки сообщения, которое не удалось выполнить на этапе отправки), сначала извлекаются данные из таблицы исходящих сообщений. Если такое сообщение существует, это означает, что оно уже успешно обработано, надо пропустить этап обработки, и перейти к этапу отправки. Если сообщение является дубликатом, и исходящие сообщения уже отправлены, то и этап отправки также можно пропустить. На псевдокоде это выглядит так:
 
var message = PeekMessage();
// проверка на дубликат
var outbox = DB.GetOutboxData(message.Id);
// обработка
if(outbox == null)
{
using(var trans = DB.StartTransaction())
{
var result = ExecuteHandler(message);
outbox = new OutboxData(message.Id, result);
DB.StoreOutboxData(outbox);
trans.Commit();
}
}

// отправка
if(!outbox.IsDispatched)
{
Bus.DispatchMessage(outbox);
DB.SetAsDispatched(message.Id);
}

Используя этот шаблон, мы получаем идемпотентность на стороне обработки, когда вы можете отличить дубликат, просто взглянув на id сообщения.

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

Гораздо проще использовать такую инфраструктуру, как Outbox, которая может воспользоваться транзакцией локальной базы данных, уже используемой для хранения бизнес-данных, и использовать эту транзакцию для обеспечения согласованности между операциями входящего/исходящего обмена сообщениями и бизнес-данными, хранящимися в БД.

Источник: https://particular.net/blog/what-does-idempotent-mean
  • 👍 14
  • 👎 2
More from @netdeveloperdiary
  1. Sep 26, 2026День 2796. #ЗаметкиНаПолях #AI Рабочий процесс с Copilot для .NET. Продолжение Начало Три…
  2. Sep 25, 2026День 2795. #ЗаметкиНаПолях #AI Рабочий процесс с Copilot для .NET. Начало Проблема с позиц…
  3. Sep 24, 2026День 2794. #Оффтоп #Здоровье Сегодня будет необычный пост. Завтра в Москве стартует конфер…
  4. Sep 23, 2026День 2793. #ЗаметкиНаПолях #SQL 10 Редких Возможностей SQL, Которые Стоит Знать Каждому. Ч…
  5. Sep 22, 2026День 2792. #ЗаметкиНаПолях #SQL 10 Редких Возможностей SQL, Которые Стоит Знать Каждому. Ч…
  6. Sep 21, 2026🔍Тестовое собеседование с Senior C# разработчиком уже завтра 22 сентября(уже завтра!) в 1…
Threads Profile ViewerView any public Threads profile without an account.Open ThreadLook →Writing with AI? Make it sound human.Metric37 rewrites AI drafts so they read naturally. Free AI detector, 1,500 words free.Try Metric37 →