写完马上从 secondary 读,能不能读到自己刚写的数据?因果一致性会话是怎么做到的?这篇用 3000 轮写后读统计旧读比例,用 failpoint 让 secondary 落后,看读命令里多出来的 afterClusterTime。
不带会话:2052 / 3000 次读到旧值;因果一致性会话(把写入的 operationTime 传给会话):0 / 3000 次
写 primary 后立刻读 secondary,3000 次里有 2052 次读到旧值;因果一致性会话把写入的 operationTime 传给读,0 次旧读。
secondary(已停止拉取)上不带会话的读:i = 5999(旧值) 同一个 secondary 上,带因果一致性会话(operationTime=Timestamp(1791775788, 1))的读:等了 3008 ms 后返回 i = 'FRESH'(等到 failpoint 关闭、secondary 追上)
用 failpoint stopReplProducer 让 secondary 停止拉取 oplog:无会话的读立刻返回旧值,因果会话的读会阻塞到 secondary 追上才返回新值(这里等了约 3 秒)。
带因果会话的 find 命令里的 readConcern: {'afterClusterTime': Timestamp(1791775788, 1)}
不带会话的 find 命令里的 readConcern: None会话的读命令里多了 readConcern: {afterClusterTime: ...},服务端等到本节点应用到该时间点之后才执行读。
写入后 s1.operation_time = Timestamp(1791775791, 1) 新会话 s2 一开始 operation_time = None 把 s1 的 operationTime 传给 s2 之后: Timestamp(1791775791, 1);读 → 'A'
每次响应都带 operationTime,会话把它记下;可以手工传给另一个会话(advance_operation_time)。
新会话没有传递 operationTime:读到 i = 'A'(旧值;因果一致性会话自己没有写过,也没有被告知要等谁)
只保证同一条因果链:没有传递时间戳的新会话没有任何保证。
# afterClusterTime 是 readConcern 的一个参数,可以和 local / majority / snapshot 任意搭配
test> db.runCommand({find: "t", filter: {_id: 0}, readConcern: {level: "local", afterClusterTime: Timestamp(0, 1)}}).ok
1afterClusterTime 可以和各种读关注搭配;要避免被回滚撤销,还需要 majority 读写关注。