Написание идемпотентного кода: руководство
29 октября 2023 г.Введение
При разработке программного обеспечения крайне важно писать надежный код, особенно при работе с распределенными системами и микросервисами. Одним из способов достижения этой надежности является идемпотентность. Но что означает «идемпотент» и как написать идемпотентный код?
Что такое идемпотентность?
Идемпотентность в программировании и математике — это свойство некоторых операций, при котором независимо от того, сколько раз вы их выполняете, вы достигаете одного и того же результата.
Например, нажатие кнопки «стоп» на будильнике является идемпотентным. Независимо от того, нажмете ли вы ее один или пять раз, будильник останется отключенным. С другой стороны, нажатие кнопки «увеличить громкость» не идемпотентно, каждое нажатие увеличивает громкость.
Почему идемпотентность важна?
Идемпотентность необходима по нескольким причинам:
- Восстановление ошибок: если система выйдет из строя во время операции, вы можете безопасно повторить операцию, не беспокоясь о неблагоприятных последствиях.
* Параллелизм: в распределенной системе несколько узлов могут попытаться выполнить одну и ту же операцию. Идемпотентность гарантирует, что конечный результат останется неизменным.
* Пользовательский опыт: пользователи могут обновить страницу или нажать кнопку несколько раз без непредвиденных побочных эффектов.
Как написать идемпотентный код
Очистите результат перед его обновлением
Перед выполнением операций удаляйте данные из таблиц и переназначайте значения переменным. Если вы хотите добавлять только новые данные, очистите раздел, с которым работаете.
ALTER TABLE table_name DROP IF EXISTS PARTITION(year = 2022, month = 12);
INSERT INTO TABLE table_name PARTITION (year = 2022, month = 12)
SELECT * FROM another_table
WHERE some_condition;
Транзакции с базой данных
Используйте транзакции базы данных, чтобы гарантировать, что серия операций либо полностью увенчается успехом, либо потерпит неудачу. Это гарантирует, что база данных останется в согласованном состоянии, даже если операция будет повторена.
BEGIN TRANSACTION;
UPDATE accounts SET balance = balance - 1000 WHERE account_id = 1;
UPDATE accounts SET balance = balance + 1000 WHERE account_id = 2;
COMMIT;
Ключи идемпотентности
В таких ситуациях, как обработка платежей, вы можете использовать ключи идемпотентности, чтобы сделать операции идемпотентными. Когда клиент отправляет запрос, он включает уникальный ключ идемпотентности. Сервер проверяет, обработан ли уже запрос с этим ключом.
Если это так, сервер возвращает предыдущий ответ вместо повторного выполнения операции.
registrations = {}
def register_user(idempotency_key, user_data):
if idempotency_key in registrations:
return registrations[idempotency_key]
# App logic here
registrations[idempotency_key] = "User registered"
return registrations[idempotency_key]
Используйте безопасные методы HTTP для веб-служб
Если вы создаете RESTful API, используйте методы HTTP, которые по своей сути идемпотентны, например GET, PUT, DELETE и HEAD. Избегайте использования POST или используйте ключи идемпотентности, как в примере выше.
Идемпотентность предмета
Для таких операций, как «добавить в корзину», вы можете сделать операцию идемпотентной, указав конечное состояние, а не изменение. Например, вместо «добавить один элемент» можно использовать операцию «установить количество элементов равным 1».
# Server-side Python code
cart = {}
def set_cart_item(item_id, quantity):
cart[item_id] = quantity
Использовать конечные автоматы
Конечные автоматы также могут быть полезны для обеспечения идемпотентности. Они определяют возможные состояния системы и переходы между ними. Проверив текущее состояние перед выполнением операции, вы можете убедиться в ее идемпотентности.
jobs = {}
def run_job(job_id):
if jobs[job_id] == "queued":
jobs[job_id] = "running"
#Start the job
Проверка на идемпотентность
Проверить идемпотентность несложно. Выполните операцию один раз и запишите результат. Затем выполните операцию несколько раз и сравните результаты. Они должны быть одинаковыми.
def test_idempotence_delete():
initial_result = update_user(1, {"name": "Yuri", "age": 30})
repeat_result = update_user(1, {"name": "Yuri", "age": 30})
assert initial_result == repeat_resultpy
Обратите внимание, что в распределенной системе проверка идемпотентности может быть более сложной из-за таких проблем, как задержка в сети, сбои и т. д.
Заключение
Написание идемпотентного кода — это не просто лучшая практика, но зачастую и необходимость в современной разработке программного обеспечения. Это делает ваши приложения более надежными, простыми для понимания и менее подверженными ошибкам.
Понимая основные принципы и реализуя упомянутые выше стратегии, вы будете на пути к написанию более надежного кода.
Также опубликовано здесь
Оригинал