在 cockroachdb 中,为什么不确定性重启不更新不确定性 window 的上限?

In cockroachdb why doesn't an uncertainty restart update the upper bound of the uncertainty window?

我最近阅读了来自 cockroachdb 博客的 this 一篇很棒的文章,其中讨论了它们如何以类似于扳手但没有原子钟的方式保持一致性。这是 post 的相关部分:

When CockroachDB starts a transaction, it chooses a provisional commit timestamp based on the current node's wall time. It also establishes an upper bound on the selected wall time by adding the maximum clock offset for the cluster [commit timestamp, commit timestamp + maximum clock offset]. This time interval represents the window of uncertainty.

...

It's only when a value is observed to be within the uncertainty interval that CockroachDB-specific machinery kicks in. The central issue here is that given the clock offsets, we can't say for certain whether the encountered value was committed before our transaction started. In such cases, we simply make it so by performing an uncertainty restart, bumping the provisional commit timestamp just above the timestamp encountered. Crucially, the upper bound of the uncertainty interval does not change on restart, so the window of uncertainty shrinks. Transactions reading constantly updated data from many nodes may be forced to restart multiple times, though never for longer than the uncertainty interval, nor more than once per node.

具体来说,我不明白为什么 window 不确定性的上限在不确定性重新启动期间也不必增加。这是一个例子来说明我的困惑:

假设我们有两个写入 A 和 B(在不同的节点上)。写入 A 的时间戳为 3,B 的时间戳为 5。假设最大时钟偏移量为 3 个时间单位,如果我们在时钟当前读取为 1 的节点上开始事务,我们将构造 [1 的不确定性 window , 4].当我们遇到写入 A 时,我们将执行不确定性重启以包含它并将不确定性 window 减少到 (3, 4)。当我们遇到写入 B 时,我们将忽略它,因为它位于不确定性 window。但是,由于我们的最大时钟偏移量是 3 个单位,而 A 和 B 的时间戳相距不到三个单位,B 可能发生在 A 之前。但是我们在交易中包含了 A 而不是 B,所以我们没有一致性。

提前感谢您指出我遗漏的内容!

好问题。这很微妙;我会尽力解释发生了什么。

首先让我们看看涉及的交易。我们有两个写入事务,A (ts=3) 和 B (ts=5)。如果这两个事务触及的键有任何重叠,它们将无法提交 "out of order" 并且我们可以确定事务 A 在事务 B 之前发生。但是如果没有重叠键,则有除了时钟之外,没有任何协调点可以保证此顺序,并且由于时间戳足够接近,所以不清楚哪个 "happened first".

在存在这种歧义的情况下,我们要做的是分配某种顺序,以认为交易是按照其时间戳所暗示的顺序发生的。只要我们知道没有人 观察到 写入 B 而不是 A 的状态,这样做是安全的。这是由管理 read/write 的 CockroachDB 内部机制强制执行的交互(主要是名称不佳的 TimestampCache)。如果事务 B 后跟一个在时间戳 6 读取密钥 A 和 B 的事务 C,则事务 A 将不再被允许在时间戳 3 提交(它将被推送到时间戳 7)。

只要数据库可以看到读写之间的关系,这就可以工作。有时我们看不到这一点,如果写 B 对 A 有一些带外因果依赖,但从来没有一个事务同时触及两个键。这就是我们称之为 "causal reverse" 的异常,它是 CockroachDB 的可序列化隔离和 Spanner 的可线性化(或严格可序列化)隔离级别之间的差异。

更新不确定区间的上限是避免因果反向异常的一种方法,尽管它会带来严重的实际缺陷——你可能不得不一遍又一遍地重新开始,直到你找到至少你的差距事务涉及的所有键上的写入之间的最大时钟偏移。这可能永远不会成功。