change stream 的 resume token 里有什么?断线后怎么恢复才不丢不重?token 太旧会怎样?这篇解码 token、实测 resumeAfter 与 startAtOperationTime,并用刚做完 initial sync 的新成员构造出 oplog 窗口不够的情形。
insert ns={'db': 'test', 'coll': 't'} documentKey={'_id': 1} clusterTime=Timestamp(1791775093, 20) _id._data=826ACC5175000000142B042C0100…
insert ns={'db': 'test', 'coll': 't'} documentKey={'_id': 2} clusterTime=Timestamp(1791775093, 20) _id._data=826ACC5175000000142B042C0100…
update ns={'db': 'test', 'coll': 't'} documentKey={'_id': 1} clusterTime=Timestamp(1791775093, 21) _id._data=826ACC5175000000152B042C0100…
事件的键: ['_id', 'clusterTime', 'documentKey', 'fullDocument', 'ns', 'operationType', 'wallTime']每个事件的 _id 就是 resume token。同一次批量写入的多个事件 clusterTime 相同,但 token 不同。
token 826ACC5175000000142B042C0100296E5A100411… 首字节 0x82;后 8 字节解码为 Timestamp(1791775093, 20);事件的 clusterTime = Timestamp(1791775093, 20);相等: True token 826ACC5175000000142B042C01002B026E5A1004… 首字节 0x82;后 8 字节解码为 Timestamp(1791775093, 20);事件的 clusterTime = Timestamp(1791775093, 20);相等: True token 826ACC5175000000152B042C0100296E5A100411… 首字节 0x82;后 8 字节解码为 Timestamp(1791775093, 21);事件的 clusterTime = Timestamp(1791775093, 21);相等: True token 总长 83 字节
_data 的首字节 0x82 后面 8 个字节就是 clusterTime(秒加序号),实测与事件的 clusterTime 完全一致。
从第 2 个事件的 token 继续:收到 1 个事件: update 1(正是原来的第 3 个事件) 之后新写入也能收到: ['insert 3']
用 resumeAfter 传第 N 个事件的 token,从第 N+1 个事件继续,不丢不重;startAtOperationTime 则从一个时间点开始(含该时间点的写入)。
从 operationTime=Timestamp(1791775096, 1)(它是当时最后一次写入的时间)开始,收到: [3, 100, 101](含该时间点的那次写入 _id=3,以及之后的写入)
没有匹配事件时,stream 的 resume_token 仍随 oplog 前进(postBatchResumeToken),所以用它恢复不会因 token 太旧而失败。
token 前: 826ACC5179000000032B0429296E14…;其它集合写了 1000 条后(本 stream 没有事件): 826ACC5179000000052B0429296E14…;token 前进了: True
token 对应的位置不在节点的 oplog 里就无法恢复:新加入的成员 oplog 只从同步点开始,用旧 token 在它上面恢复报 286。
新成员状态 SECONDARY(initial sync 用了 2 s)
primary 的 oplog 最早一条 ts = Timestamp(1791775093, 1);新成员的 oplog 最早一条 ts = Timestamp(1791775102, 6)(比 primary 晚);最旧事件 token 的 clusterTime = Timestamp(1791775093, 20)
在新成员(secondary 读偏好)上用旧 token 恢复:code=286(ChangeStreamHistoryLost);Executor error during aggregate command on namespace: test.t :: caused by :: Resume of change stream was not possible, as the resume point may no long
在 primary 上用同一个旧 token 恢复:成功,收到的第一个事件 insert {'_id': 2}要及时保存最新的 token,并保证 oplog 窗口(见后面 oplog 窗口一篇)足够覆盖消费者可能停机的时间。