Эта страница переведена с английского языковой моделью. При любом расхождении верен английский оригинал.
Завершение
Чем ack, retry, retry_after и drop становятся в SQS: каждый исход - один вызов к очереди,
а переобработка ждёт в самой очереди.
Схема текстом
- Исход: ack; retry(); retry_after(d); drop; отброшена незавершённой
- Вызов SQS: DeleteMessage; ChangeMessage Visibility(0); ChangeMessage Visibility(d); DeleteMessage; на пределе: Visibility(0); Без вызова, без продления
- Результат: Удалено; Видно сразу; Вернётся через d, в пределах 12 ч после получения; Удалено или перенесено в DLQ; Вернётся после тайм-аута
- ack → DeleteMessage
- DeleteMessage → Удалено
- retry() → ChangeMessage Visibility(0)
- ChangeMessage Visibility(0) → Видно сразу
- retry_after(d) → ChangeMessage Visibility(d)
- ChangeMessage Visibility(d) → Вернётся через d, в пределах 12 ч после получения
- drop → DeleteMessage; на пределе: Visibility(0)
- DeleteMessage; на пределе: Visibility(0) → Удалено или перенесено в DLQ
- отброшена незавершённой → Без вызова, без продления
- Без вызова, без продления → Вернётся после тайм-аута
retry_after(d) считает задержку в целых секундах с округлением вверх, поэтому сообщение никогда
не возвращается раньше d. Задержка, которая выходит за 12 часов с момента получения,
сокращается до их конца, и крейт пишет в журнал предупреждение с именем очереди. Доставка, которую отбросили без завершения, не делает вызова: её продление
прекращается, и сообщение возвращается, когда истекает его текущий тайм-аут.
В SQS нет операции, которая отвергает сообщение, не удаляя его, поэтому drop удаляет сообщение.
Исключение - очередь, регистрация которой объявляет политику перенаправления. Там доставка,
полученная maxReceiveCount раз, возвращается в очередь, а не удаляется: в DLQ её
переносит именно следующее получение, а удаление её бы потеряло.
redelivery_count доставки - это
ApproximateReceiveCount очереди, и первая доставка считается первой. Он считает каждое
получение, включая истёкший тайм-аут, а не только переобработки, которые запросил сервис.
Переобработка
Схема текстом
- Доставка счётчик получений показывает номер попытки → Обработчик возвращает retry_after(d) или retry(): обработана
- Обработчик возвращает retry_after(d) или retry() → ChangeMessageVisibility(d) сообщение ждёт в очереди: переобработка
- ChangeMessageVisibility(d) сообщение ждёт в очереди → Следующее получение: сообщение получено уже maxReceiveCount раз?: через d
- Следующее получение: сообщение получено уже maxReceiveCount раз? → Ещё нет: доставляется повторно то же сообщение: ещё нет
- Следующее получение: сообщение получено уже maxReceiveCount раз? → Да: SQS переносит его в DLQ: да
- drop на последнем допустимом получении возвращает сообщение с видимостью 0: SQS переносит его, а не теряет
- Ждущее сообщение хранит очередь: падение сервиса не теряет ни одной переобработки
Переобработка никогда не покидает очередь. retry_after(d) делает сообщение невидимым на d, и оно
возвращается тем же сообщением, с тем же идентификатором и счётчиком получений на единицу больше.
Ничего не публикуется заново, поэтому запасной путь рантайма с повторной публикацией здесь не
работает, и .out_retry(..) на SqsQueue не компилируется.
Видимость, которая заканчивается позже 12 часов после получения, SQS отвергает.
max_attempts(n) и
dead_letter(q) вместе образуют политику
перенаправления очереди: maxReceiveCount равен n, а DLQ - q.
Подписка записывает политику в очередь при открытии, и SQS сам считает получения и переносит
исчерпавшее попытки сообщение. Одна половина без другой даёт ошибку
SqsError::IncompleteRedrive при старте.
Ждущее сообщение хранит очередь, поэтому падение сервиса не теряет ни одной переобработки: сообщение возвращается, когда заканчивается его видимость, к тому потребителю, который опросит очередь следующим.