Site icon IT Education Center Blog – блог навчального центру DevOps – ITEDU by NETFORCE Group

Інструкція по AWS Lambda Durable Functions

У serverless-архітектурі AWS Lambda добре працює для коротких завдань — обробки подій або запитів API. Але коли процес триває години чи дні, чекає на зовнішні події або потребує збереження проміжного стану, цей сервіс просто не підходить.

На допомогу приходять AWS Lambda Durable Functions. Вони дозволяють:

  1. створювати stateful функції Lambda, які пам’ятають свій стан;
  2. призупиняти виконання і відновлювати його після отримання зовнішньої події;
  3. автоматично повторювати кроки у разі помилок;
  4. інтегруватися з сервісами AWS, як EventBridge, S3 або DynamoDB, без додаткових складних рішень.

Простіше кажучи, Durable Functions допомагають будувати довготривалі, надійні serverless workflow, зберігаючи простоту та масштабованість Lambda.

Архітектура та принцип роботи AWS Lambda Durable Functions

Durable Functions додають до Lambda можливість пам’ятати стан і контролювати порядок виконання кроків. В основі лежать два ключові компоненти:

  1. Оркестратор — це функція, яка керує воркфлоу. Вона визначає, які кроки виконуються, в якому порядку, і коли очікувати зовнішніх подій. Оркестратор не виконує бізнес-логіку безпосередньо, а викликає інші функції.
  2. Activity-функції — це окремі кроки процесу: перевірка наявності товару, обробка оплати, надсилання повідомлення клієнту тощо. Саме вони виконують реальну роботу.

Механізм роботи простий

Оркестратор керує воркфлоу, запускаючи activity-функції одну за одною або одночасно.

Якщо на якомусь кроці потрібно чекати зовнішню подію, він призупиняє виконання і зберігає стан процесу. Lambda в цей час не використовує ресурси, тому ви не платите за простої. 

Коли подія надходить, виконання автоматично відновлюється з того місця, де воно зупинилося, і воркфлоу продовжується без втрати даних.

Наприклад, типовий воркфлоу обробки замовлення виглядає так:

Оркестратор викликає кожен крок, чекає на зовнішні події або таймери і контролює повтори у разі помилок.

Початок роботи: налаштування середовища та 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.

Після запуску:

Оскільки воркфлоу містить очікування події ShippingConfirmed, він перейде у стан призупинення.

Симуляція зовнішньої події

Щоб завершити процес, потрібно надіслати подію відновлення (resumption API), використовуючи instance ID, який ви отримали з логів CloudWatch.

Після надходження події:

Розширені можливості та обробка помилок

У реальних системах процеси рідко працюють ідеально з першого разу. Платіж може не пройти, зовнішній 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 у цей період не споживаються.

Готовність до продакшну

Для таких сценаріїв варто врахувати:

Післяслово

AWS Lambda традиційно асоціюється з короткими, подієвими задачами. Проте з використанням Durable Functions ви отримуєте можливість будувати довготривалі воркфлоу, які зберігають стан, очікують зовнішні події, автоматично відновлюються після пауз та коректно обробляють помилки.

Потрібно освіжити знання про AWS? Рекомендуємо курс, де всього за 3 тижні ви повторите базу та дізнаєтесь багато фіч про головні сервіси AWS.

Exit mobile version