Все главы учебника
Содержание учебника
Глава 05 / Бесплатный backend-путь

Два разных повтора: запрос клиента и попытка обработки

35 мин чтенияКонтент v0.8.0

Ответ не дошёл

Клиент отправил POST, сервер создал запись, но ответ потерялся. Если клиент повторит запрос, обычное создание даст второе задание. Добавьте необязательный заголовок Idempotency-Key: клиент выбирает ключ операции, а сервер связывает его с заданием и исходным текстом.

Повтор с тем же ключом и тем же text возвращает прежнюю запись. Тот же ключ с другим текстом получает 409: клиент пытается использовать идентификатор одной операции для другой. Сравнивайте исходное поле text до удаления пробелов, а не результат Analyze. В записи задания текст хранится с удалёнными краевыми пробелами; исходный вход для проверки ключа сохраняется отдельно. Поэтому "hello" и " hello " с одним ключом конфликтуют, хотя результат преобразования совпадёт. Без ключа каждый POST остаётся отдельным созданием.

Что прочитать

Прочитайте разделы о неопределённом результате и повторах в HTTP, об обработке сообщений в очередях и событиях, о таймаутах и повторах в надёжности. Повторите errors.Is в ошибках Go и отменяемое ожидание в context.

Реализуйте оба механизма

Проверка ключа и создание задания должны быть одной согласованной операцией: два одновременных запроса с одним ключом не должны пройти проверку «ещё нет» и создать две записи. Связь ключа с записью должна восстанавливаться из файла после перезапуска. Ключ не обещает один запуск Process: восстановление из прошлого этапа допускает повторную обработку одного задания.

Другой повтор происходит внутри обработчика. Process может вернуть временную ошибку, узнаваемую через errors.Is(err, ErrRetryable). После неё разрешена новая попытка с ожиданием RetryDelay. Прочие ошибки сразу завершают задание состоянием failed. Считайте первую попытку в пределе одного цикла обработки: при MaxAttempts: 3 допускаются максимум три вызова в этом цикле, а не первый плюс три повтора. Поле attempts накопительное между перезапусками. После сбоя незавершённая запись может начать новый цикл и получить суммарно больше трёх попыток: это следствие принятой обработки как минимум один раз.

Не используйте голый time.Sleep для ожидания повтора: сервис должен реагировать на отмену. Ожидание заканчивается либо по таймеру, либо по ctx.Done(). Если обработка отменена при остановке, не расходуйте новую попытку просто из-за восстановления записи. Разделяйте число реально начатых попыток, сохранённое состояние и окончательную ошибку.

Проверьте без случайности

go test ./... -run '^TestStage0[1-5]' -count=1
go test -race ./... -run '^TestStage0[1-5]' -count=1

Подмените Process: в первом тесте она дважды возвращает ErrRetryable, а затем результат; во втором всегда возвращает временную ошибку; в третьем — обычную ошибку. На новом задании без перезапуска проверьте done и три попытки, failed и три попытки, failed и одну попытку соответственно. Сделайте отдельный тест отмены во время задержки.

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

На что посмотреть в ошибках

Бесконечные повторы удерживают обработчики и скрывают неисправность. Повтор всех ошибок заставляет заново выполнять работу с неверными данными. Увеличение лимита без причины может усилить нагрузку на неисправную зависимость. В этом проекте задержка постоянная для простоты; перед работой с внешними зависимостями отдельно изучают backoff, случайный разброс и общий бюджет времени.

Когда идти дальше

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

Самопроверка этапа

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

Что выполнено

Для завершения отметьте все критерии и добавьте объяснение.