Понятие идемпотентности

Я помогаю Golang, Java и C# разработчикам находить работу и проходить собеседования: резюме и отклики, которые доходят до HR, спокойный собес и торг по офферу. Разбираю механику найма изнутри, я провёл более 100 собеседований. Без воды, только чек-листы и готовые фразы.

идемпотентностьapitransactional inbox

"Что такое идемпотентность?" - Этот вопрос можно часто услышать на собеседовании.

🧮 В математике

Понятие идемпотентность пришло из математики. Идемпотентность - это свойство операции давать одинаковый результат при повторном применении.

Примеры идемпотентных операций

  • Сложение с нулем
  • Умножение на единицу

💻 В программировании

Идемпотентные операции часто используются в распределенных системах и микросервисной архитектуре при проектировании API. Из-за сбоев сети операции могут быть выполнены несколько раз. Поэтому важно, чтобы повторное выполнение не приводило к неконсистентности.

🔄 Идемпотентность при синхронных взаимодействиях

interface IUserController 
{
  // Создает пользователя. Email уникальный для каждого пользователя
  // Возвращает идентификатор пользователя
  Task<long> CreateUser(string email);

  // Возвращает пользователя по идентификатору
  Task<User> GetUserById(long id);

  // Возвращает пользователя по email.
  Task<User> GetUserByEmail(string email);
}

Метод CreateUser, который принимает email, создает пользователя и возвращает его идентификатор. Email - ключ идемпотентности для операции CreateUser.

Метод не идемпотентен, если он возвращает ошибку при отправке запроса с тем же email. Например, сообщает что пользователь уже существует с другим кодом ответа. Это усложняет взаимодействие с сервисом. Например, клиент отправил запрос на создание пользователя, но не получил ответ из-за сбоя сети. При повторной отправке запроса будет возвращена ошибка. Для получения идентификатора клиенту придется обратиться к сервису повторно для получения данных о пользователе по email. Это не так эффективно, как работа с целочисленным идентификатором, если используется реляционная база данных.

Чтобы сделать метод идемпотентным, необходимо в случае существования пользователя с переданным email вернуть его идентификатор. Для этого достаточно получить данные о пользователе из БД по ключу идемпотентности перед созданием пользователя и вернуть идентификатор клиенту.

Используй идемпотентные методы там, где нет гарантии успешности выполнения вызова. Например, при интеграции с внешними системами.

📨 Идемпотентность при асинхронных взаимодействиях

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

Бизнес-логика обработчиков сообщений должна учитывать повторную обработку сообщений. При повторной обработке не должно меняться состояние системы. С большой вероятностью это усложнит реализацию. Поэтому стандартом является использование паттерна Transactional Inbox, который гарантирует обработку сообщения один раз и не выполняет бизнес-логику, если сообщение уже обработано.

В сообщение добавляется ключ идемпотентности. Обычно Guid. Он будет уникальным идентификатором каждого сообщения. Составной ключ из бизнес-полей используется реже. Он не всегда позволяет явно идентифицировать событие. На потребителе сохраняются ключи идемпотентности обработанных сообщений. Так как паттерн называется Transactional Inbox, подразумевается, что бизнес-логика и соблюдение идемпотентности выполняются транзакционно. То есть для сохранения ключей идемпотентности используется то же хранилище данных, поддерживающее транзакционность, что и для хранения бизнес-данных. Например, реляционная база данных или Redis.

При поступлении сообщения потребитель транзакционно выполняет следующие действия:

  1. Проверить, что ключ идемпотентности существует в списке обработанных.
  2. Пропустить сообщение, если ключ идемпотентности существует в списке обработанных.
  3. Обработать сообщение, если ключ идемпотентности не существует в списке обработанных. Сохранить ключ идемпотентности.

Transactional Inbox упрощает реализацию идемпотентности при обработке асинхронных сообщений и гарантирует доставку строго один раз.

🎉 Вывод

Теперь ты знаешь, что такое идемпотентность и не растеряешься на собеседовании. Удачи!

#техничка #backend

Дискуссия

Евгений Беляев
Не все синхронные операции имею явный ключ идемпотентности. Например, перевод 5000р соседке. Тогда для этого случая можно использовать тот же самый “transactional” inbox:) Ещё интересно про ошибку 409. Когда стоит кидать 409, а когда возвращать 200.
Сергей Назаров | Карьерный бустер
Евгений Беляев
Не все синхронные операции имею явный ключ идемпотентности. Например, перевод 5000р соседке. Тогда для этого случая можно использовать тот же самый “transactional” inbox:) Ещё интересно про ошибку 409. Когда стоит кидать 409, а когда возвращать 200.
Когда предлагаешь 409 бросать?
Евгений Беляев
Сергей Назаров | Карьерный бустер
Когда предлагаешь 409 бросать?
Сложно сказать… Думаю в тех случаях, когда клиенту важно знать, что его операция выполнена. Но сейчас подумал, всё таки это уже редкий сценарий в идемпотентности…) Например, ты запустил процесс удаления миллиона фоток. Нажимаешь отменить 10 раз и тебе каждый раз говорят «Операция отменена». Как вариант получить ошибку «Нечего отменять»
Сергей Назаров | Карьерный бустер
Евгений Беляев
Сложно сказать… Думаю в тех случаях, когда клиенту важно знать, что его операция выполнена. Но сейчас подумал, всё таки это уже редкий сценарий в идемпотентности…) Например, ты запустил процесс удаления миллиона фоток. Нажимаешь отменить 10 раз и тебе…
Согласен. Кстати, 409 можно бросать в сценарии с оптимистичной блокировкой. Когда версия изменяемой сущности поменялась.
Присоединиться к обсуждению →

Читайте так же