Запрос оборвался: как повторить заказ и не создать дубль

Пользователь нажал «Оформить», сервер успел создать заказ, но ответ потерялся в сети. На экране ошибка. Нажать ещё раз кажется естественным, однако второй запрос может создать вторую покупку. Главная сложность здесь — неизвестный результат первой попытки: отсутствие ответа не означает отсутствие действия.
Один заказ, несколько сетевых попыток
Отделите намерение пользователя от транспорта. Намерение — оформить определённый состав заказа один раз. Попытки — передать это намерение через нестабильное соединение. У намерения должен быть устойчивый ключ, который сохраняется при повторе запроса. Новый осознанный заказ получает новый ключ, даже если товары и сумма совпадают.
Обычный POST сам по себе не гарантирует идемпотентность. Заголовок с названием Idempotency-Key тоже ничего не обеспечивает, если сервер просто игнорирует его. Приложение должно определить область уникальности, срок хранения результата, правила конкурирующих попыток и поведение при несовпадающих параметрах.
| Ситуация | Ключ | Параметры | Ожидаемое решение |
|---|---|---|---|
| Сеть оборвалась, клиент повторяет | Прежний | Прежние | Вернуть результат той же операции |
| Клиент изменил корзину | Прежний | Другие | Отклонить конфликт, не переиспользовать старый итог |
| Клиент действительно покупает ещё раз | Новый | Могут совпадать | Создать самостоятельный заказ |
| Две попытки пришли одновременно | Один | Одни | Одна выполняет действие, другая получает согласованное состояние |
Где хранить ключ и результат
Для операции внутри одной базы запись ключа, изменение бизнес-данных и сохранение результата удобно согласовать одной транзакцией. Уникальное ограничение защищает от двух одновременно пришедших запросов. Проверка «сначала найдём ключ, потом вставим» без ограничения оставляет окно гонки: оба обработчика могут увидеть отсутствие записи.
Область ключа должна включать владельца и тип операции. Иначе один пользователь способен случайно получить результат другого. Рядом сохраняют нормализованный отпечаток параметров и ссылку на бизнес-объект. Секреты и платёжные реквизиты в отпечаток или диагностический журнал не переносят без необходимости.
Если операция затрагивает внешнего провайдера, локальной транзакции недостаточно. Надо использовать поддерживаемую провайдером схему повторов либо выполнять сверку его состояния по устойчивому внешнему идентификатору. Между «провайдер выполнил» и «мы записали ответ» тоже возможен сбой. Обещание ровно одного действия во всей распределённой цепочке требует отдельного анализа, а не одного Redis-ключа.
Почему удаление ключа через минуту бывает ошибкой
Окно хранения определяется допустимым временем повторов. Мобильный клиент может восстановить соединение значительно позже первоначальной попытки. После удаления ключа сервер перестанет узнавать повтор. С другой стороны, бессрочное хранение полных ответов увеличивает объём базы и срок хранения пользовательских данных.
Разделите компактную запись бизнес-идентичности операции и тяжёлый ответ. Установите документированный срок повторного получения результата. Если ключ устарел, клиенту нужен понятный путь сверки, а не молчаливое создание нового заказа. Срок хранения выбирают по реальному процессу: универсального числа секунд для всех приложений нет.
Испытание с падением между двумя действиями
- Отправьте две одновременные попытки с одним ключом. Проверьте число бизнес-объектов, а не только ответы HTTP.
- Повторите запрос после успешного создания. Должен вернуться тот же идентификатор.
- Измените сумму при сохранённом ключе. Сервер обязан обнаружить несовместимость.
- Имитируйте сбой после действия внешнего провайдера до сохранения локального ответа. Убедитесь, что повтор сначала сверяет прежнюю попытку.
- Проверьте два разных аккаунта с одинаковой строкой ключа. Их данные не должны пересекаться.
После внедрения следите за конфликтами параметров, возрастом незавершённых операций и долей повторов. Не считайте каждый повтор атакой: на мобильной сети это нормальное поведение. Но серия новых ключей не является повтором одной операции и требует отдельного ограничения частоты.
Локальная модель повторов
Инструмент ниже группирует учебные попытки по ключу и составу. Он показывает, где ожидается повтор результата, а где конфликт. Это не готовый серверный механизм и не средство отправки заказов. Вводите обезличенные метки вроде «корзина-A», а не настоящие платёжные данные. Для реального проекта схема должна учитывать авторизацию, транзакции и внешний платёжный сервис.
Проверка учебной последовательности повторов
Область модели — один владелец и один тип операции. Сеть и база данных не вызываются. Расчёт выполняется на вашем устройстве, введённые данные никуда не отправляются.
Материалы для проверки
Если нужна реализация
Состав работ зависит от текущего проекта и подтверждённой причины. Можно обсудить диагностику или разработку подходящего решения.
