Проектирование для бессерверной модели — Примеры
Пример 1 — Обработчик, переживающий вторую доставку
Очередь доставляет обработчику событие заказа, а тот списывает деньги с покупателя и отправляет чек. Очереди могут доставить одно сообщение больше одного раза, а платформа повторяет вызов, который завершился ошибкой. Первая версия приведена ниже. Сделайте её безопасной для двойного запуска и назовите запись, доказывающую, что он не случился.
async function handle(event) {
const order = JSON.parse(event.body);
await charge(order.customer, order.total);
await sendReceipt(order.customer, order.id);
}
Решение
async function handle(event) {
const order = JSON.parse(event.body);
const first = await seen.putIfAbsent(event.id, { at: event.time });
if (!first) return { skipped: "повторное событие заказа" };
await charge(order.customer, order.total, event.id);
await sendReceipt(order.customer, order.id);
}
putIfAbsent записывает id события до любого побочного эффекта и возвращает false, если такой id уже есть, поэтому вторая доставка ничего не списывает и не отправляет чек, а доказательством служит сохранённый id. Ошибка — записывать работу после её выполнения: сбой между списанием и записью позволит повторной попытке списать снова. Ключом делайте id события, а не id заказа: один и тот же заказ может законно прийти как два разных события.
Пример 2 — Очередь перед ограниченным нижестоящим сервисом
Функция веером обращается к нижестоящему сервису, который принимает двадцать вызовов одновременно. При всплеске платформа включает троттлинг вместо постановки в очередь, и излишек отбрасывается. Текущее решение приведено ниже. Вставьте буфер и скажите, что происходит с сообщением, которое падает снова и снова.
загрузка -> функция -> 1000 параллельных вызовов -> сервис (предел 20)
^ троттлинг, излишек отброшен
Решение
загрузка -> очередь -> функция-потребитель (не больше 20 в работе) -> сервис
3 попытки -> очередь недоставленных -> оповещение
всплеск из 1000: очередь поглощает его; сервис видит не больше 20 сразу
повторный отказ: сообщение покидает очередь и видно в DLQ
Очередь и есть тот буфер, который нужен пределу параллелизма: всплеск ждёт вместо троттлинга, а настроенный потолок потребителя удерживает нижестоящий сервис на скорости, которую тот выдерживает. Сообщение, у которого не удалась ни одна попытка, попадает в очередь недоставленных, где его видно, а не теряется молча. Частая ошибка — отвечать на троттлинг новыми повторными попытками: это повторяет тот же отклонённый запрос и всё равно теряет сообщение.