例如在事务中往一个空集合中 prepare 添加一个元素, 之后, 事务中心已经提交, 但元素仍是 prepared 状态. 如果此时去读这个元素, 可以读到, 向事务中心求证, 发现其是 committed 状态. 但如果去读集合计数, 仍然是 0, 因为元素只在变更为 committed 状态时才更新集合计数.
一个事务, set a=1, set b=2. 如果先读发现 a=1, 然后去读发现 b=0, 这种行为是否违反事务 ACID 的原子性?
如果先读发现 b=0, 再读发现 a=1, 我认为符合原子性. 但反过来我认为不符合, 因为发现 a=1 时, 可以推导出事务已经提交, 既然已经提交, 那么 b=2 也应该已经提交.
为了达到原子性, 应该加上读修复功能. 当读发现 prepare 状态的资源时, 应该尝试修复: 如果确认是 prepared 状态, 立即返回旧值. 如果确认是 committed 状态, 修复(或者等待, 不主动修复), 然后再返回新值.
对于 2PC 实现, 强原子性几乎不可能实现. 例如容器中的某一个元素是 prepared 状态, 但其对应的事务对象(commit point)已经 committed, 所以本质上元素应该也是 committed. 但不可能做到 commit point 和 cohort "同时"更新状态. 当我们去读容器的 size 时, 我们无法知道它是否包含有 prepared 状态的元素, 所以, 容器的 size 一定是"错误的". 既然对于容器, 无法实现强原子性, 我们索性只承诺最终原子性, 读操作总是忽略 prepared 状态的数据即可, 也即前面说的, 先读到 a=1, 然后又读到 b=0.
如果非要往隔离级别上靠, 那便是 Eventual Read Committed, 保证最终结果, 不保证立即结果. 事务的执行者在全部 cohort 都 commit 之后再返回, 这样可以实现先后顺序的原子性, 但在 commit 之前读的话, 无法保证, 可能只读到部分 committed 的结果.
不实现交互式事务. 事务只有写操作, 读操作独立于事务之外.
https://www.cockroachlabs.com/blog/how-cockroachdb-distributes-atomic-transactions/ 提到"Filter: Reading an Intent", 也即读到 prepared 状态的数据应该如何处理. 它这种逻辑并不能正确处理级联关系, 也即读取容器的属性时, 并不知道容器内的元素是否为 prepared 状态, 但容器内的元素明显会影响(级联)容器的属性.