Flowness / Flowness 运行机制

世代号写入隔离:Flowness 如何拒绝旧执行者迟到的结果

租约到期允许新的执行者接手,存储端再核对世代号,阻止旧身份继续写入受保护的账本。

Flowness:开放花园里的协作路径

Flowness 的多个进程要把结果写进同一本事件账本。我们为它做了一版新账本,建在 PostgreSQL 上,按分区落账:每个分区由一个进程负责写入,这个进程叫持有者,它在一段有效期内拥有这个分区的写入资格。持有者停顿或失去响应时,要允许别的进程接手,否则这个分区的落账会一直停着。这版新账本的代码和测试已经在仓库里,还没有替换系统现在运行中的事件账本;下文讲的是它的写入路径。

接手之后会出现一个更棘手的问题:旧进程可能恢复运行。它手里还保留着此前的任务和资格记录,也可能已经发出一份在网络中迟到的写入请求。本文介绍的世代号隔离接收写入者身份、世代号和租约状态,在存储写入条件中判断这份请求是否仍然有效。

租约过期不能证明旧进程已经停止

租约是一种带期限的资格。超过期限,其他进程可以尝试接手。但网络延迟、系统暂停和进程死亡,在观察者眼中可能都表现为“好一阵没有回应”。等待时间到了,只能说明应当采取恢复行动,不能保证旧执行者再也不会出现。

如果只在开始工作时检查一次资格,旧进程就可能在长暂停后继续把结果写回。即便在发送前再检查一次,检查之后、真正写入之前仍然可能发生延迟。要保护共享数据,接收写入的一方也需要参与判断。

Martin Kleppmann 在讨论分布式锁时给出了这种问题的经典解释:每次接手发出递增的 fencing token,并由存储端拒绝过时 token 的请求。这种设计将资格验证推进到资源真正被修改的位置。原文:How to do distributed locking。

机制示意:世代号写入隔离

每次成功接手,都把世代号增加一

新账本的租约表为每个分区记下持有者身份、当前世代号和租约到期时间。世代号可以理解为第几次有效认领,旧持有者保留的数字不会因新持有者接手而自动更新。

设甲持有第 41 代资格,随后停顿,租约过期。乙读取到第 41 代,尝试接手。数据库只在两个条件同时满足时更新:租约确实已经到期,而且当前世代仍然等于乙观察到的 41。成功后,持有者改成乙,世代变为 42,并写入新的到期时间。

“仍然等于观察值”这一条件称为比较并交换,常写作 CAS。如果乙和丙都拿着旧的 41 尝试认领,乙先成功更新为 42,丙就不能再凭同一个旧观察完成更新。竞争者必须重新了解当前状态。

这一步负责选出新的有效持有者。它不会关闭甲的进程,也不能阻止甲继续在自己的内存里计算。

写入时同时核对身份、世代与期限

真正阻止旧结果落账的是写入路径。相关 SQL 在追加事件时要求:请求中的持有者身份匹配、世代号匹配,且租约仍未过期。校验被放在数据库写入条件里,通过这些条件的请求才有机会执行后续追加。

当第 42 代认领已经提交,并对写入检查可见之后,甲携带第 41 代发来的新请求便不符合当前世代。哪怕甲坚称自己曾经合法接单,这次写入仍然会被拒绝。乙的第 42 代请求也必须满足身份与期限条件,并通过该写入路径的其他检查,并非只要数字较大就能任意写。

这里保护的是受这些检查约束的账本写入。若某个外部系统接受没有世代检查的请求,这套机制不会自动替它提供隔离。事务中已经发生或正在并发发生的操作,还需要按数据库事务语义分析,不能用一张接手示意图概括全部并发时序。

三秒租约是当前参数,安全性不能只靠这个数字

源码中的默认租约期限是 3,000 毫秒。短期限能更快允许接手,也更容易在暂时停顿时引发资格更替;长期限减少这种更替,却增加等待失效资格过期的时间。这个值需要结合实际延迟与故障恢复表现调整,并没有材料证明三秒是通用最优值。

世代检查的作用,是让“旧进程可能还活着”不必成为继续接受旧资格写入的理由。缩短租约仍然不会让我们更准确地知道一个进程是否已经死亡。运行恢复的速度与写入权限的边界,应当分别解释和验证。

已实现的写入隔离,不等于完整接管系统已经验收

已有 PostgreSQL 测试覆盖并发抢占、旧持有者迟到写入被拒、有效租约不被夺走等情形。本文阅读了实现及测试入口,没有在本轮启动数据库重跑,也没有把它们写成生产故障恢复时间。

相关编排模块仍记录着部分拒绝事件的持久化落点未完成。另一个名为尝试租约的模块属于只观察的影子机制,也不能拿来证明自动接管已经生效。因此,本篇主张限定在已经实现的存储检查路径。

对多 Agent 系统而言,这项机制提出了一个实用要求:交接责任时,必须考虑旧参与者以后再次出现。只通知新执行者“你可以开始”,还不足以保护共同结果;接受结果的入口也要知道,当前究竟是哪一代资格有效。

更正(2026年10月26日):文中讲的租约、世代号和写入检查,都在 Flowness 新一版账本(建在 PostgreSQL 上)的代码里。初版没有交代这一点,读起来像系统现在的账本就是这样写入的。这版账本的代码和测试已在仓库里,还没有替换系统现在运行中的事件账本。正文第一段已补上这一层。

公众号排版与配图

本篇另备有明亮、适合手机连续阅读的公众号排版稿,配图按照本文机制单独绘制。查看公众号版,或下载 Markdown、HTML、PNG 与 SVG 配图包。

下一步

如果你的团队正在把 agent 放进真实业务,想了解 Flowness,或想聊企业 AI 落地怎么做,请在“联系我们”页写下行业和想解决的问题,我们按这些内容回复。 联系我们