Skip to content
Published on

Durable Execution エンジン 2026 — Temporal・Restate・Inngest・Trigger.dev・DBOS 深掘り比較:cron とリトライ地獄から抜け出す道

Authors

プロローグ — cron とリトライ地獄から抜け出す道

2019 年の backend engineer はこんな code を書いていた。「決済を受け、5 分後に receipt を送り、2 日後にレビュー依頼メールを発送する」。答えはたいていこう。

  • 決済処理 — 同期 API。
  • Receipt — queue(SQS・RabbitMQ)に push、worker が 5 分遅延処理。
  • レビュー mail — cron で毎晩 scan、「2 日経過した決済」を選んで送信。
  • 失敗時? retry_count column を作って、7 日後に諦める。

この pattern は動いた。ただし毎回新しく書き、毎回 bug が出て、毎回 observability が無かった。「どこまで進んだか」を見るには DB を直接掘るしかなかった。

2026 年の答えは違う。

@workflow
async function postPurchaseFlow(orderId: string) {
  await chargeCustomer(orderId)
  await sleep('5 minutes')
  await sendReceipt(orderId)
  await sleep('2 days')
  await sendReviewRequest(orderId)
}

これで終わりだ。関数そのものが workflow 定義になる。server が死んでも、5 分後でも、2 日後でも、関数はまさにその行から再開される。queue も cron も retry_count column も無い。

この魔法の名前が Durable Execution(以下 DE)。2024 年から本格的に話題になり、2026 年には backend infrastructure の標準カテゴリになった。

  • Temporal は 2026 年 2 月の Series D で 3 億ドルを 50 億ドルの valuation で調達。累計 9.1 兆 action execution、AI native 企業のみで 1.86 兆。
  • AWS は re:Invent 2025 で Lambda Durable Functions を発表。
  • Cloudflare Workflows が GA。
  • Vercel は Workflow DevKit をリリース。
  • Inngest・Trigger.dev・Restate・DBOS もそれぞれの武器で share を伸ばした。

なぜ今か? AI agent である。1 時間にわたって tool を 30 回呼ぶ agent loop は、server が一度でも死ぬと最初からやり直しになる。それは即ち請求書 — token cost が一度壊れるごとに 5 ドルずつ蒸発する。DE はそれを防ぐ。

この記事の流れはこう。

  1. Durable Execution とは何か — deterministic replay と workflow-as-code
  2. 2026 年の engine landscape 一覧 — comparison matrix
  3. Temporal — DE の標準になった経緯
  4. Restate — 軽量な挑戦者
  5. Inngest — event 駆動の DX 優位
  6. Trigger.dev — Next.js 時代の DE
  7. DBOS — Postgres 1 つで済む light-weight
  8. 同じ workflow を 3 つの engine で — 実 code 比較
  9. AWS Step Functions・Azure Durable Functions・Cadence の立ち位置
  10. AI agent と DE — なぜ爆発したか
  11. 導入 decision tree と anti-pattern

読み終えたら「自分のチームに何が合うか」が 5 分で決まるべきだ。


1 章 · Durable Execution とは何か

1.1 定義 — 意図と実行の分離

Durable Execution は関数の意図(business logic)と実際の実行(いつ・どこで・何回回すか)を分離する paradigm である。関数は普通の code に見えるが、runtime が各 step の進捗を永続 storage に checkpointing する。server が死ぬと別の worker がその行から継続する。

中核の保証:

  • Exactly-once 効果: 同じ step は 2 回実行されない。副作用は 1 回だけ。
  • 長時間 sleep: sleep('30 days') がそのまま意味を持つ。30 日後にも起きる。
  • State 付き retry: 失敗した step だけ retry、成功した step の結果は記憶。
  • Deterministic replay: worker が死んで蘇ると history を replay して全く同じ状態に復元。

1.2 2 つの mechanism

DE engine は通常 2 つの流派のどちらか。

Mechanism説明代表
Journal-based replay完了した全 step を append-only log に記録。worker が起きると最初から code を再実行するが、すでに記録された結果は再呼び出しせず cache から使う。Temporal・Cadence・Restate
Database checkpointing各 step 終了時に DB transaction で state を保存。再開時に DB から最終 state を読み、次の step から実行。DBOS・Inngest・Trigger.dev(系統で異なる)

Journal 系は code を deterministic に書く制約がある。例えば Math.random()Date.now() を直接呼ぶと replay 時に違う値が出て壊れる。engine が提供する deterministic API を使う必要がある。

DB checkpointing は制約は緩いが、step の input/output serialization・DB 負荷・transaction 境界が運用の中核になる。

1.3 Workflow-as-code モデル

昔の workflow engine(例: Airflow・初期 Cadence・Step Functions)は DAG・JSON・YAML で書いた。DE はただの code である。iffortryawait がそのまま意味を持つ。

@workflow
async function orderFlow(input: OrderInput) {
  const payment = await ctx.run('charge', () => chargeCard(input))

  try {
    await ctx.run('ship', () => shipItem(input))
  } catch (e) {
    // saga: compensating transaction
    await ctx.run('refund', () => refundCard(payment))
    throw e
  }

  await ctx.sleep('1 day')
  await ctx.run('review-request', () => sendReviewMail(input))
}

これは普通の JS だ。だが runtime は ctx.run ごとに結果を checkpointing し、ctx.sleep は worker memory を占有せず別の worker で再開される。

1.4 DE が解く pattern

DE が輝く workflow の形:

  • 長時間の承認 flow — 休暇申請、manager が 3 日後に click して進む。
  • Saga・compensating transaction — 決済 → 出荷 → mail、どこで失敗しても補償が走る。
  • State 付き retry — 外部 API が 5 回失敗しても idempotent に retry、進捗を保つ。
  • Schedule・fan-out — 毎日午前 0 時に user 1 万人へ push 通知、各 push 結果を追跡。
  • AI agent loop — LLM 呼び出し → tool 実行 → 次の LLM、途中で死んでも context 維持。

1.5 DE が要らない場合

すべての workflow が DE を呼ぶわけではない。

  • 単純な request-response: 100ms 以内に終わるなら HTTP・gRPC で十分。
  • 読み取り専用 pipeline: ETL・batch 分析は Spark・dbt・Airflow が適切。
  • 1 日 100 件未満: 運用負担が利得を上回る。log・DB column の方が安い。
  • Team が 1 人: DE engine 自体の learning curve が手書きの cost を超える。

「workflow が 5 分を超え、外部呼び出しが 3 つ以上で、失敗が高い」→ DE を検討せよ。


2 章 · 2026 年 engine 比較 matrix

項目TemporalRestateInngestTrigger.devDBOS
カテゴリDE 標準・enterpriseDE + state・軽量DE + event・serverlessDE + Next.js 親和DE library・Postgres
Language SDKGo・Java・TS・Python・.NET・PHP・RubyTS・Java・Kotlin・Go・Python・RustTS・Python・Go・JavaTS・PythonTS・Python・Java・Go
Self-host可(Apache 2.0)可(single binary・BSL→Apache)可(Apache 2.0、Inngest server)可(Apache 2.0、v4)可(Apache 2.0、library)
Infra 依存自前 server + Cassandra/PostgresSingle binary + RocksDB自前 server + Postgres自前 server + Postgres + RedisPostgres のみ
Cloud 価格約 0.00025 USD/action から初期段階の SaaS実行数 + event の freemium同時実行 + 月額 freemiumcheckpoint 100 万あたり 50 USD
無料 tierSelf-host 無料Self-host 無料5 万実行/月 無料5 USD クレジット/月Self-host 無料
Workflow modelDeterministic 関数(journal)Deterministic 関数(journal + state key)Step 関数(event trigger)Task 関数(trigger 駆動)Transactional 関数(Postgres)
AI agent 親和度非常に高い(OpenAI Codex 採用)高い(Pydantic AI 統合)非常に高い(Agent Kit)非常に高い(Session・Realtime)中(library 統合)
ObservabilityWeb UI + tctl + OTelWeb UI + OTelWeb UI + live streamWeb UI + live log・triggerDB query + dashboard
最強点scale・multi-language・成熟度軽量・single binaryevent 駆動・DX・価格TS full-stack・time-limit 無し単純・Postgres 1 つ
弱点learning curve・運用負担ecosystem 新生multi-language が弱いTS 中心deterministic replay 無し
想定 user大企業・金融・AI infrafull-stack backend・DB 親和TS・Python startupNext.js・full-stack teamPostgres 中心 backend

この表 1 つで意思決定の 80% が決まる。どの行がチームの deal-breaker かを抜き出せ。たいていは 3 つのどれかだ — 言語・self-host 義務・価格 model。


3 章 · Temporal — DE の標準になった経緯

3.1 出資・採用・規模

Temporal は 2019 年、Cadence(Uber で Maxim Fateev・Samar Abbas が作った OSS)を離れた同じ team による後継だ。2026 年 2 月の Series D は 3 億ドル、valuation 50 億ドル。累計 9.1 兆 action 実行、AI native 企業 1.86 兆。OpenAI Codex が Temporal で毎日数百万件の coding agent request を処理する公開事例がある。NVIDIA・Netflix を含む 3,000 以上の有料 customer。

3.2 Architecture — journal と worker の分離

中核 component:

  • Temporal Server: workflow state(journal = Event History)を保存。Cassandra・Postgres・MySQL backend。
  • Worker: user code を実行。server と long-poll で通信。
  • 内部的に Frontend・History・Matching・Worker service に分割可能。

Worker は state を持たない。workflow 進行 state はすべて server にある。worker が死ぬと別の worker が同じ workflow の次の task を取って history を replay し継続する。

3.3 Deterministic workflow code — 制約と報酬

ルールは単純だが厳格。workflow 関数の中では:

  • 時刻を直接呼ぶな(Date.now() 禁止)。workflow.now()
  • random を直接呼ぶな。workflow.random()
  • 外部呼び出しを直接するな。proxyActivities で activity を呼ぶ。
  • multi-thread・global 変数禁止。

この制約を守ると replay が deterministic になる。workflow code を最初から回しても同じ step sequence が出る。server の history を辿り、すでに終わった activity は cache から結果を返す。

3.4 Activity — 外界と接する場所

// activities.ts
export async function chargeCard(orderId: string): Promise<PaymentReceipt> {
  return await stripe.charge({ orderId })
}

export async function shipItem(orderId: string): Promise<TrackingNumber> {
  return await fedex.createShipment({ orderId })
}

// workflows.ts
import { proxyActivities, sleep } from '@temporalio/workflow'
import type * as activities from './activities'

const { chargeCard, shipItem } = proxyActivities<typeof activities>({
  startToCloseTimeout: '30 seconds',
  retry: { maximumAttempts: 5, initialInterval: '1s', backoffCoefficient: 2 },
})

export async function orderFlow(orderId: string) {
  const receipt = await chargeCard(orderId)
  await sleep('5 minutes')
  const tracking = await shipItem(orderId)
  return { receipt, tracking }
}

Activity は普通の関数。retry・timeout・heartbeat・idempotency が option として乗る。

3.5 価格と self-host

  • Temporal Cloud: 約 0.00025 USD/action。保存 action・active worker・保存期間(7・30・90 日)が請求の倍率になる。低 traffic で月 200 USD から。
  • Self-host: Apache 2.0、license 上は無料。だが Cassandra/Postgres の HA 運用・tuning・backup は楽じゃない。現実的には月 2,500 ~ 4,500 USD の infra + 人件費。

規模と成熟度が武器。学習曲線の急峻さが小 team には弱点。


4 章 · Restate — 軽量な挑戦者

4.1 立ち位置

Restate は「Temporal が重すぎる」という問題意識から始まった。Single binary(Rust 製、内蔵 RocksDB)で動き、外部 DB 依存がない。既存 service の前段に proxy のように挟まり呼び出し journal を捕える。

4.2 核心の差別点

  • Single binary: Docker 1 行、別途 DB・message queue 不要。
  • State と通信を一緒に: workflow 内で key-value state(ctx.set('cart', items))を扱える。Redis・DB を挟まなくても OK。
  • HTTP・gRPC handler: workflow がそのまま HTTP service である。外部からただ呼ぶだけ。
  • Deterministic 関数 model: Temporal に似た制約。JS・TS・Java・Kotlin・Go・Python・Rust SDK。

4.3 Code の形

import * as restate from '@restatedev/restate-sdk'

const order = restate.workflow({
  name: 'order',
  handlers: {
    run: async (ctx: restate.WorkflowContext, orderId: string) => {
      const receipt = await ctx.run('charge', () => chargeCard(orderId))
      await ctx.sleep(5 * 60_000)
      const tracking = await ctx.run('ship', () => shipItem(orderId))
      ctx.set('status', 'shipped')
      return { receipt, tracking }
    },
  },
})

restate.endpoint().bind(order).listen(9080)

これを立てて、Restate server を 1 台立てて、登録すれば終わり。

4.4 価格と self-host

  • Restate Cloud: 2025 年後半 GA、初期は free tier + 近い将来に paid tier を新設予定。SLA を強化中。
  • Self-host: BSL → Apache 2.0(時間経過後)license。single binary は無料。

Temporal より運用負担が小さい、DBOS より workflow model が表現力ある。ただし ecosystem はまだ新生。


5 章 · Inngest — event 駆動の DX 優位

5.1 立ち位置

Inngest の直観は「event → 関数」モデル。関数が event を subscribe し、関数内のすべての step.run 呼び出しが自動で checkpointing される。workflow 定義が別に無い — 関数自体が workflow。

5.2 Code の形

import { Inngest } from 'inngest'

const inngest = new Inngest({ id: 'shop' })

export const orderFlow = inngest.createFunction(
  { id: 'order-flow' },
  { event: 'order.placed' },
  async ({ event, step }) => {
    const receipt = await step.run('charge', async () => {
      return await chargeCard(event.data.orderId)
    })

    await step.sleep('wait-5m', '5m')

    const tracking = await step.run('ship', async () => {
      return await shipItem(event.data.orderId)
    })

    await step.sleep('wait-2d', '2d')

    await step.run('review-mail', async () => {
      return await sendReviewMail(event.data.orderId)
    })

    return { receipt, tracking }
  }
)

Event → 関数モデルなので domain event 発行と自然に噛み合う。AWS EventBridge 感覚で self-host 可能。

5.3 2026 年の注目点

  • Checkpointing 発表: step 間 latency を almost-zero に縮め workflow 総実行時間を 50% 短縮。
  • Agent Kit: AI agent 構築用 helper。tool call・LLM response を自動で step に分解。
  • Agent Skills: Claude Code・Cursor・Windsurf 向け事前定義 skill 6 種。
  • Self-host Inngest server: production grade self-host、Postgres backend。

5.4 価格

  • Cloud: 月 5 万実行まで無料。それ以降は usage-based + event 単位課金。最初の 100 万 event は無料。
  • Self-host: Apache 2.0、無料。

TypeScript・Python team、特に Vercel・Next.js deploy と相性がよい。multi-language が弱い。


6 章 · Trigger.dev — Next.js 時代の DE

6.1 立ち位置

Trigger.dev は「Vercel で回せない長い task をどこで回すか」という切実な問題に答える tool だ。v4 で deterministic replay model・session 基盤の双方向 channel・realtime log streaming を導入。

6.2 Code の形

import { task, logger, wait } from '@trigger.dev/sdk/v3'

export const orderFlow = task({
  id: 'order-flow',
  retry: { maxAttempts: 5, factor: 2, minTimeoutInMs: 1000 },
  run: async (payload: { orderId: string }, { ctx }) => {
    const receipt = await chargeCard(payload.orderId)
    logger.info('charged', { receipt })

    await wait.for({ minutes: 5 })

    const tracking = await shipItem(payload.orderId)
    logger.info('shipped', { tracking })

    await wait.for({ days: 2 })

    await sendReviewMail(payload.orderId)

    return { receipt, tracking }
  },
})

3 つの強み:

  1. Timeout 無し: Lambda・Vercel・Cloudflare の時間制限なしに時間単位の task が可能。
  2. 待機中は課金されない: task が wait に入ると container が凍結され課金停止。
  3. Realtime log: user が実行を見ている間 log が SSE で流れる。

6.3 2026 年の注目点

  • Session primitive: 単一 run を超えて生きる双方向 I/O channel。chat agent の manager 役。
  • Concurrency・queue 制御: task ごとに同時実行 cap と queue priority。
  • v4 deterministic mode: opt-in。より強い正しさ保証が要るとき。

6.4 価格

  • Free: 月 5 USD クレジット、同時実行 10。
  • Hobby: 月 10 USD。
  • Pro: 月 50 USD、同時実行 200+。
  • Enterprise: 別途。
  • Self-host: v4 が Apache 2.0。

Next.js・full-stack TS team の default に近い選択肢になった。


7 章 · DBOS — Postgres さえあればよい light-weight

7.1 立ち位置

DBOS は「Postgres があるのにまた infra を立てるのか」に答える。Library で完結。別 server 無し。workflow state は user の Postgres にそのまま保存。

7.2 Code の形(TypeScript)

import { DBOS, Workflow, Step } from '@dbos-inc/dbos-sdk'

class OrderFlow {
  @Step()
  static async chargeCard(orderId: string) {
    return await stripe.charge({ orderId })
  }

  @Step()
  static async shipItem(orderId: string) {
    return await fedex.create({ orderId })
  }

  @Workflow()
  static async run(orderId: string) {
    const receipt = await OrderFlow.chargeCard(orderId)
    await DBOS.sleep(5 * 60_000)
    const tracking = await OrderFlow.shipItem(orderId)
    return { receipt, tracking }
  }
}

@Step は単一 Postgres transaction 内で実行できる — DB 操作なら exactly once を DB 自体が保証する。workflow が失敗すると DB row がそれを記録し、worker 再起動時に同じ workflow ID で再開する。

7.3 強みと弱み

  • 強み: infra が 1 つ(Postgres のみ)。learning curve が最も低い。transactional 整合性。
  • 弱み: deterministic replay model ではない。workflow code を最初から再実行しない。最後の checkpoint から再開。非常に長い workflow・複雑な分岐には表現力の限界。
  • 2026: Java 0.8 で Spring Boot 統合。Conductor(cloud observability)が self-host 可能に。

7.4 価格

  • Self-host library: 無料。
  • DBOS Cloud + Conductor: 追加 checkpoint 100 万あたり 50 USD。enterprise は custom。

Postgres 中心の backend team に最も摩擦が少ない。


8 章 · 同じ workflow を 3 つの engine で — 実 code 比較

シナリオ: card 決済 → 4 回まで retry → 成功時に receipt → 2 日後に review mail。同じ形を Temporal・Inngest・Trigger.dev で書く。

8.1 Temporal(TS)

// activities.ts
export async function chargeCard(orderId: string) {
  return await stripe.charge({ orderId })
}
export async function sendReceipt(orderId: string, receipt: Receipt) {
  await mailer.send({ to: orderId, body: receipt })
}
export async function sendReviewMail(orderId: string) {
  await mailer.send({ to: orderId, template: 'review' })
}

// workflow.ts
import { proxyActivities, sleep } from '@temporalio/workflow'
import type * as acts from './activities'

const { chargeCard, sendReceipt, sendReviewMail } = proxyActivities<typeof acts>({
  startToCloseTimeout: '60 seconds',
  retry: {
    maximumAttempts: 4,
    initialInterval: '2s',
    backoffCoefficient: 2,
    nonRetryableErrorTypes: ['CardDeclinedError'],
  },
})

export async function postPurchaseFlow(orderId: string) {
  const receipt = await chargeCard(orderId)
  await sendReceipt(orderId, receipt)
  await sleep('2 days')
  await sendReviewMail(orderId)
  return receipt
}

特徴: retry・timeout・例外分類が activity option に分離。workflow code 自体は綺麗。

8.2 Inngest(TS)

import { Inngest, NonRetriableError } from 'inngest'

const inngest = new Inngest({ id: 'shop' })

export const postPurchaseFlow = inngest.createFunction(
  {
    id: 'post-purchase',
    retries: 4,
  },
  { event: 'order.placed' },
  async ({ event, step }) => {
    const receipt = await step.run('charge', async () => {
      try {
        return await stripe.charge({ orderId: event.data.orderId })
      } catch (e) {
        if (e.code === 'card_declined') {
          throw new NonRetriableError('card declined')
        }
        throw e
      }
    })

    await step.run('send-receipt', () => mailer.send({
      to: event.data.orderId, body: receipt,
    }))

    await step.sleep('wait-2d', '2 days')

    await step.run('review-mail', () => mailer.send({
      to: event.data.orderId, template: 'review',
    }))

    return receipt
  }
)

特徴: retry は関数 option、分類は NonRetriableError を投げる。event payload が workflow input。

8.3 Trigger.dev(TS)

import { task, wait, AbortTaskRunError } from '@trigger.dev/sdk/v3'

export const postPurchaseFlow = task({
  id: 'post-purchase-flow',
  retry: {
    maxAttempts: 4,
    factor: 2,
    minTimeoutInMs: 2000,
    maxTimeoutInMs: 30000,
  },
  run: async (payload: { orderId: string }) => {
    let receipt
    try {
      receipt = await stripe.charge({ orderId: payload.orderId })
    } catch (e) {
      if (e.code === 'card_declined') {
        throw new AbortTaskRunError('card declined')
      }
      throw e
    }

    await mailer.send({ to: payload.orderId, body: receipt })

    await wait.for({ days: 2 })

    await mailer.send({ to: payload.orderId, template: 'review' })

    return receipt
  },
})

特徴: task に単一 retry option。AbortTaskRunError で retry 中止。最も小さい surface area。

8.4 同じ仕事を Go で — Temporal activity

Temporal の強みである multi-language の例:

// activities.go
package activities

import (
    "context"
    "errors"
)

type ChargeInput struct {
    OrderID string
}

type Receipt struct {
    PaymentID string
    Amount    int
}

func ChargeCard(ctx context.Context, input ChargeInput) (*Receipt, error) {
    r, err := stripeClient.Charge(ctx, input.OrderID)
    if err != nil {
        if errors.Is(err, stripe.ErrCardDeclined) {
            return nil, temporal.NewNonRetryableApplicationError(
                "card declined", "CardDeclinedError", err,
            )
        }
        return nil, err
    }
    return &Receipt{PaymentID: r.ID, Amount: r.Amount}, nil
}
// workflow.go
package workflows

import (
    "time"
    "go.temporal.io/sdk/workflow"
)

func PostPurchaseFlow(ctx workflow.Context, orderID string) (*Receipt, error) {
    ao := workflow.ActivityOptions{
        StartToCloseTimeout: time.Minute,
        RetryPolicy: &temporal.RetryPolicy{
            MaximumAttempts:    4,
            BackoffCoefficient: 2.0,
            InitialInterval:    2 * time.Second,
        },
    }
    ctx = workflow.WithActivityOptions(ctx, ao)

    var receipt Receipt
    if err := workflow.ExecuteActivity(ctx, ChargeCard, ChargeInput{OrderID: orderID}).Get(ctx, &receipt); err != nil {
        return nil, err
    }

    if err := workflow.Sleep(ctx, 48*time.Hour); err != nil {
        return nil, err
    }

    if err := workflow.ExecuteActivity(ctx, SendReviewMail, orderID).Get(ctx, nil); err != nil {
        return nil, err
    }
    return &receipt, nil
}

同じ意味。Temporal の強みは Go・Java・Python・.NET など multi-language で統一された model にある。

8.5 まとめ — 同じだが違う

3 つの engine とも「state 付き retry」を綺麗に表現する。違いは周辺環境にある。

  • Temporal: retry option が activity 単位、精密で表現力ある。workflow code が最も抽象化されている。
  • Inngest: event 発行 → 関数自動 trigger。domain event と結合度が高い team に自然。
  • Trigger.dev: 単一 file に全部表現。Next.js の単一 repo に入る。

9 章 · AWS Step Functions・Azure Durable Functions・Cadence の立ち位置

9.1 AWS Step Functions

古典 classic。JSON・YAML 基盤の ASL(Amazon States Language)。state machine を宣言的に描く model。強み: AWS service 統合が深い(Lambda・SQS・SNS・DynamoDB がほぼ 1 行)。弱み: JSON は code じゃないので大きい workflow の保守が辛い、state 遷移ごとの課金が意外と積もる。

re:Invent 2025 で発表された AWS Lambda Durable Functions が gear を変えた。Lambda 内で直接 deterministic replay workflow が書ける — Python 3.12+・Node.js 22+・TypeScript 5+ 対応。Step Functions の代替であり補完でもある。

9.2 Azure Durable Functions

Microsoft の正統。C#・JS・Python・F#・PowerShell。Orchestrator 関数は deterministic であるべき、Activity 関数が副作用を起こす。Temporal と同じ paradigm だが Azure Functions infrastructure に縛られる。2026 年は .NET Microsoft Agent Framework に統合され agent workflow の標準位置を狙う。

9.3 Cadence

Temporal の親に当たる。Uber では今も core で使われ OSS としても生きている。新規 project なら Temporal を選ぶのがほぼ常に正解 — Temporal が後継で active な方だ。

9.4 それぞれの居場所

  • AWS・Azure と深く結びついた team: Step Functions・Lambda Durable・Azure Durable が自然。cloud lock-in を受け入れる代わりに ops 摩擦が無い。
  • Multi-cloud・on-prem 義務・既存 code 統合: Temporal・Restate・Inngest の self-host。
  • 既存 Uber code: Cadence を維持、新 workflow には Temporal を検討。

10 章 · 2024 ~ 2026 でなぜ爆発したか — AI agent が答え

10.1 5 つの爆発要因

  1. AI agent の長時間実行: LLM 呼び出し → tool 呼び出し → 次の LLM が 30 回反復。1 時間 task が普通になる。server が 1 度死ぬと 50 USD が蒸発する。
  2. Token cost の非対称: compute cost より LLM 呼び出し cost が圧倒的。同じ呼び出しを繰り返さないよう checkpointing が必須。
  3. Big-tech の採用: OpenAI Codex が Temporal で回り、Cloudflare・Vercel・AWS が自前 durable solution を GA。market signal が強くなった。
  4. Framework が 1 級 citizen に: LangGraph・Pydantic AI・OpenAI Agents SDK が DE を option ではなく標準として採用。
  5. Developer experience の改善: 5 年前に比べ Inngest・Trigger.dev・Restate・DBOS が「単一関数・単一 file・単一 infra」で入門ハードルを下げた。

10.2 AI agent loop — DE がまさに解く形

@workflow
async function researchAgent(query: string) {
  let context = []
  for (let i = 0; i < 30; i++) {
    const plan = await ctx.run('llm-plan', () => llm.plan(query, context))
    if (plan.action === 'done') return plan.answer

    const result = await ctx.run(`tool-${i}`, () => tools.run(plan.tool, plan.args))
    context.push({ plan, result })

    // guard: cost 暴走防止
    if (cost(context) > 5) throw new Error('budget exceeded')
  }
}
  • LLM 呼び出し・tool 呼び出しが各々 checkpointing → 30 回目で死んでも 1 ~ 29 回目は再呼び出ししない。
  • for loop がそのまま workflow 意図になる。
  • Cost guard が単純な if 文で表現される。

これを queue と cron で書いたことのある人だけが知る — debug 地獄。DE はこれを 1 関数に縮める。


11 章 · 導入 decision tree

workflow が 5 分を超えるか?
├── いいえ → DE 不要。HTTP・gRPC で十分。
└── はい
    ├── 外部呼び出しが 3 つ以上か?
    │   ├── いいえ → queue + idempotency key で十分かも。
    │   └── はい
    │       ├── multi-language が必要か?
    │       │   ├── はい → Temporal・Restate
    │       │   └── いいえ(TS 中心)
    │       │       ├── event 駆動 domain? → Inngest
    │       │       ├── Next.js full-stack? → Trigger.dev
    │       │       └── Postgres 1 つで終わらせたい? → DBOS
    │       └── self-host が義務か?
    │           ├── はい → Temporal・Restate・Inngest・Trigger.dev・DBOS の self-host
    │           └── いいえ → cloud SaaS すべて OK
    └── AI agent loop か?
        ├── はい → Temporal・Inngest・Trigger.dev(この 3 つが最強)
        └── いいえ → 一般 matrix で選択

追加分岐:

  • AWS・Azure と深く結びついている? → Lambda Durable Functions・Azure Durable Functions を先に見る。
  • すでに Cadence を使用? → 新規 workflow は Temporal を検討。
  • データ主権・規制(GDPR・HIPAA)? → すべて self-host 可能。Restate が運用負担最小。
  • 1 日 100 件未満の小 workflow? → DE 導入 cost が利得を上回る。DB column・単純な queue で十分。

12 章 · Anti-pattern

12.1 Workflow 内で直接 時刻・random・外部呼び出し

Deterministic model(Temporal・Restate)の workflow で Date.now()Math.random()fetch() をそのまま呼ぶと replay 時に違う値が出て壊れる。必ず engine API(ctx.now()ctx.random()ctx.run())で包む。

12.2 すべての関数を step に分割

Step(activity)は checkpointing cost がある。1ms の純粋関数まで step にすると workflow が 100 倍遅くなる。外部副作用・外部呼び出し・retry 単位が step の境界。

12.3 DE 1 つで ETL・Kafka・DAG まで処理

DE は transactional・長時間・state ある workflow 用。data pipeline は Airflow・dbt・Spark が適切。2 つを混ぜると両方ぎこちなくなる。

12.4 Self-host を運用人員無しで

Temporal cluster を EC2 に立てて終わり。6 ヶ月後 Cassandra disk が満杯で backup も無い。DE の self-host は無料じゃない。運用 cost を cloud SaaS cost と比較すべき。

12.5 Workflow code 変更に versioning 無し

実行中の workflow の code を変えると古い worker が新しい history に出会って壊れる。すべての DE engine は versioning API(Patched・Workflow Versioning・Side Effects)を提供する。変更ごとに version を切れ

12.6 AI agent に無制限 tool・予算

LLM 呼び出し・tool 呼び出しが deterministic なのは良いが無限 loop は deterministic でも高い。tool whitelist と予算 guard が workflow 内に入る必要がある。

12.7 Idempotency key 無しの外部 API 呼び出し

Activity が retry できるということは外部 API も同じ呼び出しを 2 度受け得るという意味。idempotency key を入力に含め、外部 API が対応していれば header(Idempotency-Key)で送る。Stripe・SendGrid・Slack はすべて対応。


エピローグ — 「engine を選ぶ」ではなく「workflow 思考を買う」

Durable Execution engine の本当の価値は道具ではない。workflow を code として見る思考だ。queue と cron で組む backend と、関数 1 つで組む backend は思考 model 自体が違う。

導入 checklist:

  • 5 分超えの workflow 候補を 5 つ書き出す。
  • 各 workflow の外部呼び出し数・失敗頻度・cost を推定。
  • 使う言語 SDK を決める(Go・Java まで必要? TS のみ? Python まで?)。
  • Self-host 義務の有無を確認(データ主権・規制)。
  • 6 ヶ月後の請求書を simulation — 実行数・同時実行・checkpoint。
  • 1 つ engine を選び 2 週間 PoC、実際の 1 つの workflow を移行。
  • Deterministic model なら versioning policy を初日に決める。
  • Activity・step の idempotency key 規約を team convention にする。
  • Observability の 1 画面(web UI または OTel dashboard)を合意。
  • AI agent があれば cost guard と tool whitelist を workflow 内に入れる。

Anti-pattern 短い list:

  • Workflow 内で直接 時刻・random・HTTP — deterministic model で壊れる。
  • 1ms 関数まで step に分割 — checkpoint cost が bottleneck になる。
  • DE で ETL まで処理 — data pipeline は別の道具。
  • Self-host を人員無しで — 見えない運用 cost が重い。
  • Workflow 変更に versioning 無し — 生きている実行を壊す。
  • AI agent に無制限権限 — cost・security 両方暴走。
  • 外部 API に idempotency key 無し — retry が副作用を二重化。

次回予告

次回は Temporal self-host production guide — Cassandra と Postgres backend の選び方、multi-cluster・HA・blue-green worker deploy、Worker Versioning と workflow migration、cost・observability まで。6 ヶ月で 1 つの会社が壊したことを整理する。Temporal Cloud の請求書が月 5,000 USD を超え始めたら、その記事が次の意思決定の基準になる。

Durable Execution は単一道具の選択ではない。時間そのものを関数の 1 級 input として扱う行為だ。その思考を買えば、道具は後からついてくる。


参考 / References

Temporal

Restate

Inngest

Trigger.dev

DBOS

AWS・Azure・Cadence・調査

Pattern・AI Agent・比較