Функции и управляемые среды выполнения — Примеры

Пример 1 — Форма обработчика вокруг своего события

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

// было: долгоживущий сервер, соединение открывается один раз при запуске
const db = connect(process.env.DB);
app.get("/orders/:id", (req, res) => res.json(db.getOrder(req.params.id)));

Решение

// область модуля: переиспользуется, если среда осталась тёплой, и создаётся заново, если нет
let pool = null;
function getPool() {
  if (pool === null) pool = connect(process.env.DB, { max: 1 });
  return pool;
}

export async function handler(event) {
  const id = event.pathParameters.id;
  return { statusCode: 200, body: JSON.stringify(await getPool().getOrder(id)) };
}

Теперь интерфейсом стало событие: идентификатор заказа приходит в событии, а не через таблицу маршрутов, и возвращаемое значение является ответом. Соединение создаётся лениво в области модуля, поэтому тёплая среда выполнения его переиспользует, а холодная всё равно работает. Ошибка — создавать соединение внутри обработчика: тогда рукопожатие оплачивается на каждом вызове, а соединения текут по мере роста нагрузки.

Пример 2 — Выбрать рамки раньше, чем это сделает тайм-аут

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

задание:          прочитать 4 часа событий, агрегировать, записать одну сводку
предлагается:     одна функция, память по умолчанию, тайм-аут по умолчанию
ожидаемый объём:  около 40 миллионов событий за ночь

Решение

шаг            что нужно                   помещается в функцию?
чтение+разбор  потоково, 2-3 мин на шард   да, небольшой обработчик
агрегация      секунды, мало памяти        да
вся работа     4 часа, десятки ГБ          нет — задание или контейнер

счёт при таком объёме, только для примера:
  за запрос + за гигабайт-секунду -> много и непредсказуемо
  один контейнер на окно          -> ровно и предсказуемо

Разбиение чтения на шарды удерживает каждый вызов внутри рамок по тайм-ауту и памяти, и каждый шард завершается за секунды. Не помещается как раз вся работа целиком: работе, измеряемой часами, место в задании или в контейнере, а узнавать об этом из продакшен-тайм-аута дорого. Прежде чем брать обязательства, сравните две цены при своей ожидаемой нагрузке: оплата за вызов вознаграждает прерывистую работу и наказывает ровный объём.