Інструкція по AWS Lambda Durable Functions
У serverless-архітектурі AWS Lambda добре працює для коротких завдань — обробки подій або запитів API. Але коли процес триває години чи дні, чекає на зовнішні події або потребує збереження проміжного стану, цей сервіс просто не підходить.
На допомогу приходять AWS Lambda Durable Functions. Вони дозволяють:
- створювати stateful функції Lambda, які пам’ятають свій стан;
- призупиняти виконання і відновлювати його після отримання зовнішньої події;
- автоматично повторювати кроки у разі помилок;
- інтегруватися з сервісами AWS, як EventBridge, S3 або DynamoDB, без додаткових складних рішень.
Простіше кажучи, Durable Functions допомагають будувати довготривалі, надійні serverless workflow, зберігаючи простоту та масштабованість Lambda.
Архітектура та принцип роботи AWS Lambda Durable Functions
Durable Functions додають до Lambda можливість пам’ятати стан і контролювати порядок виконання кроків. В основі лежать два ключові компоненти:
- Оркестратор — це функція, яка керує воркфлоу. Вона визначає, які кроки виконуються, в якому порядку, і коли очікувати зовнішніх подій. Оркестратор не виконує бізнес-логіку безпосередньо, а викликає інші функції.
- Activity-функції — це окремі кроки процесу: перевірка наявності товару, обробка оплати, надсилання повідомлення клієнту тощо. Саме вони виконують реальну роботу.
Механізм роботи простий
Оркестратор керує воркфлоу, запускаючи activity-функції одну за одною або одночасно.
Якщо на якомусь кроці потрібно чекати зовнішню подію, він призупиняє виконання і зберігає стан процесу. Lambda в цей час не використовує ресурси, тому ви не платите за простої.
Коли подія надходить, виконання автоматично відновлюється з того місця, де воно зупинилося, і воркфлоу продовжується без втрати даних.
Наприклад, типовий воркфлоу обробки замовлення виглядає так:
- перевірка наявності товару (CheckInventory);
- оплата замовлення (ProcessPayment);
- очікування підтвердження доставки (ShippingConfirmed);
- надсилання повідомлення клієнту (SendNotification).
Оркестратор викликає кожен крок, чекає на зовнішні події або таймери і контролює повтори у разі помилок.
Початок роботи: налаштування середовища та SDK
Перед тим як писати свій перший workflow, потрібно підготувати середовище. Для цього потрібен акаунт AWS, встановлений AWS CLI та одна з підтримуваних мов, наприклад Node.js або Python.
Далі створюємо новий проєкт і встановлюємо SDK для Durable Functions. У терміналі це виглядає так:
mkdir durable-demo
cd durable-demo
npm init -y
npm install @aws-sdk/durable-functions
Після цього у AWS Console створюють функцію Lambda з назвою “MyDurableWorkflow”, вибирають середовище виконання Node.js 20.x і призначають роль AWSLambdaBasicExecutionRole. Також додають політики доступу до потрібних сервісів, наприклад SQS або DynamoDB.
Написання першої Durable-функції
У цьому прикладі ми створимо процес обробки замовлення: перевірка доступності позицій, оплата, очікування підтвердження доставки та фінальне сповіщення.У файлі index.js додайте наступний код:
const { DurableFunctions } = require('@aws-sdk/durable-functions');
exports.handler = DurableFunctions.orchestrator(function* (context) {
const order = context.input;
// Check inventory
const inventoryResult = yield context.callActivity('CheckInventory', order.items);
if (!inventoryResult.available) throw new Error('Out of stock');
// Process payment
yield context.callActivity('ProcessPayment', order.paymentInfo);
// Wait for shipping (suspend)
const shippingEvent = yield context.waitForExternalEvent('ShippingConfirmed', { timeout: 'P7D' });
// Notify customer
yield context.callActivity('SendNotification', { email: order.email, message: 'Order shipped!' });
return { status: 'Completed', orderId: order.id };
});
// Activity functions
exports.CheckInventory = async (items) => ({ available: true });
exports.ProcessPayment = async (paymentInfo) => ({ success: true });
exports.SendNotification = async ({ email, message }) => console.log(`Email to ${email}: ${message}`);
Що важливо зрозуміти?
Оркестратор описує логіку всього процесу. Він не виконує бізнес-операції напряму — для цього використовуються activity-функції.Ключовий момент — yield. Завдяки цьому механізму виконання може бути призупинене, а стан воркфлоу автоматично зберігається. Особливо це важливо на рядку:
yield context.waitForExternalEvent('ShippingConfirmed', { timeout: 'P7D' });
Тут процес ставиться на паузу до 7 днів. Lambda не займає ресурси в цей час і не споживає обчислювальний час. Після надходження події виконання відновлюється з того самого місця.
Деплой і тестування Durable Function
Після написання коду потрібно завантажити його в AWS Lambda та перевірити роботу воркфлоу.
Пакування та оновлення функції
Спочатку створіть архів із кодом:
zip -r function.zip .
Далі оновіть код у функції Lambda:
aws lambda update-function-code --function-name MyDurableWorkflow --zip-file fileb://function.zip
Ця команда завантажує нову версію коду в уже створену функцію MyDurableWorkflow.
Запуск workflow
Тепер можна ініціювати виконання:
aws lambda invoke --function-name MyDurableWorkflow --payload '{"orderId": "123", "items": [{"id": "1"}], "paymentInfo": {"amount": 100}, "email": "user@example.com"}' output.json
У payload передаються дані замовлення. Саме їх оркестратор отримує через context.input.
Після запуску:
- перевірте логи у CloudWatch Logs;
- знайдіть instance ID workflow;
- переконайтеся, що кроки CheckInventory та ProcessPayment виконалися.
Оскільки воркфлоу містить очікування події ShippingConfirmed, він перейде у стан призупинення.
Симуляція зовнішньої події
Щоб завершити процес, потрібно надіслати подію відновлення (resumption API), використовуючи instance ID, який ви отримали з логів CloudWatch.
Після надходження події:
- виконання продовжиться з точки очікування;
- буде викликана SendNotification;
- воркфлоу завершиться зі статусом Completed.
Розширені можливості та обробка помилок
У реальних системах процеси рідко працюють ідеально з першого разу. Платіж може не пройти, зовнішній API — тимчасово не відповідати, а окремі кроки можуть потребувати паралельної обробки.
Повторні спроби (Retries)
Щоб автоматично повторювати виконання activity-функції у разі помилки, використовуйте callActivityWithRetry:
yield context.callActivityWithRetry('ProcessPayment', { retryOptions: { maxAttempts: 3 } }, order.paymentInfo);
У цьому випадку ProcessPayment буде виконано до трьох разів, якщо виникне помилка. Це особливо корисно для інтеграцій із зовнішніми сервісами.
Паралельна обробка (fan-out / fan-in)
Якщо потрібно обробити кілька замовлень одночасно, можна запустити activity-функції паралельно:
const tasks = orders.map(order => context.callActivity('ProcessOrder', order));
const results = yield context.whenAll(tasks);
Тут створюється масив задач, які виконуються паралельно. whenAll очікує завершення всіх процесів і повертає результати. Це дозволяє значно прискорити обробку великих обсягів даних.
Таймери та відкладені дії
Іноді потрібно зробити паузу перед наступним кроком — наприклад, відкласти обробку на 24 години. Для цього використовується таймер:
yield context.createTimer(new Date(Date.now() + 24 * 60 * 60 * 1000)); // 24-hour delay
Виконання буде призупинене до зазначеного часу. Як і у випадку з очікуванням події, ресурси Lambda у цей період не споживаються.
Готовність до продакшну
Для таких сценаріїв варто врахувати:
- інтеграцію з VPC, якщо воркфлоу працює з приватними ресурсами;
- використання Lambda Layers для спільних залежностей;
- версіонування функцій, щоб довготривалі воркфлоу залишалися стабільними навіть під час оновлень.
Післяслово
AWS Lambda традиційно асоціюється з короткими, подієвими задачами. Проте з використанням Durable Functions ви отримуєте можливість будувати довготривалі воркфлоу, які зберігають стан, очікують зовнішні події, автоматично відновлюються після пауз та коректно обробляють помилки.
Потрібно освіжити знання про AWS? Рекомендуємо курс, де всього за 3 тижні ви повторите базу та дізнаєтесь багато фіч про головні сервіси AWS.