Skip to content

目标项目设计

最终项目叫 durable-mini。它是一个教学版 Durable Execution runtime,用订单处理作为业务场景。

技术栈:

技术
教程站点VitePress
后端运行时Python + FastAPI
持久化MySQL
前端控制台React + Vite

功能目标

用户可以在前端创建一个订单 workflow,然后观察:

  1. invocation 被创建。
  2. worker 执行多个 durable steps。
  3. 故障注入后,worker 自动重试。
  4. 已完成 step 在重放时不会再次执行。
  5. 业务订单表 business_orders 随 durable steps 更新状态。
  6. timer 到期后 workflow 继续。

业务流程

业务代码看起来仍然像普通函数:

python
def process_order(ctx, payload):
    order = ctx.run("create-order", lambda: create_order(ctx, payload))
    payment = ctx.run("charge-payment", lambda: charge_payment(ctx, order))
    inventory = ctx.run("reserve-inventory", lambda: reserve_inventory(ctx, order))
    ctx.run("mark-settlement-waiting", lambda: mark_settlement_waiting(ctx, order))
    ctx.sleep("settlement-wait", seconds=5)
    receipt = ctx.run("send-receipt", lambda: send_receipt(ctx, order, payment))
    return {"order": order, "payment": payment, "inventory": inventory, "receipt": receipt}

区别在于 ctx.runctx.sleep 不只是函数调用,它们会读写 Journal。

运行时状态和业务状态

这个项目故意保留两套状态:

状态来源负责回答
运行时状态invocationsworkflow 是否运行、暂停、重试、完成
执行历史journal_entries哪个 durable step 已经提交,重放时是否跳过
业务状态business_orders订单当前对用户和运营系统意味着什么

这三张表不能混在一起。WAITING_TIMER 是运行时状态,表示 workflow 暂停;WAITING_SETTLEMENT 是业务状态,表示订单已经扣款和预留库存,正在等待结算窗口。

系统结构

为了让学习路径清晰,教学版把运行时和业务 handler 放在同一个 FastAPI 进程。生产系统会把服务协议、worker、日志服务器、元数据服务拆开。

目录结构

text
examples/durable-mini/
├── docker-compose.yml
├── README.md
├── backend/
│   ├── requirements.txt
│   └── app/
│       ├── main.py
│       ├── db.py
│       ├── models.py
│       ├── schemas.py
│       ├── engine.py
│       └── handlers.py
└── frontend/
    ├── package.json
    ├── index.html
    └── src/
        ├── main.tsx
        ├── App.tsx
        └── styles.css

教学版明确不做什么

不实现原因
多节点复制日志需要复杂的 quorum、segment、failover,超出入门项目
SDK 服务协议先聚焦 Journal 语义,避免陷入网络协议细节
分区和 sharding单机教学更容易观察状态
exactly-once 跨服务通信需要 outbox、dedupe、destination partition 等机制
workflow 版本迁移会在扩展方向解释

Teaching project inspired by Restate's public architecture and documentation.