Как настроить идемпотентность SMS API и избежать дублей

Как настроить идемпотентность SMS API и избежать дублей

Идемпотентность SMS API помогает повторить запрос после тайм-аута или сбоя связи, не отправив клиенту одно и то же сообщение несколько раз. Для этого приложению нужен постоянный идентификатор операции, а шлюзу — правило, по которому повтор с тем же идентификатором не создаёт новую отправку. Разберём, где хранить ключ, как обрабатывать статусы и что проверить перед запуском. Эти шаги подойдут и для небольшой интеграции, и для потока сообщений из CRM.

Почему повтор запроса создаёт дубль?

Приложение отправляет SMS-запрос, но ответ от шлюза не приходит вовремя. Причина может быть в разрыве соединения, задержке ответа или сбое на стороне приложения. При этом сам запрос уже мог дойти до шлюза и запустить отправку.

Если приложение автоматически повторяет запрос как новую операцию, шлюз получает два задания. Клиент в итоге может получить одинаковые SMS. Тайм-аут поэтому не доказывает, что первая попытка завершилась неудачно: он сообщает только, что приложение не получило ответ вовремя.

Идемпотентность разделяет две ситуации: повтор той же операции и новую отправку такого же текста. В первом случае система должна распознать прежний запрос. Во втором — обработать сообщение как отдельное, даже если текст и номер получателя совпадают.

Какой ключ использовать для распознавания повтора?

Перед отправкой приложение создаёт уникальный ключ операции и сохраняет его вместе с заданием. При повторе оно передаёт тот же ключ. Шлюз или ваш интеграционный слой ищет его в журнале и возвращает результат исходной операции вместо создания новой отправки.

Ключ лучше связать с бизнес-событием: например, с конкретным уведомлением о заказе или напоминанием о записи. Номер телефона и текст SMS для этой роли не подходят: одинаковое сообщение одному человеку может быть нужно отправить повторно по новому событию.

В журнале полезно хранить ключ, время создания, номер получателя, текст или его контрольную сумму и текущий статус. Если система получает тот же ключ с другими параметрами, она должна зафиксировать конфликт, а не молча менять исходное сообщение.

Правило обработки повторов зависит от интеграции. Его нужно согласовать между приложением и SMPP-шлюзом: наличие ключа в вашей CRM само по себе не остановит повторную отправку, если шлюз не получает этот ключ или не проверяет его. Общая схема отправки, статусов и повторов описана в материале об отправке SMS через SMPP, API и DLR.

Как обрабатывать тайм-ауты и статусы доставки?

Разделите статус запроса и статус SMS. Первый показывает, принял ли интеграционный слой задание в работу. Второй приходит позже и отражает состояние доставки, если маршрут передаёт DLR. Поэтому отсутствие ответа на API-запрос нельзя автоматически превращать в команду создать новую отправку.

После тайм-аута сначала проверьте запись по ключу операции. Если задание уже зарегистрировано, получите его текущий статус и продолжайте обработку. Если подтверждения пока нет, повторите запрос с прежним ключом, а не генерируйте новый. Так система сможет сопоставить попытки.

Когда приходит DLR, обновите статус существующей операции. Не создавайте новое SMS-задание только потому, что доставка не подтверждена сразу: отчёт может задержаться. Для очередей и CRM полезно отдельно настроить приём и обработку DLR, чтобы статус одного сообщения не запускал повторную рассылку. Подробности есть в материале об обработке DLR-статусов без перегрузки CRM.

Храните историю переходов статуса, а не только последнее значение. Тогда при разборе инцидента будет видно, когда приложение отправило запрос, когда шлюз принял задание и какой ответ поступил позже.

Как проверить защиту от дублей до запуска?

Проведите тест на отдельном номере и в тестовом контуре, если он предусмотрен интеграцией. Отправьте один запрос, затем повторите его с тем же ключом. Система должна связать оба обращения с одной операцией. После этого повторите запрос с новым ключом: он должен создать отдельное задание.

Проверьте и сценарии с потерей ответа. Например, шлюз принял запрос, но приложение не получило подтверждение. Повтор с исходным ключом должен вернуть состояние уже созданной операции. Отдельно проверьте, что запрос с тем же ключом, но другим текстом или номером, попадает в обработку конфликта.

Во время теста сверяйте число созданных заданий, записи журнала и DLR. Для проверки устойчивости SMPP-соединения и поведения очереди полезно моделировать рост нагрузки и ошибки: нагрузочное тестирование показывает, как шлюз работает при увеличении потока сообщений (smpp.by, «Как повысить доставляемость SMS в Беларуси: настройка SMPP-шлюза»).

Какие ошибки чаще всего приводят к дублям?

  • Создавать новый ключ при каждом повторе запроса. Так система считает каждую попытку отдельной отправкой.
  • Считать тайм-аут подтверждением неудачной отправки. Запрос мог дойти до шлюза, даже если ответ потерялся.
  • Использовать номер и текст как единственный признак операции. Однотипные уведомления могут относиться к разным событиям.
  • Хранить ключ только в интерфейсе CRM, не передавая его в слой, который создаёт SMS-задание.
  • Повторно отправлять SMS при задержке DLR. Отчёт о доставке и подтверждение приёма API — разные события.

Начните с одного сценария отправки: добавьте постоянный ключ операции, сохраняйте его вместе со статусом и настройте повтор только с тем же ключом. Затем проверьте потерю ответа и задержку DLR. Если в проекте есть несколько приложений или SMPP-сессий, зафиксируйте единое правило обработки ключей и повторов в API-интеграции, чтобы разные компоненты не создавали независимые задания для одной операции.