Короткий ответ
Надёжнее всего искать повторы по устойчивому event_id. Если его нет, используют осторожно выбранный составной ключ из пользователя, события, времени и значимых свойств.
Как это работает подробнее
Клиент может повторить запрос, если не получил подтверждение, хотя сервер уже записал событие. Повторная доставка нормальна для схемы at-least-once, поэтому приёмник или слой трансформации должен быть идемпотентным.
- Поиск. Группировка показывает ключи, встречающиеся более одного раза.
- Проверка. Несколько найденных групп нужно просмотреть, чтобы не удалить легитимные повторные действия.
Пример кода
find_duplicate_events.sql
SELECT event_id, COUNT(*) AS duplicate_count
FROM events
GROUP BY event_id
HAVING COUNT(*) > 1
ORDER BY duplicate_count DESC;Что использовать на практике
- Генерируйте
event_idдо первой отправки и сохраняйте его при ретраях. - Дедуплицируйте raw-данные оконной функцией, сохраняя правило выбора версии.
Подводные камни
- Полное равенство строк. Техническое время получения у копий различается, поэтому
DISTINCTможет не помочь. - Слабый ключ. Близкие по времени события одного пользователя не обязательно являются дублями.