Content
同一个操作执行一次和执行多次,产生的效果相同。
Question
- 在 REST API 设计中,POST 天然不幂等,有哪些常用方案让它变幂等?(提示:Idempotency Key)
- 前端重复提交和后端幂等性是同一个问题吗?它们各自的防护层在哪里?
- 数据库层面,UPSERT(INSERT ON CONFLICT)算不算一种幂等设计?
See Also
-
对话来源: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
POST 变幂等的方案?
客户端生成唯一的 Idempotency Key(通常是 UUID),放在请求 header 里。服务端收到后检查该 key 是否已处理过——处理过就直接返回缓存的响应,没处理过就正常执行并缓存结果。Stripe API 就是这么做的。
前端重复提交 vs 后端幂等性?
不是同一个问题,是两层防护。前端防的是用户手抖(按钮 disable、防抖),后端防的是网络重试(超时后客户端自动重发)。前端防护是 UX 优化,后端幂等是数据一致性保障,不能互相替代。
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文件,覆盖输出,天然幂等