"Что такое идемпотентность?" - Этот вопрос можно часто услышать на собеседовании.
🧮 В математике
Понятие идемпотентность пришло из математики. Идемпотентность - это свойство операции давать одинаковый результат при повторном применении.
Примеры идемпотентных операций
- Сложение с нулем
- Умножение на единицу
💻 В программировании
Идемпотентные операции часто используются в распределенных системах и микросервисной архитектуре при проектировании 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.
При поступлении сообщения потребитель транзакционно выполняет следующие действия:
- Проверить, что ключ идемпотентности существует в списке обработанных.
- Пропустить сообщение, если ключ идемпотентности существует в списке обработанных.
- Обработать сообщение, если ключ идемпотентности не существует в списке обработанных. Сохранить ключ идемпотентности.
Transactional Inbox упрощает реализацию идемпотентности при обработке асинхронных сообщений и гарантирует доставку строго один раз.
🎉 Вывод
Теперь ты знаешь, что такое идемпотентность и не растеряешься на собеседовании. Удачи!
#техничка #backend
Дискуссия