两个 worker 同时推进订单,UPDATE … WHERE version = 7 只有一个成功,于是状态机就安全了吗?它解决了这一行的竞争,却没有回答三件事:这次跳转是否合法,写入能否持久化,外部扣款是否已经执行。
先把允许的边写出来
状态 created 可以进入 reserved,reserved 才能进入 paid;取消后的订单不能凭旧重试再次支付。版本号防止拿旧快照覆盖新值,合法边集合限制业务跳转,两者都要在同一次原子更新中判断。
PostgreSQL 事务隔离文档解释并发更新时条件可能重新求值。数据库的具体语义很重要;不能把一个进程中的 atomic.CompareAndSwap 当成多进程持久存储。

下面是串行内存模型,用来暴露“版本对了但跳转不合法”的反例。
allowed = {('created', 'reserved'), ('created', 'cancelled'),
('reserved', 'paid'), ('reserved', 'cancelled')}
row = {'state': 'created', 'version': 1}
def move(expected_version, expected_state, target):
if row['version'] != expected_version or row['state'] != expected_state:
return False
if (expected_state, target) not in allowed: return False
row.update(state=target, version=expected_version + 1)
return True
assert not move(1, 'created', 'paid')
assert move(1, 'created', 'reserved')
assert not move(1, 'created', 'cancelled')
assert move(2, 'reserved', 'paid')
assert not move(3, 'paid', 'reserved')
print(row)
本地 Python 3 输出 paid、版本 3,非法跳跃和旧版本写入被拒绝。未运行数据库并发测试。落地 SQL 时应同时约束主键、租户、版本和源状态,并检查受影响行数为 1;零行不能直接当成功。
扣款仍然需要另一份合同
如果先扣款再更新状态,扣款成功后进程崩溃,重试可能再次扣款;若先更新为已支付再扣款,又可能记录成功但资金未动。常见做法是持久化支付意图、使用稳定业务幂等键、查询外部最终结果,并通过对账推进状态。它不是“加了 CAS 就恰好一次”。
写入 durability、唯一键、事务边界和消息投递仍需分别验证。建议先做三组隔离实验:两个连接竞争同版本;写入提交前后进程终止;外部成功但响应丢失。本文只完成内存状态分支验证,不冒充三组集成实验。
十月 Agent 工具越能自动执行,状态约束越不能留给自然语言指令。发布前检查真实表约束和旧任务状态,再开放新的动作。
网硕互联帮助中心



评论前必须登录!
注册