PRO

Что такое идемпотентность задачи? Почему это важно?

Идемпотентная задача даёт одинаковый итоговый результат при повторном запуске с теми же данными. Это важно для Celery и очередей сообщений, потому что задача может быть доставлена повторно после сбоя, timeout, повторной попытки или отсутствия подтверждения обработки.
Подробный ответ

Что такое идемпотентность

Задача идемпотентна, если её запуск один или несколько раз приводит к одному итоговому состоянию. Например, операция «установить статус заказа в paid» идемпотентна, а операция «увеличить баланс на 100» без дополнительной защиты — нет.

Почему дубликаты возможны

Исполнитель может успеть выполнить задачу, но аварийно завершиться до подтверждения брокеру. Тогда брокер считает задачу необработанной и передаёт её повторно. Повтор также возможен из-за retry, сетевой ошибки или слишком короткого времени видимости задачи в Redis.

Опасный пример

@shared_task
def add_bonus(user_id: int, amount: int) -> None:
    user = get_user(user_id)
    user.balance += amount
    user.save()

При повторном запуске баланс увеличится дважды.

Идемпотентный вариант

@shared_task
def apply_bonus(user_id: int, operation_id: str, amount: int) -> None:
    if bonus_operation_exists(operation_id):
        return

    with transaction.atomic():
        create_bonus_operation(
            operation_id=operation_id,
            user_id=user_id,
            amount=amount,
        )
        increment_user_balance(user_id, amount)

Уникальное ограничение на operation_id в базе данных предотвращает повторное применение одного и того же начисления.

Способы сделать задачу идемпотентной

  • передавать уникальный идентификатор операции и хранить факт обработки;

  • использовать уникальные ограничения базы данных;

  • проверять текущее состояние перед изменением;

  • использовать операции установки состояния вместо повторного добавления;

  • применять идемпотентный ключ при вызове внешнего платёжного или почтового сервиса.

Как ответить на собеседовании

Идемпотентная задача безопасна при повторном запуске: итоговое состояние не меняется после первого успешного выполнения. Это важно, потому что очереди обычно допускают повторную доставку. Для защиты использую идентификатор операции, уникальное ограничение в БД, проверку текущего статуса и идемпотентные ключи во внешних вызовах.

Оцени свой прогресс

Честно оцени своё понимание этого вопроса, чтобы мы могли построить твой учебный трек максимально эффективно.
Читать в блоге