← На главную

DBOS размещает workflow-движок в Postgres — атомарность и exactly-once

02.07.2026 18:38 · hackernews

Недавно вышла статья-продолжение к предыдущему «просто используйте Postgres» для надёжных рабочих процессов. Авторы поясняют: речь шла не о том, чтобы взять любой workflow-движок, который хранит состояние в Postgres. Идея в том, что сам движок должен жить внутри той же базы данных Postgres, что и ваше приложение. Звучит сомнительно: обычно же разделяют данные и оркестрацию? Но в распределённых системах совместное размещение (co-location) — суперсила.

Когда метаданные воркфлоу и данные приложения лежат в одном Postgres, их можно обновить в одной транзакции. Это убивает частичные сбои. Строить воркфлоу, корректно обрабатывающие все граничные случаи, становится гораздо проще.

Главная проблема — идемпотентность. Обычные durable workflows чекпоинтят шаги после их завершения. Если сбой случился после выполнения шага, но до записи чекпоинта, то при восстановлении шаг выполнится снова. Например, зачисление $100 на счёт — неидемпотентная операция: повторишь — добавишь лишние деньги. Типовое решение — добавлять таблицу applied_payments и вручную проверять дубликаты.

Но если workflow-движок co-located, он может записывать чекпоинт и делать обновление базы в одной транзакции. Движок даёт шагу транзакцию; шаг обновляет данные, движок пишет чекпоинт — и всё коммитится атомарно. Если коммит прошёл — оба действия зафиксированы, шаг больше не запустится. Если сбой до коммита — откат всего, и при восстановлении шаг выполняется заново. Так достигается exactly-once для транзакционных шагов без дополнительной логики и таблиц.

Вторая типичная проблема — атомарность при записи в несколько систем (например, обновить запись в БД и отправить уведомление на склад). Классическое решение — transactional outbox: в одной транзакции обновляем данные и пишем сообщение в таблицу-outbox, а отдельный фоновый процесс забирает и рассылает. Но это добавляет сложности: нужен poll, retry, reconciliation jobs.

Co-location упрощает и это. Вместо outbox и polling можно использовать Postgres UDF (user-defined function), которая в той же транзакции, что и обновление данных, создаёт запись воркфлоу (название, очередь, входные данные). Гарантия атомарности: или обновление + enqueue, или ничего. Асинхронный worker потом забирает и выполняет воркфлоу.

Авторы из DBOS утверждают, что их подход делаетPostgres-backed durable execution простым и производительным.

Читать оригинал →