【etcd】Watch 机制:watchableStore、synced/unsynced 与 ErrCompacted
内容提要
本文介绍etcd v3.5.33的Watch机制核心实现。watchableStore将watcher分为synced和unsynced两组:synced接收新事件,unsynced由后台循环从bbolt追赶历史。当channel满时,事件进入victims队列重试。compaction会触发ErrCompacted错误,客户端需重建缓存。Watch事件按revision严格有序,但可能早于Put响应到达,且不替代线性一致读。
延伸解读
Watch 分组与背压:排障先看 synced/unsynced/victims
生产环境中,Put 成功但下游无事件,常被误判为 Raft 未提交。实际应先检查 Watch 落在 synced、unsynced 还是 victims:synced 接收新事件,unsynced 追赶历史,victims 是 channel 满时的重试队列。若 slow_watcher 指标上升,更可能是客户端消费慢或同流 Watch 过多,而非 Raft lag。
ErrCompacted 的触发与客户端重建策略
当 watcher 的 minRev 小于 compactMainRev 时,会收到 CompactRevision 通知,客户端映射为 ErrCompacted。此时不能假设能从旧 revision 续传,因为 auto-compaction 和手动 compact 都会裁历史。正确做法是记录最后成功事件的 revision,收到错误后全量或按 prefix 重建缓存,再以当前 revision 重新 Watch。
Watch 与读一致性的边界:不替代线性一致读
Watch 事件按 revision 严格有序,但可能早于 Put 响应到达,且不同 gRPC 流。若读路径走 Serializable 读,可能读到 stale 数据,Watch 本身只反映已 Apply 的 MVCC 状态,不能替代线性一致读。Follower 上的 Watch 进度可能落后 Leader,K8s apiserver 需处理成员切换。
Q&A
etcd watchableStore 中 synced 和 unsynced watcher 的区别是什么?
synced watcher 已与 store 当前进度对齐,只接收新事件;unsynced watcher 是慢 watcher,需要从 bbolt 追赶历史 revision,由后台 syncWatchersLoop 处理。
etcd 中 watcher 的 channel 满了会发生什么?
当 watcher 的 channel 满时,事件批次会进入 victims 队列,watcher 被标记为 victim 并移出 synced,由 syncVictimsLoop 每 10ms 或收到信号后重试发送。
etcd 中 ErrCompacted 错误是如何产生的?客户端应如何处理?
当 watcher 的 minRev 小于 compactMainRev 时,watcher_group 会发送包含 CompactRevision 的 WatchResponse,客户端将其映射为 ErrCompacted。客户端应记录最后成功事件的 revision,收到错误后重新 List 全量或按 prefix 重建缓存,再以新 revision 重新 Watch。
etcd Watch 事件与 Put 响应的顺序关系是怎样的?
Watch 事件和 Put 响应通过不同的 gRPC 流传输,Watcher 可能略早于 Put RPC 返回看到事件,因此不能依赖 Watch 事件来确认写入完成。
etcd 中 syncWatchersLoop 的作用和触发机制是什么?
syncWatchersLoop 负责将 unsynced watcher 从 bbolt 追赶历史事件,默认每 100ms 唤醒,每轮最多处理 512 个 watcher,成功追赶后移入 synced。
etcd 中如何判断一个 watcher 是 synced 还是 unsynced?
当 startRev 为 0 或大于当前 revision 时,watcher 为 synced;否则为 unsynced(慢 watcher)。
etcd 中 compaction 对 Watch 历史有什么影响?
compaction 会更新 compactMainRev,早于该 revision 的历史不可再读。如果 watcher 需要的 revision 已被 compaction 裁掉,会收到 ErrCompacted 错误,客户端需重建缓存。
etcd 中 Watch 事件是否保证严格有序?
同一 Watch 流内事件按 revision 严格递增,跨 key 顺序与 Raft log 全序一致。