Content
同一个操作执行一次和执行多次,产生的效果相同。
Question
- 在 REST API 设计中,POST 天然不幂等,有哪些常用方案让它变幂等?(提示:Idempotency Key)
- 前端重复提交和后端幂等性是同一个问题吗?它们各自的防护层在哪里?
- 数据库层面,UPSERT(INSERT ON CONFLICT)算不算一种幂等设计?
See Also
Reference
- 对话来源:2026-03-17 私聊,Jeff 在 Ghost-in-the-Shell 项目中遇到脚本幂等性要求时提问
- 项目上下文:AGENTS.md 中「幂等执行」规则——执行任务前先检查是否已完成
YoYo’s Note
幂等性(Idempotency):同一个操作执行一次和执行多次,产生的效果相同。
核心特征:
- 赋值是幂等的:
x = 5执行几次结果都是 5 - 累加不是幂等的:
x = x + 1每次结果不同
HTTP 方法的幂等性:
| 方法 | 幂等? | 原因 |
|---|---|---|
| GET | ✅ | 只读,不改变状态 |
| PUT | ✅ | 用完整数据覆盖,结果一致 |
| DELETE | ✅ | 删一次和删多次,资源都不在了 |
| POST | ❌ | 每次创建新资源 |
| PATCH | ⚠️ | 取决于实现方式 |
为什么重要:网络不可靠(超时、重试),幂等性保证重试不会产生副作用。经典案例:支付接口重复调用只扣一次款。
脚本/自动化中的幂等性:执行前先检查目标状态是否已达成(flag 文件、数据库记录),已达成则跳过。这是 Ghost-in-the-Shell 项目对 cron 脚本的核心要求。
Answer
Q: POST 变幂等的方案?
客户端生成唯一的 Idempotency Key(通常是 UUID),放在请求 header 里。服务端收到后检查该 key 是否已处理过——处理过就直接返回缓存的响应,没处理过就正常执行并缓存结果。Stripe API 就是这么做的。
Q: 前端重复提交 vs 后端幂等性?
不是同一个问题,是两层防护。前端防的是用户手抖(按钮 disable、防抖),后端防的是网络重试(超时后客户端自动重发)。前端防护是 UX 优化,后端幂等是数据一致性保障,不能互相替代。
Q: UPSERT 算幂等设计吗?
算。INSERT ON CONFLICT DO UPDATE SET x = 5 执行一次和多次结果相同,是典型的幂等操作。但 INSERT ON CONFLICT DO UPDATE SET x = x + 1 就不是——每次冲突都会累加。
Extra
Ghost-in-the-Shell 中的实际案例(2026-03-20 补充)
采集器幂等设计:
- 每个采集器(weather/calendar/gmail/rss 等)输出到
.runtime/staging/{date}/xxx.json - 同一天重复运行 → 覆盖同名文件,结果一致
- 采集器内部检查:如果今天的文件已存在且非空,跳过采集(
idempotent skip)
cron 任务幂等设计:
- 发送类任务用 flag 文件防重发:
.runtime/sent/{date}/{task-id}存在则跳过 at类型任务(固定时间):检查 last-run 时间戳,今天已执行过则不再执行every类型任务(间隔执行):检查 last-run 距今是否超过间隔
记账脚本幂等设计:
add.ts写入后自动bean-check,失败则回滚(原子性,不是幂等性,但防止了半写入状态)- 采集器
bookkeeping.ts每天重新解析.bean文件,覆盖输出,天然幂等