Время - деньги с бессерверным режимом - узнайте, как избежать состояний ожидания

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

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

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

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

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

Часто можно увидеть, что какое-то управление состоянием или функция контроллера вызывают последовательность других функций, оставляя вызывающую функцию сидеть и вертеть пальцами, ничего не делая:

В Node это стало более распространенным с момента появления async / await, что делает тривиальным превращение асинхронного кода в последовательные операции. При таком подходе вы платите дважды - один раз за каждую «ступенчатую» функцию и еще раз за ожидающую, контролирующую функцию. Это не имеет большого значения для редко используемых функций, но, по моему опыту, это плохая практика по двум причинам:

  1. В реальном мире эти функции контроллера вызывают другие функции контроллера, которые вызывают другие вложенные функции контроллера, поэтому у вас может быть многие функции, все ожидающие возвращаемого значения, все выполняющиеся одновременно и вызывающие ваш AWS. законопроект.
  2. Когда что-то значительно масштабируется, это становится более заметным, и люди начинают утверждать, что Lambda не так дёшево, как они думали.

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

В этой версии вы не платите за время, необходимое DynamoDB или S3, чтобы завершить свою работу:

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

Вторая альтернатива - AWS Step Functions, которая берет на себя роль задачи управления состоянием или контроллера. Это действительно отличный сервис, который решает всю эту проблему, и вы должны использовать его для всего, за исключением того, что он невероятно дорогой. Хотя 0,025 доллара за 1000 переходов между состояниями звучит не так уж и много, они могут быстро вырасти, если у вас более сложные рабочие процессы.

Step Functions отлично подходят для долговременных рабочих процессов, таких как управление заказами, но обычно излишни для управления несколькими краткосрочными шагами по перемещению данных по AWS. На практике меня отпугивает стоимость, но как только они сделают это бесплатной услугой (#awswishlist!), Управление состоянием в бессерверном режиме станет проще простого.

Хорошая асинхронность: несколько записей в источнике событий

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

К счастью, в Node эти записи просто обрабатывать параллельно. В этом примере наша функция processRecord устанавливает таймер на 1 секунду, и в тестовом событии есть 3 записи, но общее время выполнения было всего 1048 мс, а не ~ 3000 мс:

Почему? Функция processRecord вызывается для каждой записи (почти) одновременно, и Promise.all ожидает выполнения всех обещаний, что позволяет вам выполнять все запросы параллельно. На практике вы должны учитывать отклоненные обещания.

Конечно, в некоторых случаях вы не сможете запускать их параллельно, но там, где вы можете, это значительно сэкономит время. Хороший Async создает победу, и подход Promise.all / Records.map - простой ответ на эту проблему.

Хорошая асинхронность: несколько одновременных задач

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

Мой код выглядит так:

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

TL;DR!

  • В бессерверном режиме время - деньги - мы должны оптимизировать по затраченному времени, а не по количеству созданных функций. Цель состоит в том, чтобы не ждать и не платить, когда Lambda ничего не делает.
  • Асинхронность в NodeJS проста - вы можете переходить от блок-схемы к коду с помощью всего нескольких строк Node, а ключевые слова async / await устраняют ад обратного вызова.
  • Плохая асинхронность создает функции контроллера, которые ждут, пока сервисы AWS что-то сделают. Когда вы ждете AWS, вы часто можете использовать события или пошаговые функции, чтобы найти лучший (и более дешевый) способ.
  • Хорошая асинхронность может существенно повлиять на время выполнения, выполняясь параллельно там, где это имеет смысл. Общие примеры включают несколько записей о событиях и несколько одновременных задач.