摘要

  • happened-before 是由进程内次序、同一消息的发送到接收,以及二者的传递闭包生成的最小偏序;它只记录信息在选定事件模型中可能传播的路径。
  • Clock Condition 只有一个方向:a → b 要求 C(a) < C(b)。较小的标量时间戳不能反过来证明 a 导致、通知了 b,甚至不能单独证明 a 在物理时间上更早。
  • 两个方向都没有 happened-before 路径的事件是并发事件。这里的含义是在已观察模型中无序,而不是墙钟时刻完全相同;后来的向量时钟更能保留这种不可比较性。

两个服务写下看似清楚的记录:事件 A 的逻辑时间是 41,B 是 42。控制台排序、连线,然后诱导值班人员断言 A 导致了 B。数字让故事更顺,却没有让证据更完整。

Leslie Lamport 1978 年的论文《Time, Clocks, and the Ordering of Events in a Distributed System》,并没有为分布式系统找到一只隐藏的万能时钟。它所做的是更严格的事:在没有全局时钟时,定义系统究竟有资格证明哪一种先后。

从可观察边生成最小偏序

Lamport 把系统建模为多个进程,每个进程包含一列事件,进程之间用消息连接。happened-before 是满足三条规则的最小关系:同一进程里较早事件先于较晚事件;消息发送先于同一消息的接收;若 a 先于 b、b 先于 c,则 a 先于 c。

“最小”意味着空白处仍然是空白。若 a → b 和 b → a 都不成立,两个不同事件就被称为并发。模型不会为了画出一条整齐时间线而擅自选择谁在前。

Lamport 说,a → b 表示 a 有可能在因果上影响 b。这是可达性结论:信息可以沿本地步骤和消息边从 a 传播到 b。它不是业务语义已经发生的证明,更不表示操作者具有某种意图,或责任已经归属于某一方。存在因果通道与实际造成结果,是两份不同的证据。

事件本身怎样定义也会改变图。论文脚注指出,把“收到消息”定义为中断位被设置,还是中断处理程序开始执行,会改变接收事件的次序。在问谁先谁后之前,必须先说明什么算一个事件。

Clock Condition 是蕴含,不是等价

逻辑时钟给事件赋一个数。Clock Condition 要求:若 a → b,则 C(a) < C(b)。实现规则很简洁:进程在相邻事件之间推进本地计数;发送消息时附带当前时间戳;接收时把本地时钟推进到严格晚于当前值和消息时间戳的位置。

这些规则保证已知箭头不会在数字上倒流。它们没有授权逆向推理:仅凭 C(a) < C(b),不能得到 a → b。

这不是小字条款,而是标量时钟的结构边界。若每一次数字比较都等价于 happened-before,那么所有并发事件必须拥有相同时间值。然而,一个事件可能同时与另一进程里两个彼此有序的事件并发;让三者同值,会直接破坏后两个事件的进程内次序。

因此,标量时间可以安全证明“已知 happened-before 边得到保持”。它不能证明“每一对递增数字之间都有一条因果边”。

全序是一项决定,不是找回的世界

有些系统必须在偏序允许多个合法答案时选出一个结果。Lamport 给出的办法是先按逻辑时间排序,再用固定进程顺序打破平局。所得全序与 happened-before 一致,足以支持互斥等控制。

但该全序并不唯一。另一组满足 Clock Condition 的时钟,或另一种平局规则,都可能得到不同线性次序。由事件系统唯一决定的是偏序,而不是后加的全序。

队列、复制服务或审计界面可以正当地选一种次序;问题在于它必须把选择标记为政策。若把 tie-break 伪装成历史发现,确定性控制规则就会被误写成事实。

没有进入日志的电话

论文“异常行为”部分提供了一个极简而持久的警告。某人在电脑 A 发出请求 A,随后打电话给另一座城市的朋友,让对方在电脑 B 发请求 B。电话在系统之外,B 完全可能得到更小的时间戳,并在内部全序中排到 A 之前。

只观察系统内事件的算法,不可能复原一条从未被观察的边。Lamport 提出两种办法:把缺失的先后信息明确带进系统,或在更强假设下使用妥善同步的物理时钟。

今天,缺失边可能是支持电话、人工批准、外部 webhook 或另一套队列。“没有找到路径”只能解释为“这次采集中没有路径”,不能外推成现实世界没有影响。

逻辑时间、向量时间与物理时间

Lamport 的标量时钟保持 Clock Condition 所需的正向蕴含,并能构造一致全序。约十年后,Colin Fidge 与 Friedemann Mattern 分别发展向量时间,以保存更多偏序信息。Mattern 直言,把偏序事件映射到线性整数会丢失信息:本可并发的事件会得到不同数字,看起来仿佛存在确定次序。

若两个事件都不在对方的因果过去中,向量时间可以让它们保持不可比较。这对调试、快照和冲突判断非常有用。但它仍依赖已经记录的进程、事件和消息边,不会发现缺失电话,也不证明人为什么做出决定。

物理时钟回答的是另一类问题。Lamport 同一篇论文随后讨论 Strong Clock Condition、时钟速率误差、漂移、消息最小时延和同步边界。这些额外假设可以让标签接近物理时间,却不会把逻辑计数器变成墙钟;物理时间也必须附带自己的误差模型。

署名边界同样需要保留。Lamport 在 1978 年建立 happened-before 框架和标量逻辑时钟,同时明确肯定 Paul Johnson 与 Bob Thomas 更早的消息时间戳思路。向量时间属于 Fidge 与 Mattern 的后续贡献,不能追溯成 Lamport 的个人发明。

一份诚实的顺序收据

可辩护的事件调查应记录事件定义及版本、进程或主体、本地序号、消息 ID、发送/接收配对、时钟算法和采集边界。若主张物理先后,还要记录物理时钟来源和误差;若主张因果,则必须拿出改变结果的机制证据,而不是只有递增计数。

最终结论可以是:在已捕获模型中 happened-before;在该模型中并发;由全序平局规则排在前面;在给定误差内物理上更早;或语义因果尚未证明。

Lamport 的贡献不是允许我们把一切排序后称为真相。它教会系统分别说清自己知道的顺序、选择的顺序,以及仍无法证明的顺序。

来源