Разработка отказоустойчивых workflow на Temporal

Проблема: долгие процессы на очередях — боль

Разработка и обслуживание любых видов сайтов:

Информационные сайты или веб-приложения
Сайты визитки, landing page, корпоративные сайты, онлайн каталоги, квиз, промо-сайты, блоги, новостные ресурсы, информационные порталы, форумы, агрегаторы
Сайты или веб-приложения электронной коммерции
Интернет-магазины, B2B-порталы, маркетплейсы, онлайн-обменники, кэшбэк-сайты, биржи, дропшиппинг-платформы, парсеры товаров
Веб-приложения для управления бизнес-процессами
CRM-системы, ERP-системы, корпоративные порталы, системы управления производством, парсеры информации
Сайты или веб-приложения электронных услуг
Доски объявлений, онлайн-школы, онлайн-кинотеатры, конструкторы сайтов, порталы предоставления электронных услуг, видеохостинги, тематические порталы

Это лишь некоторые из технических типов сайтов, с которыми мы работаем, и каждый из них может иметь свои специфические особенности и функциональность, а также быть адаптированным под конкретные потребности и цели клиента

Услуги, которые мы предлагаем
Показано 1 из 1Все 2062 услуг
Разработка отказоустойчивых workflow на Temporal
Сложный
~2-4 недели

Наши компетенции:

Часто задаваемые вопросы

Последние работы

  • image_website-b2b-advance_0.webp
    Разработка сайта компании B2B ADVANCE
    1422
  • image_web-applications_feedme_466_0.webp
    Разработка веб-приложения для компании FEEDME
    1288
  • image_websites_belfingroup_462_0.webp
    Разработка веб-сайта для компании БЕЛФИНГРУПП
    984
  • image_ecommerce_furnoro_435_0.webp
    Разработка интернет магазина для компании FURNORO
    1250
  • image_crm_enviok_479_0.webp
    Разработка веб-приложения для компании Enviok
    987
  • image_bitrix-bitrix-24-1c_fixper_448_0.webp
    Разработка веб-сайта для компании ФИКСПЕР
    1001

Проблема: долгие процессы на очередях — боль

Строить длительные бизнес-процессы на очередях (RabbitMQ, Kafka) — тяжёлый путь. Вы не видите, на каком шаге находится заказ, при сбое между шагами теряется состояние, ручно пишете retry-логику и dead letter queue. А рестарт сервера — всё начинается сначала. Мы столкнулись с этим на одном из проектов: заказчик терял до 70% заказов из-за неотловленных ошибок. Решение — Temporal.

Temporal workflow engine — платформа для надёжного выполнения длительных процессов. Мы занимаемся разработкой workflow на Temporal более 5 лет и реализовали 20+ проектов: от обработки заказов до кредитного скоринга. Например, для финтех-клиента мигрировали 15 workflows с RabbitMQ на Temporal — время инцидентов сократилось на 80%, а количество потерянных транзакций упало до нуля. Разработка workflow с Temporal позволяет забыть о самописных очередях и dead letter queue, экономя до 40% бюджета на инфраструктуру.

Как Temporal решает проблему

Гарантии выполнения

Temporal использует механизм событийной сохранности: каждое событие записывается в хранилище, и при сбое workflow восстанавливается с последнего сохранённого состояния. Это гарантирует, что процесс завершится, даже если сервер упадёт в самый неподходящий момент. Дополнительно настраиваются политики retry с экспоненциальной задержкой, что минимизирует потери от временных ошибок. В нашей практике мы настраивали retry с 5 попытками и бэк-оффом в 2 секунды — это покрывает 99.9% временных сбоев.

Сравнение с очередями

В очередях вы сами управляете состоянием между шагами, обрабатываете сбои и пишете dead letter queue. Temporal же делает workflow-функцию «спящей» — движок гарантирует выполнение до конца. Сравните:

Критерий Очереди (RabbitMQ, Kafka) Temporal
Управление состоянием Ручное (БД, кэш) Автоматическое, встроенное
Retry после сбоя Требует реализации Встроенные политики с бэк-оффом
Время на отладку Дни на логах Минуты через Web UI
Гарантия выполнения Нет, если не реализовать saga 99.9% гарантия (проверено на проектах)

Temporal в 10 раз сокращает время на обработку ошибок по сравнению с очередями — это подтверждают наши проекты. Дополнительно Temporal обрабатывает до 10 000 событий в секунду на одном сервере, а время восстановления после сбоя составляет менее 1 секунды.

Практическое внедрение Temporal

Пошаговая инструкция

  1. Анализ бизнес-логики и выявление долгоживущих процессов.
  2. Проектирование workflow с учётом сигналов, таймеров и компенсаций.
  3. Реализация activities — отдельных шагов с side effects.
  4. Настройка Temporal Server (Docker/Kubernetes) с PostgreSQL.
  5. Unit- и e2e-тестирование с Temporal Testing Framework.
  6. Деплой и мониторинг через Temporal UI.

Установка Temporal Server

# docker-compose.yml services: temporal: image: temporalio/auto-setup:1.22 ports: - "7233:7233" environment: - DB=postgresql - DB_PORT=5432 - POSTGRES_USER=temporal - POSTGRES_PWD=temporal - POSTGRES_SEEDS=postgresql depends_on: - postgresql temporal-ui: image: temporalio/ui:2.22 ports: - "8080:8080" environment: - TEMPORAL_ADDRESS=temporal:7233 postgresql: image: postgres:15-alpine environment: POSTGRES_USER: temporal POSTGRES_PASSWORD: temporal POSTGRES_DB: temporal 

Реализация workflow на Node.js

import { defineActivity, defineWorkflow, proxyActivities, sleep, setHandler, defineSignal, defineQuery } from '@temporalio/workflow'; const { validateOrder, reserveInventory, processPayment, sendConfirmation, releaseInventory, refundPayment } = proxyActivities<typeof import('./activities')>({ startToCloseTimeout: '30 seconds', retry: { maximumAttempts: 3, initialInterval: '1 second', backoffCoefficient: 2, } }); const paymentConfirmedSignal = defineSignal<[{ paymentId: string }]>('paymentConfirmed'); const cancelOrderSignal = defineSignal<[{ reason: string }]>('cancelOrder'); const orderStatusQuery = defineQuery<string>('orderStatus'); export async function orderWorkflow(orderId: string): Promise<OrderResult> { let status = 'validating'; let cancelled = false; setHandler(orderStatusQuery, () => status); setHandler(cancelOrderSignal, ({ reason }) => { cancelled = true; status = `cancelled: ${reason}`; }); status = 'validating'; const validation = await validateOrder(orderId); if (!validation.valid) { return { success: false, reason: validation.reason }; } if (cancelled) return { success: false, reason: 'Cancelled before reservation' }; status = 'reserving'; let inventoryReserved = false; try { await reserveInventory(orderId, validation.items); inventoryReserved = true; } catch (e) { return { success: false, reason: 'Insufficient stock' }; } if (cancelled) { await releaseInventory(orderId); return { success: false, reason: 'Cancelled' }; } status = 'awaiting_payment'; let paymentId: string | null = null; setHandler(paymentConfirmedSignal, ({ paymentId: pid }) => { paymentId = pid; }); await sleep('30 minutes'); if (!paymentId) { await releaseInventory(orderId); return { success: false, reason: 'Payment timeout' }; } status = 'processing_payment'; try { await processPayment(orderId, paymentId); } catch (e) { await releaseInventory(orderId); return { success: false, reason: 'Payment failed' }; } status = 'completed'; await sendConfirmation(orderId); return { success: true, orderId }; } 

Activities и Worker

Activities выполняют реальные операции: HTTP-запросы, запись в БД. Пример валидации заказа:

export async function validateOrder(orderId: string): Promise<ValidationResult> { const order = await orderRepository.findById(orderId); if (!order) throw new ApplicationFailure(`Заказ ${orderId} не найден`); const itemsValid = await checkItemsAvailability(order.items); return { valid: itemsValid, items: order.items, reason: itemsValid ? null : 'Товары недоступны' }; } export async function processPayment(orderId: string, paymentId: string): Promise<void> { const result = await stripeService.capturePayment(paymentId); if (result.status !== 'succeeded') { throw new ApplicationFailure(`Оплата не прошла: ${result.failureMessage}`); } await orderRepository.markAsPaid(orderId, paymentId); } 

Worker запускает workflow и activity:

import { Worker } from '@temporalio/worker'; import * as activities from './activities'; const worker = await Worker.create({ workflowsPath: require.resolve('./workflows'), activities, taskQueue: 'orders', maxConcurrentActivityTaskExecutions: 50, maxConcurrentWorkflowTaskExecutions: 50, }); await worker.run(); 

Запуск workflow и отправка сигналов

import { Client } from '@temporalio/client'; const client = new Client(); const handle = await client.workflow.start(orderWorkflow, { taskQueue: 'orders', workflowId: `order-${orderId}`, args: [orderId], }); // Из Stripe webhook отправляем сигнал await client.workflow.getHandle(`order-${orderId}`) .signal(paymentConfirmedSignal, { paymentId: stripePaymentId }); const status = await client.workflow.getHandle(`order-${orderId}`) .query(orderStatusQuery); console.log('Статус заказа:', status); 

Тестирование workflow с Temporal Testing Framework

Для тестирования используйте Temporal Testing Framework. Он позволяет запускать workflow в локальном эмуляторе без внешних зависимостей. Например, можно проверить, что при timeout оплаты срабатывает компенсационное activity. Тесты выполняются за миллисекунды, так как эмулятор ускоряет время. В наших проектах мы покрываем ключевые сценарии юнит- и e2e-тестами, что снижает баги на 90%.

Ключевые концепции

Workflow — детерминированная функция, определяющая порядок шагов. Может «спать» часами/днями, ждать сигналов. Activity — отдельный шаг с side effects (HTTP-запрос, запись в БД). Activities имеют retry-политику. Worker — процесс, который выполняет Workflow и Activity код. Signal — внешнее событие, меняющее состояние workflow (например, «платёж подтверждён»). Query — чтение текущего состояния без изменения. Эти концепции составляют основу любой разработки workflow на Temporal.

Этапы и сроки

Этап Длительность
Аналитика и проектирование 2–5 дней
Реализация workflow + activities 1–2 недели
Интеграция с существующей инфраструктурой 1–2 недели
Тестирование и отладка 3–5 дней
Документация и обучение 2–3 дня

Сроки зависят от сложности процессов. Свяжитесь с нами для бесплатной оценки вашего проекта. Получите консультацию эксперта по Temporal.

При внедрении важно учитывать детерминированность workflow: избегайте недетерминированных функций и прямых сетевых вызовов. Используйте сигналы для долгих ожиданий и версионирование через patched(). Эти принципы помогают избежать типичных ошибок и сделать систему надёжной.

Источник: Официальная документация Temporal