Skip to content

Demo 01:幂等入口

第一个问题:客户端超时后重试,会不会创建两个订单流程?

如果入口不幂等,后面的 Journal 做得再好也没用,因为你已经创建了两个不同的 Invocation。Durable Execution 的第一道门是把重复请求映射到同一个执行实例。

目标

同一个 Idempotency-Key 多次请求,只返回同一个 invocation:

bash
curl -X POST http://127.0.0.1:8000/api/orders \
  -H 'Content-Type: application/json' \
  -H 'Idempotency-Key: order-demo-001' \
  -d '{"sku":"book","quantity":1,"amount":99}'

表设计

sql
CREATE TABLE invocations (
  id CHAR(36) PRIMARY KEY,
  workflow_type VARCHAR(100) NOT NULL,
  idempotency_key VARCHAR(255) UNIQUE,
  status VARCHAR(32) NOT NULL,
  input JSON NOT NULL,
  result JSON NULL,
  error TEXT NULL,
  attempt_count INT NOT NULL DEFAULT 0,
  created_at DATETIME NOT NULL,
  updated_at DATETIME NOT NULL
);

idempotency_key 的唯一索引是关键。不要只在代码里“先查再插”,并发请求会穿透。数据库唯一约束才是最终防线。

API 逻辑

python
existing = find_by_idempotency_key(session, key)
if existing:
    return existing

invocation = Invocation(
    id=str(uuid.uuid4()),
    workflow_type="order",
    idempotency_key=key,
    status="QUEUED",
    input=payload,
)
session.add(invocation)
session.commit()

生产环境还要捕获唯一键冲突:两个并发请求都没查到 existing,其中一个 insert 成功,另一个 insert 失败后重新查询即可。

为什么不是用订单号当主键

可以,但要分清两个概念:

概念含义
业务 ID订单、用户、购物车等业务实体的标识
Invocation ID一次 durable handler 执行的标识
Idempotency Key客户端请求去重的标识

很多场景下业务 ID 和 idempotency key 可以相同,但运行时最好不要强依赖这个假设。比如“重新发送收据”可能没有新订单,却仍然需要一个幂等 invocation。

小练习

  1. 去掉 idempotency_key 的唯一索引,写两个并发请求测试会发生什么。
  2. 设计一个 idempotency key 过期策略。完成的 invocation 保留多久?正在运行的 invocation 能不能被清理?

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