Логотип YeaHub

База вопросов

Собеседования

Тренажёр

База ресурсов

Обучение

Навыки

Задачи

Войти

Выбери, каким будет IT завтра — вместе c нами!

YeaHub — это полностью открытый проект, призванный объединить и улучшить IT-сферу. Наш исходный код доступен для просмотра на GitHub. Дизайн проекта также открыт для ознакомления в Figma.

© 2026 YeaHub

AI info

Карта сайта

Документы

Медиа

Назад
Вопрос про Python: idempotency, redis

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

Этот вопрос проверяет понимание идемпотентности и умение придумать устойчивый способ избежать повторной обработки событий, даже если у события нет явного ID.

Короткий ответ

Если внешний сервис не присылает явный идентификатор события, можно построить «искусственный» ключ идемпотентности на основе содержимого события. Например, взять хеш от важных полей (тип события, сумма, время, внешний user_id) и хранить его в таблице обработанных событий. При получении нового события вы вычисляете тот же хеш и проверяете, обрабатывали ли вы его раньше. Иногда дополнительно используют временное окно (например, хранить такие ключи сутки), чтобы не копить вечный список. Главное — аккуратно выбрать поля, чтобы одинаковые по смыслу события давали один и тот же ключ, а разные — разные.

Длинный ответ

1. Что такое идемпотентность в контексте событий

Определение.
Идемпотентность — это свойство операции, при котором повторное выполнение имеет тот же эффект, что и первое, и не приводит к «дублированию» результата.

В контексте событий это означает:

  • Получив одно и то же событие несколько раз, мы:

    • Не создаём дубликаты записей.

    • Не списываем деньги повторно.

    • Не отправляем повторных уведомлений, если это критично.

Обычно для этого используется идентификатор события (event_id), но если его нет, нужно придумать его самим.

2. Стратегии, когда явного ID нет

Есть несколько подходов, которые можно комбинировать.

2.1. Хеш от содержимого события

Идея простая:

  1. Выделить набор полей, которые однозначно определяют событие:

    • Тип события (event_type).

    • Пользователь (user_id или аналог).

    • Сумма/параметры (amount, currency).

    • Временная метка или «бизнесовый» ID (например, номер заказа).

  2. Собрать из них строку/структуру и вычислить хеш (например, SHA-256).

  3. Хранить этот хеш как «ключ идемпотентности» в БД.

Python

import hashlib
import json

def make_idempotency_key(event: dict) -> str:
    key_data = {
        "type": event.get("type"),
        "user_id": event.get("user_id"),
        "amount": event.get("amount"),
        "order_id": event.get("order_id"),
    }
    payload = json.dumps(key_data, sort_keys=True)
    return hashlib.sha256(payload.encode("utf-8")).hexdigest()

При обработке события:

  1. Считаем ключ.

  2. Смотрим в таблице processed_events:

    • Если ключ найден — считаем событие уже обработанным.

    • Если нет — обрабатываем и записываем ключ.

2.2. Комбинация бизнес-полей

Иногда можно обойтись и без хеша, если есть естественный уникальный набор полей, например: (order_id, event_type).

  • Для каждой комбинации (order_id, event_type) делаем уникальный индекс в БД.

  • При попытке вставить второй раз БД бросит ошибку «уникальное ограничение нарушено».

  • В такой ситуации обработчик может интерпретировать это как «событие уже было».

3. Использование временных окон и TTL

Если идентификатор строится из «похожих» полей (например, сумма и время округлённое до минуты), есть риск случайных совпадений. Чтобы ограничить риск и размер таблицы:

  1. Можно хранить ключи только какое-то время:

    • В Redis с TTL (например, 24 часа).

    • В БД с регулярной чисткой старых записей.

  2. Типичный подход:

    • SETNX (или SET key value NX EX ttl в Redis) для вставки ключа.

    • Если ключ уже есть — считаем событие дубликатом.

Python (псевдокод для Redis)

def should_process_event(redis_client, key: str, ttl_seconds: int = 86400) -> bool:
    # Вернёт True, если ключ успешно установлен (событие новое)
    return redis_client.set(key, "1", ex=ttl_seconds, nx=True)

4. Осторожность при выборе полей

Важно понимать, что:

  • Если вы выберете слишком мало полей, разные события могут «сливаться» в один ключ — часть событий будет проигнорирована.

  • Если выберете слишком много полей (например, точное время с миллисекундами), одно и то же событие, отправленное дважды, может дать разные ключи — защита не сработает.

Практические советы:

  1. Ориентируйтесь на «бизнесовый смысл»:

    • Для платежей — order_id + event_type.

    • Для подписок — subscription_id + period_start.

    • Для изменения баланса — account_id + «бизнесовый» ID операции.

  2. Если внешнего «бизнесового» ID нет — попробуйте договориться с внешним сервисом, чтобы он его добавил. Иногда это проще и безопаснее.

5. Как вписать это в общий пайплайн обработки

Типичный сценарий:

  1. Пришёл webhook без event_id.

  2. Внутри обработчика:

    • Считаем idempotency_key.

    • Пытаемся записать его в БД/Redis.

    • Если запись прошла — продолжаем обработку (меняем баланс, создаём запись).

    • Если запись не прошла (ключ уже есть) — логируем и завершаем без повторных действий.

  3. Важно:

    • Логировать такие случаи для дебага.

    • Следить за размером хранилища ключей.

6. Вывод: что делать на практике

  • Если нет явного ID, создаём искусственный ключ на основе содержимого события.

  • Храним ключи в БД или Redis и используем уникальность/TTL.

  • Аккуратно подбираем поля, чтобы минимизировать ложные совпадения и «пропуск» реальных дубликатов.

  • По возможности стараемся договориться с внешним сервисом о явном event_id или request_id — это всегда проще и надёжнее.

Краткий вывод:
При отсутствии ID события идемпотентность достигается за счёт искусственного «idempotency key» — хеша или комбинации бизнес-полей, которые однозначно описывают событие. Такой ключ сохраняется в устойчивом хранилище и позволяет безопасно игнорировать повторы без повторного выполнения критичных действий.

  • Аватар

    Python Guru

    Sergey Filichkin

    Guru – это эксперты YeaHub, которые помогают развивать комьюнити.

Уровень

  • Рейтинг:

    4

  • Сложность:

    6

Навыки

  • Python

    Python

Ключевые слова

#idempotency

#redis

Подпишись на Python Developer в телеграм

  • Аватар

    Python Guru

    Sergey Filichkin

    Guru – это эксперты YeaHub, которые помогают развивать комьюнити.