活动约束
活动跨越持久边界,执行 IO 和外部副作用。活动不会像工作流代码一样被重放,而是按至少一次执行。重试、租约过期和重新投递,都可能让同一逻辑工作被观察多次。
这是契约的一部分。如果活动会扣款、发送邮件、写入其他系统或修改外部资源,其操作必须能够安全重复。
实际含义
- 一次逻辑活动执行的默认幂等标识是
activity_execution_id。 - 每次重试还有自己的
activity_attempt_id。 - Worker 可能完成了外部操作,却丢失租约,之后才报告结果。机制可能因为另一个 Worker 已提交胜出的持久结果而拒绝迟到的报告,但外部副作用可能已经发生。
- 活动代码可以执行 IO、读取系统时钟、使用可变进程状态。工作流代码不能这样做。请保持这条边界清晰。
推荐的幂等模式
许多外部 API 接受 Idempotency-Key。远程服务支持时,使用运行时提供的逻辑活动标识。
- 如果远程系统应将重试视为同一逻辑请求,优先使用
activity_execution_id。 - 只有远程系统必须区分同一逻辑工作的不同尝试时,才使用
activity_attempt_id。
其他常用模式:
- 写入名称确定的外部资源或使用自然键。
- 使用以持久标识为键的 upsert 或去重表。
- 将操作设计为天然幂等,使第二次调用不再产生变化。
许多操作天然幂等。例如,对同一个视频编码两次,最终仍得到同一个视频。删除同一个文件两次,第二次删除不产生变化。
有些操作不是天然幂等,但重复可能仍比遗漏更安全。如果无法确定邮件是否已由服务商发出,重复发送可能比静默丢失通知更合适。请明确评估并选择这一取舍。
不应做的假设
- 不要假定一次活动尝试只会在一个 Worker 上执行。
- 不要假定重试意味着上一次外部副作用失败了。
- 不要假定迟到的完成报告意味着活动没有执行。
- 不要为了避免重试而把副作用移入工作流代码,这会把幂等性问题变成确定性缺陷。