Как перестать чинить распределенные системы и начать жить с Restate
Представьте ситуацию: вы пишете микросервис для обработки платежей. В середине процесса сеть моргает, база данных уходит в таймаут или контейнер просто рестартует. Что происходит с транзакцией? В классическом сценарии вам нужно вручную прописывать логику ретраев, следить за идемпотентностью, настраивать очереди сообщений и, скорее всего, внедрять какую-нибудь сложную сагу. Это больно, долго и превращает код в лабиринт из обработчиков ошибок.
Недавно я наткнулся на проект Restate, который пытается решить эту проблему на уровне инфраструктуры. Разработчики называют это «Durable Execution» — отказоустойчивое выполнение кода. Идея в том, что ваш код становится бессмертным: если процесс упадет, Restate поднимет его ровно на том же месте, сохранив все переменные и состояние.
Что это вообще такое
Restate — это бинарный файл на Rust, который работает как прокси-сервер и координатор для ваших сервисов. Он берет на себя всю грязную работу по управлению состоянием и вызовами. Вы пишете обычный код на TypeScript, Python или Go, используете SDK, и внезапно ваши функции превращаются в надежные воркфлоу.
Главное отличие от тяжеловесных решений вроде Temporal в том, что Restate гораздо легче запустить. Вам не нужна огромная обвязка из баз данных и сложных воркеров. Можно просто запустить один бинарник или Docker-контейнер и начать работать.
Чем Restate помогает на практике
В проекте заложено несколько крутых концепций, которые реально упрощают жизнь.
Гарантированное выполнение
Если вы вызвали функцию через Restate, она выполнится до конца. Точка. Если сервер с кодом упал, Restate дождется его возвращения и продолжит выполнение с последнего успешного шага. Вам больше не нужно думать, отправилось ли письмо пользователю дважды или списались ли деньги.
Умные таймеры и промисы
Обычно реализовать задержку в распределенной системе — это квест. Нужно положить сообщение в очередь с задержкой или настроить cron-задачу. В Restate вы просто пишете ctx.sleep(duration). При этом поток не блокируется впустую: сервис может вообще выключиться, а через три дня Restate его «разбудит» и продолжит выполнение.
Состояние прямо в коде
Restate позволяет хранить K/V состояние, привязанное к конкретной сущности (например, к ID пользователя). Это выглядит как обычная работа с объектом, но под капотом Restate гарантирует, что данные консистентны и всегда доступны вместе с запросом. Это особенно удобно для Serverless-архитектур, где функции обычно не имеют памяти.
Как это выглядит в коде
Допустим, нам нужно реализовать процесс регистрации пользователя с подтверждением по почте. На TypeScript с использованием Restate SDK это будет выглядеть примерно так:
import * as restate from "@restatedev/restate-sdk";
const userService = restate.service({
name: "users",
handlers: {
register: async (ctx: restate.Context, user: { id: string, email: string }) => {
// Сохраняем состояние
ctx.set("status", "pending");
// Отправляем письмо (Restate гарантирует, что это случится 1 раз)
await ctx.run(() => sendWelcomeEmail(user.email));
// Ждем подтверждения или таймаута в 24 часа
const confirmed = await ctx.awakeable<boolean>("email-confirmed");
if (confirmed) {
ctx.set("status", "active");
}
}
}
});
Здесь ctx.run гарантирует, что побочный эффект (отправка письма) выполнится успешно, а результат будет закеширован. Если функция упадет после отправки, при перезапуске Restate просто пропустит этот шаг, зная, что он уже сделан.
Техническая сторона вопроса
Проект написан на Rust, что дает отличную производительность. Архитектурно Restate выступает в роли инвайкера. Он получает входящие запросы по HTTP/gRPC, записывает их в свой журнал (log) и вызывает ваши обработчики.
Интересно, что Restate умеет «подвешивать» выполнение. Если ваш код ждет ответа от внешнего API или таймера, Restate освобождает ресурсы. Когда событие наступает, он восстанавливает контекст выполнения. Это позволяет запускать тысячи длительных процессов на скромном железе.
Кому стоит попробовать
Restate отлично закроет дыры в проектах, где:
- Много цепочек вызовов между микросервисами.
- Нужно строить сложные цепочки действий (саги, воркфлоу).
- Используются AI-агенты, которым нужно долго ждать ответа от LLM и сохранять контекст диалога.
- Есть задачи с отложенным выполнением (напомнить о брошенной корзине через 2 часа).
Проект сейчас активно развивается, у него уже почти 4000 звезд на GitHub. SDK доступны для TypeScript/JavaScript, Java/Kotlin, Python, Go и Rust.
Конечно, тащить новую инфраструктурную штуку в продакшн крупного банка завтра не стоит — нужно сначала пощупать локально. Но для стартапов или новых фич в существующих проектах это может сэкономить недели разработки.
Попробовать можно буквально за пару минут:
brew install restatedev/tap/restate-server
restate-server
И всё, у вас есть локальная среда для запуска отказоустойчивых приложений. Пожалуй, это самый низкий порог входа в мир Durable Execution на текущий момент.
