【client-go v0.36.1】tools/cache 深度分析(中篇)— 辅助组件逐行解析

tools/cache 深度分析(中篇)--- 辅助组件逐行解析

基于 client-go v0.36.1 tools/cache/ 源码


四、辅助组件深度解析

4.1 ExpirationCache --- TTL 自动过期缓存 (expiration_cache.go, 224行)

4.1.1 结构体

go 复制代码
type ExpirationCache struct {
    cacheStorage     ThreadSafeStore     // 底层存储(threadSafeMap)
    keyFunc          KeyFunc             // key 提取函数
    clock            clock.Clock         // 可注入时钟
    expirationPolicy ExpirationPolicy    // 过期判定策略

    // expirationLock: 写锁,保证过期检查和删除的原子性
    // 防止:检查过期→删除之间有新 item 插入同一 key
    expirationLock sync.Mutex
}

4.1.2 核心机制:惰性过期

ExpirationCache 不在后台定期清理 ,而是在每次读取时检查是否过期:

go 复制代码
func (c *ExpirationCache) getOrExpire(key string) (interface{}, bool) {
    // ─── 加写锁:防止过期检查→删除之间被插入新 item ───
    c.expirationLock.Lock()
    defer c.expirationLock.Unlock()

    timestampedItem, exists := c.getTimestampedEntry(key)
    if !exists {
        return nil, false
    }

    // ─── 检查是否过期 ───
    if c.expirationPolicy.IsExpired(timestampedItem) {
        c.cacheStorage.Delete(key)   // 过期 → 删除
        return nil, false
    }

    // ─── 未过期 → 返回原始对象(剥离时间戳) ───
    return timestampedItem.Obj, true
}

为什么需要 expirationLock?
cacheStorage Add goroutine Get goroutine cacheStorage Add goroutine Get goroutine #mermaid-svg-iFxFz9ahe3NCim1S{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-iFxFz9ahe3NCim1S .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-iFxFz9ahe3NCim1S .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-iFxFz9ahe3NCim1S .error-icon{fill:#552222;}#mermaid-svg-iFxFz9ahe3NCim1S .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-iFxFz9ahe3NCim1S .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-iFxFz9ahe3NCim1S .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-iFxFz9ahe3NCim1S .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-iFxFz9ahe3NCim1S .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-iFxFz9ahe3NCim1S .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-iFxFz9ahe3NCim1S .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-iFxFz9ahe3NCim1S .marker{fill:#333333;stroke:#333333;}#mermaid-svg-iFxFz9ahe3NCim1S .marker.cross{stroke:#333333;}#mermaid-svg-iFxFz9ahe3NCim1S svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-iFxFz9ahe3NCim1S p{margin:0;}#mermaid-svg-iFxFz9ahe3NCim1S .actor{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-iFxFz9ahe3NCim1S text.actor>tspan{fill:black;stroke:none;}#mermaid-svg-iFxFz9ahe3NCim1S .actor-line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-iFxFz9ahe3NCim1S .innerArc{stroke-width:1.5;stroke-dasharray:none;}#mermaid-svg-iFxFz9ahe3NCim1S .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333;}#mermaid-svg-iFxFz9ahe3NCim1S .messageLine1{stroke-width:1.5;stroke-dasharray:2,2;stroke:#333;}#mermaid-svg-iFxFz9ahe3NCim1S #arrowhead path{fill:#333;stroke:#333;}#mermaid-svg-iFxFz9ahe3NCim1S .sequenceNumber{fill:white;}#mermaid-svg-iFxFz9ahe3NCim1S #sequencenumber{fill:#333;}#mermaid-svg-iFxFz9ahe3NCim1S #crosshead path{fill:#333;stroke:#333;}#mermaid-svg-iFxFz9ahe3NCim1S .messageText{fill:#333;stroke:none;}#mermaid-svg-iFxFz9ahe3NCim1S .labelBox{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-iFxFz9ahe3NCim1S .labelText,#mermaid-svg-iFxFz9ahe3NCim1S .labelText>tspan{fill:black;stroke:none;}#mermaid-svg-iFxFz9ahe3NCim1S .loopText,#mermaid-svg-iFxFz9ahe3NCim1S .loopText>tspan{fill:black;stroke:none;}#mermaid-svg-iFxFz9ahe3NCim1S .loopLine{stroke-width:2px;stroke-dasharray:2,2;stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-iFxFz9ahe3NCim1S .note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-iFxFz9ahe3NCim1S .noteText,#mermaid-svg-iFxFz9ahe3NCim1S .noteText>tspan{fill:black;stroke:none;}#mermaid-svg-iFxFz9ahe3NCim1S .activation0{fill:#f4f4f4;stroke:#666;}#mermaid-svg-iFxFz9ahe3NCim1S .activation1{fill:#f4f4f4;stroke:#666;}#mermaid-svg-iFxFz9ahe3NCim1S .activation2{fill:#f4f4f4;stroke:#666;}#mermaid-svg-iFxFz9ahe3NCim1S .actorPopupMenu{position:absolute;}#mermaid-svg-iFxFz9ahe3NCim1S .actorPopupMenuPanel{position:absolute;fill:#ECECFF;box-shadow:0px 8px 16px 0px rgba(0,0,0,0.2);filter:drop-shadow(3px 5px 2px rgb(0 0 0 / 0.4));}#mermaid-svg-iFxFz9ahe3NCim1S .actor-man line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-iFxFz9ahe3NCim1S .actor-man circle,#mermaid-svg-iFxFz9ahe3NCim1S line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;stroke-width:2px;}#mermaid-svg-iFxFz9ahe3NCim1S :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 无锁场景(错误) 有锁场景(正确) getTimestampedEntry("key") → expiredAdd("key", newObj) ← 新对象插入!Delete("key") ← 把刚插入的新对象也删了!expirationLock.Lock()getTimestampedEntry("key") → expiredexpirationLock.Lock() ← 阻塞等待Delete("key")expirationLock.Unlock()expirationLock.Lock() ← 获得锁Add("key", newObj) ← 安全插入

4.1.3 TTLPolicy

go 复制代码
type TTLPolicy struct {
    TTL   time.Duration   // >0: 超过 TTL 过期; ≤0: 永不过期
    Clock clock.Clock     // 可注入 FakeClock
}

func (p *TTLPolicy) IsExpired(obj *TimestampedEntry) bool {
    return p.TTL > 0 && p.Clock.Since(obj.Timestamp) > p.TTL
}

4.1.4 TimestampedEntry

go 复制代码
type TimestampedEntry struct {
    Obj       interface{}   // 原始对象
    Timestamp time.Time     // 入队时间
    key       string        // 缓存 key
}

Add 操作 :将原始对象包装为 TimestampedEntry 再存入 cacheStorage:

go 复制代码
func (c *ExpirationCache) Add(obj interface{}) error {
    key, err := c.keyFunc(obj)
    c.expirationLock.Lock()
    defer c.expirationLock.Unlock()
    c.cacheStorage.Add(key, &TimestampedEntry{obj, c.clock.Now(), key})
    // 时间戳 = 当前时间
    return nil
}

Update = Add:更新就是重新添加(刷新时间戳):

go 复制代码
func (c *ExpirationCache) Update(obj interface{}) error {
    return c.Add(obj) // 刷新 Timestamp
}

List:遍历所有 item,过滤过期的:

go 复制代码
func (c *ExpirationCache) List() []interface{} {
    items := c.cacheStorage.List()
    list := make([]interface{}, 0, len(items))
    for _, item := range items {
        key := item.(*TimestampedEntry).key
        if obj, exists := c.getOrExpire(key); exists {
            list = append(list, obj)
        }
        // 过期的被自动删除
    }
    return list
}

4.1.5 构造函数

go 复制代码
func NewTTLStore(keyFunc KeyFunc, ttl time.Duration) Store {
    return NewExpirationStore(keyFunc, &TTLPolicy{ttl, clock.RealClock{}})
}

func NewExpirationStore(keyFunc KeyFunc, expirationPolicy ExpirationPolicy) Store {
    return &ExpirationCache{
        cacheStorage:     NewThreadSafeStore(Indexers{}, Indices{}),
        // 底层是无索引的 threadSafeMap
        keyFunc:          keyFunc,
        clock:            clock.RealClock{},
        expirationPolicy: expirationPolicy,
    }
}

4.2 Heap --- 线程安全优先队列 (heap.go, 322行)

4.2.1 结构体

go 复制代码
type Heap struct {
    lock sync.RWMutex       // 读写锁
    cond sync.Cond          // 条件变量(Pop 阻塞等待)
    data *heapData          // 堆数据
    closed bool             // 关闭标记
}

type heapData struct {
    items    map[string]*heapItem  // key → item 映射
    queue    []string              // 最小堆(key 列表)
    keyFunc  KeyFunc               // key 提取函数
    lessFunc LessFunc              // 比较函数
}

type heapItem struct {
    obj   interface{}  // 存储的对象
    index int          // 在 queue 中的位置(供 heap.Fix 使用)
}

4.2.2 Add --- 插入/更新

go 复制代码
func (h *Heap) Add(obj interface{}) error {
    key, err := h.data.keyFunc(obj)
    h.lock.Lock()
    defer h.lock.Unlock()

    if h.closed {
        return fmt.Errorf(closedMsg)
    }

    if _, exists := h.data.items[key]; exists {
        // 已存在 → 更新对象 + 调整堆
        h.data.items[key].obj = obj
        heap.Fix(h.data, h.data.items[key].index)
        // O(log n) 重新调整
    } else {
        // 不存在 → 新插入
        h.addIfNotPresentLocked(key, obj)
    }

    h.cond.Broadcast()  // 唤醒 Pop
    return nil
}

4.2.3 Pop --- 阻塞弹出

go 复制代码
func (h *Heap) Pop() (interface{}, error) {
    h.lock.Lock()
    defer h.lock.Unlock()

    for len(h.data.queue) == 0 {
        if h.closed {
            return nil, fmt.Errorf("heap is closed")
        }
        h.cond.Wait()  // 等待 Add 或 Close
    }

    obj := heap.Pop(h.data)  // 弹出堆顶(最小元素)
    return obj, nil
}

4.2.4 Delete --- 删除

go 复制代码
func (h *Heap) Delete(obj interface{}) error {
    key, err := h.data.keyFunc(obj)
    h.lock.Lock()
    defer h.lock.Unlock()

    if item, ok := h.data.items[key]; ok {
        heap.Remove(h.data, item.index)
        // O(log n) 从堆中移除
        return nil
    }
    return fmt.Errorf("object not found")
}

Heap 操作与堆调整
#mermaid-svg-LoU1ljVE37drZecu{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-LoU1ljVE37drZecu .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-LoU1ljVE37drZecu .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-LoU1ljVE37drZecu .error-icon{fill:#552222;}#mermaid-svg-LoU1ljVE37drZecu .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-LoU1ljVE37drZecu .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-LoU1ljVE37drZecu .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-LoU1ljVE37drZecu .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-LoU1ljVE37drZecu .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-LoU1ljVE37drZecu .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-LoU1ljVE37drZecu .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-LoU1ljVE37drZecu .marker{fill:#333333;stroke:#333333;}#mermaid-svg-LoU1ljVE37drZecu .marker.cross{stroke:#333333;}#mermaid-svg-LoU1ljVE37drZecu svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-LoU1ljVE37drZecu p{margin:0;}#mermaid-svg-LoU1ljVE37drZecu .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-LoU1ljVE37drZecu .cluster-label text{fill:#333;}#mermaid-svg-LoU1ljVE37drZecu .cluster-label span{color:#333;}#mermaid-svg-LoU1ljVE37drZecu .cluster-label span p{background-color:transparent;}#mermaid-svg-LoU1ljVE37drZecu .label text,#mermaid-svg-LoU1ljVE37drZecu span{fill:#333;color:#333;}#mermaid-svg-LoU1ljVE37drZecu .node rect,#mermaid-svg-LoU1ljVE37drZecu .node circle,#mermaid-svg-LoU1ljVE37drZecu .node ellipse,#mermaid-svg-LoU1ljVE37drZecu .node polygon,#mermaid-svg-LoU1ljVE37drZecu .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-LoU1ljVE37drZecu .rough-node .label text,#mermaid-svg-LoU1ljVE37drZecu .node .label text,#mermaid-svg-LoU1ljVE37drZecu .image-shape .label,#mermaid-svg-LoU1ljVE37drZecu .icon-shape .label{text-anchor:middle;}#mermaid-svg-LoU1ljVE37drZecu .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-LoU1ljVE37drZecu .rough-node .label,#mermaid-svg-LoU1ljVE37drZecu .node .label,#mermaid-svg-LoU1ljVE37drZecu .image-shape .label,#mermaid-svg-LoU1ljVE37drZecu .icon-shape .label{text-align:center;}#mermaid-svg-LoU1ljVE37drZecu .node.clickable{cursor:pointer;}#mermaid-svg-LoU1ljVE37drZecu .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-LoU1ljVE37drZecu .arrowheadPath{fill:#333333;}#mermaid-svg-LoU1ljVE37drZecu .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-LoU1ljVE37drZecu .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-LoU1ljVE37drZecu .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-LoU1ljVE37drZecu .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-LoU1ljVE37drZecu .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-LoU1ljVE37drZecu .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-LoU1ljVE37drZecu .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-LoU1ljVE37drZecu .cluster text{fill:#333;}#mermaid-svg-LoU1ljVE37drZecu .cluster span{color:#333;}#mermaid-svg-LoU1ljVE37drZecu div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-LoU1ljVE37drZecu .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-LoU1ljVE37drZecu rect.text{fill:none;stroke-width:0;}#mermaid-svg-LoU1ljVE37drZecu .icon-shape,#mermaid-svg-LoU1ljVE37drZecu .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-LoU1ljVE37drZecu .icon-shape p,#mermaid-svg-LoU1ljVE37drZecu .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-LoU1ljVE37drZecu .icon-shape .label rect,#mermaid-svg-LoU1ljVE37drZecu .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-LoU1ljVE37drZecu .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-LoU1ljVE37drZecu .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-LoU1ljVE37drZecu :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} Yes
No
Heap 操作
Add(obj)
Pop()
Delete(obj)
itemskey 存在?
heap.Fix()

O(log n) 重新调整
heap.Push()

O(log n) 插入
heap.Pop()

O(log n) 弹出堆顶
heap.Remove()

O(log n) 删除指定元素


4.3 MutationCache --- 变更叠加 LRU 缓存 (mutation_cache.go, 264行)

4.3.1 设计意图

MutationCache 解决的核心问题:Informer 缓存可能滞后于最近写入

场景:Controller 更新了一个对象,然后立即通过 Lister 查询。但 Informer 的 Watch 事件可能还没到达,导致读到旧数据。MutationCache 在 Store 之上叠加一个 LRU 层,缓存最近的 Mutation,使 Get 返回最新版本。
#mermaid-svg-8sYhJzgcdSYVGokR{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-8sYhJzgcdSYVGokR .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-8sYhJzgcdSYVGokR .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-8sYhJzgcdSYVGokR .error-icon{fill:#552222;}#mermaid-svg-8sYhJzgcdSYVGokR .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-8sYhJzgcdSYVGokR .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-8sYhJzgcdSYVGokR .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-8sYhJzgcdSYVGokR .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-8sYhJzgcdSYVGokR .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-8sYhJzgcdSYVGokR .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-8sYhJzgcdSYVGokR .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-8sYhJzgcdSYVGokR .marker{fill:#333333;stroke:#333333;}#mermaid-svg-8sYhJzgcdSYVGokR .marker.cross{stroke:#333333;}#mermaid-svg-8sYhJzgcdSYVGokR svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-8sYhJzgcdSYVGokR p{margin:0;}#mermaid-svg-8sYhJzgcdSYVGokR .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-8sYhJzgcdSYVGokR .cluster-label text{fill:#333;}#mermaid-svg-8sYhJzgcdSYVGokR .cluster-label span{color:#333;}#mermaid-svg-8sYhJzgcdSYVGokR .cluster-label span p{background-color:transparent;}#mermaid-svg-8sYhJzgcdSYVGokR .label text,#mermaid-svg-8sYhJzgcdSYVGokR span{fill:#333;color:#333;}#mermaid-svg-8sYhJzgcdSYVGokR .node rect,#mermaid-svg-8sYhJzgcdSYVGokR .node circle,#mermaid-svg-8sYhJzgcdSYVGokR .node ellipse,#mermaid-svg-8sYhJzgcdSYVGokR .node polygon,#mermaid-svg-8sYhJzgcdSYVGokR .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-8sYhJzgcdSYVGokR .rough-node .label text,#mermaid-svg-8sYhJzgcdSYVGokR .node .label text,#mermaid-svg-8sYhJzgcdSYVGokR .image-shape .label,#mermaid-svg-8sYhJzgcdSYVGokR .icon-shape .label{text-anchor:middle;}#mermaid-svg-8sYhJzgcdSYVGokR .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-8sYhJzgcdSYVGokR .rough-node .label,#mermaid-svg-8sYhJzgcdSYVGokR .node .label,#mermaid-svg-8sYhJzgcdSYVGokR .image-shape .label,#mermaid-svg-8sYhJzgcdSYVGokR .icon-shape .label{text-align:center;}#mermaid-svg-8sYhJzgcdSYVGokR .node.clickable{cursor:pointer;}#mermaid-svg-8sYhJzgcdSYVGokR .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-8sYhJzgcdSYVGokR .arrowheadPath{fill:#333333;}#mermaid-svg-8sYhJzgcdSYVGokR .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-8sYhJzgcdSYVGokR .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-8sYhJzgcdSYVGokR .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-8sYhJzgcdSYVGokR .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-8sYhJzgcdSYVGokR .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-8sYhJzgcdSYVGokR .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-8sYhJzgcdSYVGokR .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-8sYhJzgcdSYVGokR .cluster text{fill:#333;}#mermaid-svg-8sYhJzgcdSYVGokR .cluster span{color:#333;}#mermaid-svg-8sYhJzgcdSYVGokR div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-8sYhJzgcdSYVGokR .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-8sYhJzgcdSYVGokR rect.text{fill:none;stroke-width:0;}#mermaid-svg-8sYhJzgcdSYVGokR .icon-shape,#mermaid-svg-8sYhJzgcdSYVGokR .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-8sYhJzgcdSYVGokR .icon-shape p,#mermaid-svg-8sYhJzgcdSYVGokR .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-8sYhJzgcdSYVGokR .icon-shape .label rect,#mermaid-svg-8sYhJzgcdSYVGokR .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-8sYhJzgcdSYVGokR .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-8sYhJzgcdSYVGokR .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-8sYhJzgcdSYVGokR :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 返回更新版本
Controller 写入

Update(obj)
MutationCache.Mutation(obj)
LRU Expire Cache

(最近写入)
Controller 读取

Lister.Get()
MutationCache.GetByKey()
Backing Store

(Informer 缓存)
CompareResourceVersion

取更新版本

4.3.2 GetByKey --- 双层取新

go 复制代码
func (c *mutationCache) GetByKey(key string) (interface{}, bool, error) {
    c.lock.Lock()
    defer c.lock.Unlock()

    // 1. 先查 backingCache(Informer 缓存)
    obj, exists, err := c.backingCache.GetByKey(key)
    if err != nil {
        return nil, false, err
    }
    if !exists {
        if !c.includeAdds {
            // backingCache 中没有 → 返回不存在
            // 原因:无法区分"未观察到创建"和"创建后被删除"
            return nil, false, nil
        }
        // includeAdds=true → 检查 mutationCache
        obj, exists = c.mutationCache.Get(key)
        if !exists {
            return nil, false, nil
        }
    }

    // 2. 如果是 runtime.Object → 取更新版本
    objRuntime, ok := obj.(runtime.Object)
    if !ok {
        return obj, true, nil
    }
    return c.newerObject(key, objRuntime), true, nil
}

4.3.3 newerObject --- 版本比较

go 复制代码
func (c *mutationCache) newerObject(key string, backing runtime.Object) runtime.Object {
    mutatedObj, exists := c.mutationCache.Get(key)
    if !exists {
        return backing  // LRU 中没有 → 返回 backing
    }
    mutatedObjRuntime, ok := mutatedObj.(runtime.Object)
    if !ok {
        return backing
    }

    // 比较 ResourceVersion:backing >= mutated → 移除 LRU 条目
    if c.comparator.CompareResourceVersion(backing, mutatedObjRuntime) >= 0 {
        c.mutationCache.Remove(key)
        return backing  // backing 已更新 → 用 backing
    }
    return mutatedObjRuntime  // LRU 更新 → 用 LRU
}

4.3.4 etcdObjectVersioner --- ResourceVersion 比较

go 复制代码
type etcdObjectVersioner struct{}

func (a etcdObjectVersioner) CompareResourceVersion(lhs, rhs runtime.Object) int {
    lhsVersion, _ := a.ObjectResourceVersion(lhs)  // 解析为 uint64
    rhsVersion, _ := a.ObjectResourceVersion(rhs)

    if lhsVersion == rhsVersion { return 0 }
    if lhsVersion < rhsVersion  { return -1 }
    return 1
}

4.4 UndeltaStore --- 全量推送 Store (undelta_store.go, 89行)

4.4.1 设计意图

UndeltaStore 实现了增量转全量的语义。每次 Store 发生任何变更(Add/Update/Delete/Replace),都调用 PushFunc 推送完整状态列表。

适用场景:某些消费者只关心完整状态快照,不关心增量变化。

go 复制代码
type UndeltaStore struct {
    Store                         // 嵌入标准 Store
    PushFunc func([]interface{})  // 全量推送回调
}

4.4.2 核心操作

go 复制代码
func (u *UndeltaStore) Add(obj interface{}) error {
    if err := u.Store.Add(obj); err != nil { return err }
    u.PushFunc(u.Store.List())  // 推送完整列表
    return nil
}

func (u *UndeltaStore) Update(obj interface{}) error {
    if err := u.Store.Update(obj); err != nil { return err }
    u.PushFunc(u.Store.List())  // 推送完整列表
    return nil
}

func (u *UndeltaStore) Delete(obj interface{}) error {
    if err := u.Store.Delete(obj); err != nil { return err }
    u.PushFunc(u.Store.List())  // 推送完整列表
    return nil
}

func (u *UndeltaStore) Replace(list []interface{}, rv string) error {
    if err := u.Store.Replace(list, rv); err != nil { return err }
    u.PushFunc(u.Store.List())  // 推送完整列表
    return nil
}

注意:由于 Store.Add 和 PushFunc 之间锁会释放,可能出现两次 PushFunc 传入相同列表的情况(竞态但无害)。


4.5 MutationDetector --- 缓存变更检测 (mutation_detector.go, 167行)

4.5.1 设计意图

检测 Informer 缓存中的对象是否被非法修改(深拷贝对比)。这对调试非常有用:Informer 的对象应该是只读的,如果用户代码直接修改了缓存对象,会导致数据不一致。

默认关闭 ,通过环境变量 KUBE_CACHE_MUTATION_DETECTOR=true 启用。

go 复制代码
var mutationDetectionEnabled = false

func init() {
    mutationDetectionEnabled, _ = strconv.ParseBool(os.Getenv("KUBE_CACHE_MUTATION_DETECTOR"))
}

4.5.2 defaultCacheMutationDetector

go 复制代码
type defaultCacheMutationDetector struct {
    name   string
    period time.Duration     // 检查周期(1s)

    compareObjectsLock sync.Mutex
    addedObjsLock      sync.Mutex
    addedObjs          []cacheObj       // 新添加的对象(待移动到 cachedObjs)

    cachedObjs         []cacheObj       // 当前监控的对象列表
    retainedCachedObjs []cacheObj       // 保留期内的旧对象
    retainDuration     time.Duration    // 保留时长(2min)
    lastRotated        time.Time
    failureFunc        func(string)     // 可注入的失败回调
}

type cacheObj struct {
    cached interface{}  // 原始对象(指向缓存中的引用)
    copied interface{}  // 深拷贝(用于对比)
}

4.5.3 工作流程

#mermaid-svg-FXAvxfoUfNfxEYg4{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-FXAvxfoUfNfxEYg4 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-FXAvxfoUfNfxEYg4 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-FXAvxfoUfNfxEYg4 .error-icon{fill:#552222;}#mermaid-svg-FXAvxfoUfNfxEYg4 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-FXAvxfoUfNfxEYg4 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-FXAvxfoUfNfxEYg4 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-FXAvxfoUfNfxEYg4 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-FXAvxfoUfNfxEYg4 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-FXAvxfoUfNfxEYg4 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-FXAvxfoUfNfxEYg4 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-FXAvxfoUfNfxEYg4 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-FXAvxfoUfNfxEYg4 .marker.cross{stroke:#333333;}#mermaid-svg-FXAvxfoUfNfxEYg4 svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-FXAvxfoUfNfxEYg4 p{margin:0;}#mermaid-svg-FXAvxfoUfNfxEYg4 .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-FXAvxfoUfNfxEYg4 .cluster-label text{fill:#333;}#mermaid-svg-FXAvxfoUfNfxEYg4 .cluster-label span{color:#333;}#mermaid-svg-FXAvxfoUfNfxEYg4 .cluster-label span p{background-color:transparent;}#mermaid-svg-FXAvxfoUfNfxEYg4 .label text,#mermaid-svg-FXAvxfoUfNfxEYg4 span{fill:#333;color:#333;}#mermaid-svg-FXAvxfoUfNfxEYg4 .node rect,#mermaid-svg-FXAvxfoUfNfxEYg4 .node circle,#mermaid-svg-FXAvxfoUfNfxEYg4 .node ellipse,#mermaid-svg-FXAvxfoUfNfxEYg4 .node polygon,#mermaid-svg-FXAvxfoUfNfxEYg4 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-FXAvxfoUfNfxEYg4 .rough-node .label text,#mermaid-svg-FXAvxfoUfNfxEYg4 .node .label text,#mermaid-svg-FXAvxfoUfNfxEYg4 .image-shape .label,#mermaid-svg-FXAvxfoUfNfxEYg4 .icon-shape .label{text-anchor:middle;}#mermaid-svg-FXAvxfoUfNfxEYg4 .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-FXAvxfoUfNfxEYg4 .rough-node .label,#mermaid-svg-FXAvxfoUfNfxEYg4 .node .label,#mermaid-svg-FXAvxfoUfNfxEYg4 .image-shape .label,#mermaid-svg-FXAvxfoUfNfxEYg4 .icon-shape .label{text-align:center;}#mermaid-svg-FXAvxfoUfNfxEYg4 .node.clickable{cursor:pointer;}#mermaid-svg-FXAvxfoUfNfxEYg4 .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-FXAvxfoUfNfxEYg4 .arrowheadPath{fill:#333333;}#mermaid-svg-FXAvxfoUfNfxEYg4 .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-FXAvxfoUfNfxEYg4 .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-FXAvxfoUfNfxEYg4 .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-FXAvxfoUfNfxEYg4 .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-FXAvxfoUfNfxEYg4 .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-FXAvxfoUfNfxEYg4 .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-FXAvxfoUfNfxEYg4 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-FXAvxfoUfNfxEYg4 .cluster text{fill:#333;}#mermaid-svg-FXAvxfoUfNfxEYg4 .cluster span{color:#333;}#mermaid-svg-FXAvxfoUfNfxEYg4 div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-FXAvxfoUfNfxEYg4 .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-FXAvxfoUfNfxEYg4 rect.text{fill:none;stroke-width:0;}#mermaid-svg-FXAvxfoUfNfxEYg4 .icon-shape,#mermaid-svg-FXAvxfoUfNfxEYg4 .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-FXAvxfoUfNfxEYg4 .icon-shape p,#mermaid-svg-FXAvxfoUfNfxEYg4 .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-FXAvxfoUfNfxEYg4 .icon-shape .label rect,#mermaid-svg-FXAvxfoUfNfxEYg4 .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-FXAvxfoUfNfxEYg4 .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-FXAvxfoUfNfxEYg4 .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-FXAvxfoUfNfxEYg4 :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} Yes
No
不同
相同
AddObject(obj)
obj.DeepCopyObject()
存储: {cached: obj, copied: copy}
Run(stopCh)
超过 retainDuration?
cachedObjs → retainedCachedObjs

cachedObjs = nil
CompareObjects()
移动 addedObjs → cachedObjs
reflect.DeepEqual

(cached, copied)?
CACHE ALTERED!

panic / failureFunc
等待 period

关键设计 :检测到修改后 panic,因为这是 P0 bug------缓存被修改后所有后续计算都不可信。


4.6 ListWatch --- 列表监视器 (listwatch.go, 312行)

4.6.1 接口层次

#mermaid-svg-QPbMHDQn0i4o4cH3{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-QPbMHDQn0i4o4cH3 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-QPbMHDQn0i4o4cH3 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-QPbMHDQn0i4o4cH3 .error-icon{fill:#552222;}#mermaid-svg-QPbMHDQn0i4o4cH3 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-QPbMHDQn0i4o4cH3 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-QPbMHDQn0i4o4cH3 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-QPbMHDQn0i4o4cH3 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-QPbMHDQn0i4o4cH3 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-QPbMHDQn0i4o4cH3 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-QPbMHDQn0i4o4cH3 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-QPbMHDQn0i4o4cH3 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-QPbMHDQn0i4o4cH3 .marker.cross{stroke:#333333;}#mermaid-svg-QPbMHDQn0i4o4cH3 svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-QPbMHDQn0i4o4cH3 p{margin:0;}#mermaid-svg-QPbMHDQn0i4o4cH3 g.classGroup text{fill:#9370DB;stroke:none;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:10px;}#mermaid-svg-QPbMHDQn0i4o4cH3 g.classGroup text .title{font-weight:bolder;}#mermaid-svg-QPbMHDQn0i4o4cH3 .cluster-label text{fill:#333;}#mermaid-svg-QPbMHDQn0i4o4cH3 .cluster-label span{color:#333;}#mermaid-svg-QPbMHDQn0i4o4cH3 .cluster-label span p{background-color:transparent;}#mermaid-svg-QPbMHDQn0i4o4cH3 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-QPbMHDQn0i4o4cH3 .cluster text{fill:#333;}#mermaid-svg-QPbMHDQn0i4o4cH3 .cluster span{color:#333;}#mermaid-svg-QPbMHDQn0i4o4cH3 .nodeLabel,#mermaid-svg-QPbMHDQn0i4o4cH3 .edgeLabel{color:#131300;}#mermaid-svg-QPbMHDQn0i4o4cH3 .edgeLabel .label rect{fill:#ECECFF;}#mermaid-svg-QPbMHDQn0i4o4cH3 .label text{fill:#131300;}#mermaid-svg-QPbMHDQn0i4o4cH3 .labelBkg{background:#ECECFF;}#mermaid-svg-QPbMHDQn0i4o4cH3 .edgeLabel .label span{background:#ECECFF;}#mermaid-svg-QPbMHDQn0i4o4cH3 .classTitle{font-weight:bolder;}#mermaid-svg-QPbMHDQn0i4o4cH3 .node rect,#mermaid-svg-QPbMHDQn0i4o4cH3 .node circle,#mermaid-svg-QPbMHDQn0i4o4cH3 .node ellipse,#mermaid-svg-QPbMHDQn0i4o4cH3 .node polygon,#mermaid-svg-QPbMHDQn0i4o4cH3 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-QPbMHDQn0i4o4cH3 .divider{stroke:#9370DB;stroke-width:1;}#mermaid-svg-QPbMHDQn0i4o4cH3 g.clickable{cursor:pointer;}#mermaid-svg-QPbMHDQn0i4o4cH3 g.classGroup rect{fill:#ECECFF;stroke:#9370DB;}#mermaid-svg-QPbMHDQn0i4o4cH3 g.classGroup line{stroke:#9370DB;stroke-width:1;}#mermaid-svg-QPbMHDQn0i4o4cH3 .classLabel .box{stroke:none;stroke-width:0;fill:#ECECFF;opacity:0.5;}#mermaid-svg-QPbMHDQn0i4o4cH3 .classLabel .label{fill:#9370DB;font-size:10px;}#mermaid-svg-QPbMHDQn0i4o4cH3 .relation{stroke:#333333;stroke-width:1;fill:none;}#mermaid-svg-QPbMHDQn0i4o4cH3 .dashed-line{stroke-dasharray:3;}#mermaid-svg-QPbMHDQn0i4o4cH3 .dotted-line{stroke-dasharray:1 2;}#mermaid-svg-QPbMHDQn0i4o4cH3 #compositionStart,#mermaid-svg-QPbMHDQn0i4o4cH3 .composition{fill:#333333!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-QPbMHDQn0i4o4cH3 #compositionEnd,#mermaid-svg-QPbMHDQn0i4o4cH3 .composition{fill:#333333!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-QPbMHDQn0i4o4cH3 #dependencyStart,#mermaid-svg-QPbMHDQn0i4o4cH3 .dependency{fill:#333333!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-QPbMHDQn0i4o4cH3 #dependencyStart,#mermaid-svg-QPbMHDQn0i4o4cH3 .dependency{fill:#333333!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-QPbMHDQn0i4o4cH3 #extensionStart,#mermaid-svg-QPbMHDQn0i4o4cH3 .extension{fill:transparent!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-QPbMHDQn0i4o4cH3 #extensionEnd,#mermaid-svg-QPbMHDQn0i4o4cH3 .extension{fill:transparent!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-QPbMHDQn0i4o4cH3 #aggregationStart,#mermaid-svg-QPbMHDQn0i4o4cH3 .aggregation{fill:transparent!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-QPbMHDQn0i4o4cH3 #aggregationEnd,#mermaid-svg-QPbMHDQn0i4o4cH3 .aggregation{fill:transparent!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-QPbMHDQn0i4o4cH3 #lollipopStart,#mermaid-svg-QPbMHDQn0i4o4cH3 .lollipop{fill:#ECECFF!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-QPbMHDQn0i4o4cH3 #lollipopEnd,#mermaid-svg-QPbMHDQn0i4o4cH3 .lollipop{fill:#ECECFF!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-QPbMHDQn0i4o4cH3 .edgeTerminals{font-size:11px;line-height:initial;}#mermaid-svg-QPbMHDQn0i4o4cH3 .classTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-QPbMHDQn0i4o4cH3 .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-QPbMHDQn0i4o4cH3 .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-QPbMHDQn0i4o4cH3 :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} <<interface>>
Lister
+List(options)(runtime.Object, error)
<<interface>>
ListerWithContext
+ListWithContext(ctx, options)(runtime.Object, error)
<<interface>>
Watcher
+Watch(options)(watch.Interface, error)
<<interface>>
WatcherWithContext
+WatchWithContext(ctx, options)(watch.Interface, error)
<<interface>>
ListerWatcher
+List(options)(runtime.Object, error)
+Watch(options)(watch.Interface, error)
<<interface>>
ListerWatcherWithContext
+ListWithContext(ctx, options)(runtime.Object, error)
+WatchWithContext(ctx, options)(watch.Interface, error)
ListWatch
+ListFunc ListFunc
+WatchFunc WatchFunc
+ListWithContextFunc ListWithContextFunc
+WatchFuncWithContext WatchFuncWithContext
+DisableChunking bool

4.6.2 ListWatch 结构体

go 复制代码
type ListWatch struct {
    ListFunc             ListFunc              // Deprecated
    WatchFunc            WatchFunc             // Deprecated
    ListWithContextFunc  ListWithContextFunc   // 推荐
    WatchFuncWithContext WatchFuncWithContext  // 推荐
    DisableChunking      bool                  // 禁用分页
}

优先级:WithContext 版本优先,旧版本作为 fallback。

go 复制代码
func (lw *ListWatch) ListWithContext(ctx context.Context, options metav1.ListOptions) (runtime.Object, error) {
    if lw.ListWithContextFunc != nil {
        return lw.ListWithContextFunc(ctx, options)
    }
    return lw.ListFunc(options)  // fallback
}

4.6.3 WatchList 语义适配

go 复制代码
type listWatcherWithWatchListSemanticsWrapper struct {
    *ListWatch
    unsupportedWatchListSemantics bool
    // true = 该客户端不支持 WatchList 语义
    // Reflector 检测到此标记后,即使 WatchList feature gate 开启
    // 也会使用传统的 List + Watch 模式
}

4.7 GenericLister --- 通用列表查询 (listers.go, 184行)

4.7.1 接口

go 复制代码
type GenericLister interface {
    List(selector labels.Selector) ([]runtime.Object, error)
    Get(name string) (runtime.Object, error)
    ByNamespace(namespace string) GenericNamespaceLister
}

type GenericNamespaceLister interface {
    List(selector labels.Selector) ([]runtime.Object, error)
    Get(name string) (runtime.Object, error)
}

4.7.2 ListAllByNamespace --- 核心查询函数

go 复制代码
func ListAllByNamespace(indexer Indexer, namespace string, selector labels.Selector, appendFn AppendFunc) error {
    if labels.MatchesNothing(selector) {
        return nil  // 空选择器 → 无结果
    }

    if namespace == metav1.NamespaceAll {
        return ListAll(indexer, selector, appendFn)
        // 全命名空间 → 遍历所有对象
    }

    // 使用 NamespaceIndex 加速
    items, err := indexer.Index(NamespaceIndex, &metav1.ObjectMeta{Namespace: namespace})
    if err != nil {
        // 索引失败 → 退化为全量扫描
        for _, m := range indexer.List() {
            metadata, _ := meta.Accessor(m)
            if metadata.GetNamespace() == namespace && selector.Matches(labels.Set(metadata.GetLabels())) {
                appendFn(m)
            }
        }
        return nil
    }

    // 在索引结果上做 label 选择
    selectAll := selector.Empty()
    for _, m := range items {
        if selectAll {
            appendFn(m)  // 空选择器 → 全选(跳过 label 计算)
        } else if selector.Matches(labels.Set(metadata.GetLabels())) {
            appendFn(m)
        }
    }
    return nil
}

查询路径决策
#mermaid-svg-gzwZQJdUSsXQ67B8{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-gzwZQJdUSsXQ67B8 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-gzwZQJdUSsXQ67B8 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-gzwZQJdUSsXQ67B8 .error-icon{fill:#552222;}#mermaid-svg-gzwZQJdUSsXQ67B8 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-gzwZQJdUSsXQ67B8 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-gzwZQJdUSsXQ67B8 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-gzwZQJdUSsXQ67B8 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-gzwZQJdUSsXQ67B8 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-gzwZQJdUSsXQ67B8 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-gzwZQJdUSsXQ67B8 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-gzwZQJdUSsXQ67B8 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-gzwZQJdUSsXQ67B8 .marker.cross{stroke:#333333;}#mermaid-svg-gzwZQJdUSsXQ67B8 svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-gzwZQJdUSsXQ67B8 p{margin:0;}#mermaid-svg-gzwZQJdUSsXQ67B8 .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-gzwZQJdUSsXQ67B8 .cluster-label text{fill:#333;}#mermaid-svg-gzwZQJdUSsXQ67B8 .cluster-label span{color:#333;}#mermaid-svg-gzwZQJdUSsXQ67B8 .cluster-label span p{background-color:transparent;}#mermaid-svg-gzwZQJdUSsXQ67B8 .label text,#mermaid-svg-gzwZQJdUSsXQ67B8 span{fill:#333;color:#333;}#mermaid-svg-gzwZQJdUSsXQ67B8 .node rect,#mermaid-svg-gzwZQJdUSsXQ67B8 .node circle,#mermaid-svg-gzwZQJdUSsXQ67B8 .node ellipse,#mermaid-svg-gzwZQJdUSsXQ67B8 .node polygon,#mermaid-svg-gzwZQJdUSsXQ67B8 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-gzwZQJdUSsXQ67B8 .rough-node .label text,#mermaid-svg-gzwZQJdUSsXQ67B8 .node .label text,#mermaid-svg-gzwZQJdUSsXQ67B8 .image-shape .label,#mermaid-svg-gzwZQJdUSsXQ67B8 .icon-shape .label{text-anchor:middle;}#mermaid-svg-gzwZQJdUSsXQ67B8 .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-gzwZQJdUSsXQ67B8 .rough-node .label,#mermaid-svg-gzwZQJdUSsXQ67B8 .node .label,#mermaid-svg-gzwZQJdUSsXQ67B8 .image-shape .label,#mermaid-svg-gzwZQJdUSsXQ67B8 .icon-shape .label{text-align:center;}#mermaid-svg-gzwZQJdUSsXQ67B8 .node.clickable{cursor:pointer;}#mermaid-svg-gzwZQJdUSsXQ67B8 .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-gzwZQJdUSsXQ67B8 .arrowheadPath{fill:#333333;}#mermaid-svg-gzwZQJdUSsXQ67B8 .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-gzwZQJdUSsXQ67B8 .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-gzwZQJdUSsXQ67B8 .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-gzwZQJdUSsXQ67B8 .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-gzwZQJdUSsXQ67B8 .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-gzwZQJdUSsXQ67B8 .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-gzwZQJdUSsXQ67B8 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-gzwZQJdUSsXQ67B8 .cluster text{fill:#333;}#mermaid-svg-gzwZQJdUSsXQ67B8 .cluster span{color:#333;}#mermaid-svg-gzwZQJdUSsXQ67B8 div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-gzwZQJdUSsXQ67B8 .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-gzwZQJdUSsXQ67B8 rect.text{fill:none;stroke-width:0;}#mermaid-svg-gzwZQJdUSsXQ67B8 .icon-shape,#mermaid-svg-gzwZQJdUSsXQ67B8 .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-gzwZQJdUSsXQ67B8 .icon-shape p,#mermaid-svg-gzwZQJdUSsXQ67B8 .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-gzwZQJdUSsXQ67B8 .icon-shape .label rect,#mermaid-svg-gzwZQJdUSsXQ67B8 .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-gzwZQJdUSsXQ67B8 .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-gzwZQJdUSsXQ67B8 .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-gzwZQJdUSsXQ67B8 :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} Yes
No
Yes
No
No
Yes
Yes
No
ListAllByNamespace
selector 匹配空集?
namespace == NamespaceAll?
indexer.Index(NamespaceIndex)
索引成功?
全量扫描 + namespace 过滤
selector.Empty()?
直接 append(跳过 label 计算)
selector.Matches(labels)
索引结果 + label 过滤
return nil
ListAll()


4.8 InformerName --- 全局命名注册 (identity.go, 217行)

4.8.1 设计意图

为 Informer 提供全局唯一的名称标识,用于指标和日志。同一进程中,同一 name+GVR 组合只能有一个 Informer 注册指标,防止重复计数。
#mermaid-svg-CV5194W8NPkPBTmq{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-CV5194W8NPkPBTmq .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-CV5194W8NPkPBTmq .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-CV5194W8NPkPBTmq .error-icon{fill:#552222;}#mermaid-svg-CV5194W8NPkPBTmq .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-CV5194W8NPkPBTmq .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-CV5194W8NPkPBTmq .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-CV5194W8NPkPBTmq .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-CV5194W8NPkPBTmq .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-CV5194W8NPkPBTmq .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-CV5194W8NPkPBTmq .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-CV5194W8NPkPBTmq .marker{fill:#333333;stroke:#333333;}#mermaid-svg-CV5194W8NPkPBTmq .marker.cross{stroke:#333333;}#mermaid-svg-CV5194W8NPkPBTmq svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-CV5194W8NPkPBTmq p{margin:0;}#mermaid-svg-CV5194W8NPkPBTmq .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-CV5194W8NPkPBTmq .cluster-label text{fill:#333;}#mermaid-svg-CV5194W8NPkPBTmq .cluster-label span{color:#333;}#mermaid-svg-CV5194W8NPkPBTmq .cluster-label span p{background-color:transparent;}#mermaid-svg-CV5194W8NPkPBTmq .label text,#mermaid-svg-CV5194W8NPkPBTmq span{fill:#333;color:#333;}#mermaid-svg-CV5194W8NPkPBTmq .node rect,#mermaid-svg-CV5194W8NPkPBTmq .node circle,#mermaid-svg-CV5194W8NPkPBTmq .node ellipse,#mermaid-svg-CV5194W8NPkPBTmq .node polygon,#mermaid-svg-CV5194W8NPkPBTmq .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-CV5194W8NPkPBTmq .rough-node .label text,#mermaid-svg-CV5194W8NPkPBTmq .node .label text,#mermaid-svg-CV5194W8NPkPBTmq .image-shape .label,#mermaid-svg-CV5194W8NPkPBTmq .icon-shape .label{text-anchor:middle;}#mermaid-svg-CV5194W8NPkPBTmq .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-CV5194W8NPkPBTmq .rough-node .label,#mermaid-svg-CV5194W8NPkPBTmq .node .label,#mermaid-svg-CV5194W8NPkPBTmq .image-shape .label,#mermaid-svg-CV5194W8NPkPBTmq .icon-shape .label{text-align:center;}#mermaid-svg-CV5194W8NPkPBTmq .node.clickable{cursor:pointer;}#mermaid-svg-CV5194W8NPkPBTmq .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-CV5194W8NPkPBTmq .arrowheadPath{fill:#333333;}#mermaid-svg-CV5194W8NPkPBTmq .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-CV5194W8NPkPBTmq .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-CV5194W8NPkPBTmq .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-CV5194W8NPkPBTmq .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-CV5194W8NPkPBTmq .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-CV5194W8NPkPBTmq .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-CV5194W8NPkPBTmq .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-CV5194W8NPkPBTmq .cluster text{fill:#333;}#mermaid-svg-CV5194W8NPkPBTmq .cluster span{color:#333;}#mermaid-svg-CV5194W8NPkPBTmq div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-CV5194W8NPkPBTmq .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-CV5194W8NPkPBTmq rect.text{fill:none;stroke-width:0;}#mermaid-svg-CV5194W8NPkPBTmq .icon-shape,#mermaid-svg-CV5194W8NPkPBTmq .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-CV5194W8NPkPBTmq .icon-shape p,#mermaid-svg-CV5194W8NPkPBTmq .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-CV5194W8NPkPBTmq .icon-shape .label rect,#mermaid-svg-CV5194W8NPkPBTmq .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-CV5194W8NPkPBTmq .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-CV5194W8NPkPBTmq .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-CV5194W8NPkPBTmq :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} NewInformerName('kube-controller-manager')
全局注册表

informerNameRegistry.names
.WithResource(pods)
.WithResource(services)
InformerNameAndResource

name='kcm', gvr='pods'

reserved=true
InformerNameAndResource

name='kcm', gvr='services'

reserved=true
.WithResource(pods) 第二次
InformerNameAndResource

reserved=false

(重复 GVR)

4.8.2 WithResource --- GVR 级别去重

go 复制代码
func (n *InformerName) WithResource(gvr schema.GroupVersionResource) InformerNameAndResource {
    n.lock.Lock()
    defer n.lock.Unlock()

    retval := InformerNameAndResource{name: n.name, gvr: gvr, reserved: &atomic.Bool{}}

    if n.reserved.Load() {
        if _, gvrExists := n.gvrs[gvr]; !gvrExists {
            // 首次注册此 GVR → reserved=true,启用指标
            retval.reserved.Store(true)
            n.gvrs[gvr] = retval.reserved
        } else {
            // 重复注册同一 GVR → reserved=false,禁用指标
            klog.TODO().Error(nil, "Duplicate informer registration")
        }
    }
    return retval
}

4.8.3 Reserved --- 热路径无锁检查

go 复制代码
func (n InformerNameAndResource) Reserved() bool {
    if n.reserved == nil {
        return false
    }
    return n.reserved.Load()
    // atomic load → 无锁,适合热路径(每次队列操作都调用)
}

4.9 synctrack --- 同步状态追踪 (synctrack/synctrack.go + lazy.go, 297行)

4.9.1 AsyncTrackerT --- 多 Worker 异步追踪

go 复制代码
type AsyncTracker[T comparable] struct {
    name              string
    upstreamHasSynced atomic.Bool        // 上游是否已同步
    lock              sync.Mutex
    waiting           sets.Set[T]        // 等待处理的 key 集合
    synced            context.Context    // 同步完成后取消
    cancel            func()
}

工作流程

  1. Start(key) --- 添加 key 到 waiting 集合
  2. Finished(key) --- 从 waiting 移除 key,检查是否全部完成
  3. UpstreamHasSynced() --- 标记上游已同步
  4. HasSynced() --- 返回 synced.Err() != nil(context 被取消 = 已同步)

#mermaid-svg-Ce9UMv5P336J7qnH{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-Ce9UMv5P336J7qnH .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-Ce9UMv5P336J7qnH .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-Ce9UMv5P336J7qnH .error-icon{fill:#552222;}#mermaid-svg-Ce9UMv5P336J7qnH .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-Ce9UMv5P336J7qnH .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-Ce9UMv5P336J7qnH .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-Ce9UMv5P336J7qnH .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-Ce9UMv5P336J7qnH .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-Ce9UMv5P336J7qnH .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-Ce9UMv5P336J7qnH .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-Ce9UMv5P336J7qnH .marker{fill:#333333;stroke:#333333;}#mermaid-svg-Ce9UMv5P336J7qnH .marker.cross{stroke:#333333;}#mermaid-svg-Ce9UMv5P336J7qnH svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-Ce9UMv5P336J7qnH p{margin:0;}#mermaid-svg-Ce9UMv5P336J7qnH .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-Ce9UMv5P336J7qnH .cluster-label text{fill:#333;}#mermaid-svg-Ce9UMv5P336J7qnH .cluster-label span{color:#333;}#mermaid-svg-Ce9UMv5P336J7qnH .cluster-label span p{background-color:transparent;}#mermaid-svg-Ce9UMv5P336J7qnH .label text,#mermaid-svg-Ce9UMv5P336J7qnH span{fill:#333;color:#333;}#mermaid-svg-Ce9UMv5P336J7qnH .node rect,#mermaid-svg-Ce9UMv5P336J7qnH .node circle,#mermaid-svg-Ce9UMv5P336J7qnH .node ellipse,#mermaid-svg-Ce9UMv5P336J7qnH .node polygon,#mermaid-svg-Ce9UMv5P336J7qnH .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-Ce9UMv5P336J7qnH .rough-node .label text,#mermaid-svg-Ce9UMv5P336J7qnH .node .label text,#mermaid-svg-Ce9UMv5P336J7qnH .image-shape .label,#mermaid-svg-Ce9UMv5P336J7qnH .icon-shape .label{text-anchor:middle;}#mermaid-svg-Ce9UMv5P336J7qnH .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-Ce9UMv5P336J7qnH .rough-node .label,#mermaid-svg-Ce9UMv5P336J7qnH .node .label,#mermaid-svg-Ce9UMv5P336J7qnH .image-shape .label,#mermaid-svg-Ce9UMv5P336J7qnH .icon-shape .label{text-align:center;}#mermaid-svg-Ce9UMv5P336J7qnH .node.clickable{cursor:pointer;}#mermaid-svg-Ce9UMv5P336J7qnH .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-Ce9UMv5P336J7qnH .arrowheadPath{fill:#333333;}#mermaid-svg-Ce9UMv5P336J7qnH .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-Ce9UMv5P336J7qnH .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-Ce9UMv5P336J7qnH .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-Ce9UMv5P336J7qnH .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-Ce9UMv5P336J7qnH .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-Ce9UMv5P336J7qnH .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-Ce9UMv5P336J7qnH .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-Ce9UMv5P336J7qnH .cluster text{fill:#333;}#mermaid-svg-Ce9UMv5P336J7qnH .cluster span{color:#333;}#mermaid-svg-Ce9UMv5P336J7qnH div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-Ce9UMv5P336J7qnH .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-Ce9UMv5P336J7qnH rect.text{fill:none;stroke-width:0;}#mermaid-svg-Ce9UMv5P336J7qnH .icon-shape,#mermaid-svg-Ce9UMv5P336J7qnH .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-Ce9UMv5P336J7qnH .icon-shape p,#mermaid-svg-Ce9UMv5P336J7qnH .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-Ce9UMv5P336J7qnH .icon-shape .label rect,#mermaid-svg-Ce9UMv5P336J7qnH .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-Ce9UMv5P336J7qnH .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-Ce9UMv5P336J7qnH .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-Ce9UMv5P336J7qnH :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} Yes
Start(keyA)
Start(keyB)
Start(keyC)
waiting = {A, B, C}
Finished(keyA)
Finished(keyB)
Finished(keyC)
UpstreamHasSynced()
waiting 为空 &&

upstreamHasSynced?
cancel() → HasSynced()=true

4.9.2 SingleFileTracker --- 单线程追踪

go 复制代码
type SingleFileTracker struct {
    name              string
    count             int64              // 原子计数器
    upstreamHasSynced atomic.Bool
    synced            context.Context
    cancel            func()
}

与 AsyncTracker 的区别:不跟踪具体 key,只跟踪计数。适用于事件按序处理的场景(如队列消费者)。

go 复制代码
func (t *SingleFileTracker) Start() {
    atomic.AddInt64(&t.count, 1)  // 计数+1
}

func (t *SingleFileTracker) Finished() {
    result := atomic.AddInt64(&t.count, -1)  // 计数-1
    if result < 0 {
        panic("negative counter")  // 逻辑错误检测
    }
    if result == 0 && t.upstreamHasSynced.Load() {
        t.cancel()  // 全部完成 + 上游已同步 → 标记完成
    }
}

4.9.3 LazyT --- 延迟求值缓存

go 复制代码
type Lazy[T any] struct {
    Evaluate func() (T, error)               // 求值函数
    cache    atomic.Pointer[cacheEntry[T]]    // 缓存条目
}

type cacheEntry[T any] struct {
    eval   func() (T, error)
    lock   sync.RWMutex
    result *T               // nil = 未求值
}

工作流程
cacheEntry LazyT 调用方 cacheEntry LazyT 调用方 #mermaid-svg-IpaOkD1bFSzh9pUL{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-IpaOkD1bFSzh9pUL .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-IpaOkD1bFSzh9pUL .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-IpaOkD1bFSzh9pUL .error-icon{fill:#552222;}#mermaid-svg-IpaOkD1bFSzh9pUL .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-IpaOkD1bFSzh9pUL .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-IpaOkD1bFSzh9pUL .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-IpaOkD1bFSzh9pUL .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-IpaOkD1bFSzh9pUL .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-IpaOkD1bFSzh9pUL .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-IpaOkD1bFSzh9pUL .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-IpaOkD1bFSzh9pUL .marker{fill:#333333;stroke:#333333;}#mermaid-svg-IpaOkD1bFSzh9pUL .marker.cross{stroke:#333333;}#mermaid-svg-IpaOkD1bFSzh9pUL svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-IpaOkD1bFSzh9pUL p{margin:0;}#mermaid-svg-IpaOkD1bFSzh9pUL .actor{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-IpaOkD1bFSzh9pUL text.actor>tspan{fill:black;stroke:none;}#mermaid-svg-IpaOkD1bFSzh9pUL .actor-line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-IpaOkD1bFSzh9pUL .innerArc{stroke-width:1.5;stroke-dasharray:none;}#mermaid-svg-IpaOkD1bFSzh9pUL .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333;}#mermaid-svg-IpaOkD1bFSzh9pUL .messageLine1{stroke-width:1.5;stroke-dasharray:2,2;stroke:#333;}#mermaid-svg-IpaOkD1bFSzh9pUL #arrowhead path{fill:#333;stroke:#333;}#mermaid-svg-IpaOkD1bFSzh9pUL .sequenceNumber{fill:white;}#mermaid-svg-IpaOkD1bFSzh9pUL #sequencenumber{fill:#333;}#mermaid-svg-IpaOkD1bFSzh9pUL #crosshead path{fill:#333;stroke:#333;}#mermaid-svg-IpaOkD1bFSzh9pUL .messageText{fill:#333;stroke:none;}#mermaid-svg-IpaOkD1bFSzh9pUL .labelBox{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-IpaOkD1bFSzh9pUL .labelText,#mermaid-svg-IpaOkD1bFSzh9pUL .labelText>tspan{fill:black;stroke:none;}#mermaid-svg-IpaOkD1bFSzh9pUL .loopText,#mermaid-svg-IpaOkD1bFSzh9pUL .loopText>tspan{fill:black;stroke:none;}#mermaid-svg-IpaOkD1bFSzh9pUL .loopLine{stroke-width:2px;stroke-dasharray:2,2;stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-IpaOkD1bFSzh9pUL .note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-IpaOkD1bFSzh9pUL .noteText,#mermaid-svg-IpaOkD1bFSzh9pUL .noteText>tspan{fill:black;stroke:none;}#mermaid-svg-IpaOkD1bFSzh9pUL .activation0{fill:#f4f4f4;stroke:#666;}#mermaid-svg-IpaOkD1bFSzh9pUL .activation1{fill:#f4f4f4;stroke:#666;}#mermaid-svg-IpaOkD1bFSzh9pUL .activation2{fill:#f4f4f4;stroke:#666;}#mermaid-svg-IpaOkD1bFSzh9pUL .actorPopupMenu{position:absolute;}#mermaid-svg-IpaOkD1bFSzh9pUL .actorPopupMenuPanel{position:absolute;fill:#ECECFF;box-shadow:0px 8px 16px 0px rgba(0,0,0,0.2);filter:drop-shadow(3px 5px 2px rgb(0 0 0 / 0.4));}#mermaid-svg-IpaOkD1bFSzh9pUL .actor-man line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-IpaOkD1bFSzh9pUL .actor-man circle,#mermaid-svg-IpaOkD1bFSzh9pUL line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;stroke-width:2px;}#mermaid-svg-IpaOkD1bFSzh9pUL :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 后续 Get 不再调用 eval(result 已缓存) Notify() (数据变更)cache.Swap(newCacheEntry)Get()cache.Load() → entryentry.get()lock.RLock → result==nil?lock.Lock → eval()(value, nil)(value, nil)Get()entry.get()lock.RLock → result≠nil(*result, nil)


4.10 RetryWithDeadline --- 带截止期限的重试 (retry_with_deadline.go, 78行)

go 复制代码
type RetryWithDeadline interface {
    After(error)          // 记录错误
    ShouldRetry() bool    // 是否应该继续重试
}

type retryWithDeadlineImpl struct {
    firstErrorTime   time.Time      // 首次错误时间
    lastErrorTime    time.Time      // 最近一次错误时间
    maxRetryDuration time.Duration  // 最大重试时长
    minResetPeriod   time.Duration  // 最小重置间隔
    isRetryable      func(error) bool
    clock            clock.Clock
}

核心逻辑

go 复制代码
func (r *retryWithDeadlineImpl) After(err error) {
    if r.isRetryable(err) {
        // 如果距离上次错误已超过 minResetPeriod → 重置计时
        if r.clock.Now().Sub(r.lastErrorTime) >= r.minResetPeriod {
            r.reset()
        }
        // 记录错误时间
        if r.firstErrorTime.IsZero() {
            r.firstErrorTime = r.clock.Now()
        }
        r.lastErrorTime = r.clock.Now()
    }
}

func (r *retryWithDeadlineImpl) ShouldRetry() bool {
    if r.maxRetryDuration <= 0 {
        return false  // 无限期
    }
    // 从首次错误到现在 ≤ maxRetryDuration → 可重试
    if r.clock.Now().Sub(r.firstErrorTime) <= r.maxRetryDuration {
        return true
    }
    r.reset()
    return false  // 超过截止期限 → 不重试
}

时间窗口示意
#mermaid-svg-IOuPvB7ZiNMBvMbs{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-IOuPvB7ZiNMBvMbs .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-IOuPvB7ZiNMBvMbs .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-IOuPvB7ZiNMBvMbs .error-icon{fill:#552222;}#mermaid-svg-IOuPvB7ZiNMBvMbs .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-IOuPvB7ZiNMBvMbs .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-IOuPvB7ZiNMBvMbs .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-IOuPvB7ZiNMBvMbs .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-IOuPvB7ZiNMBvMbs .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-IOuPvB7ZiNMBvMbs .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-IOuPvB7ZiNMBvMbs .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-IOuPvB7ZiNMBvMbs .marker{fill:#333333;stroke:#333333;}#mermaid-svg-IOuPvB7ZiNMBvMbs .marker.cross{stroke:#333333;}#mermaid-svg-IOuPvB7ZiNMBvMbs svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-IOuPvB7ZiNMBvMbs p{margin:0;}#mermaid-svg-IOuPvB7ZiNMBvMbs .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-IOuPvB7ZiNMBvMbs .cluster-label text{fill:#333;}#mermaid-svg-IOuPvB7ZiNMBvMbs .cluster-label span{color:#333;}#mermaid-svg-IOuPvB7ZiNMBvMbs .cluster-label span p{background-color:transparent;}#mermaid-svg-IOuPvB7ZiNMBvMbs .label text,#mermaid-svg-IOuPvB7ZiNMBvMbs span{fill:#333;color:#333;}#mermaid-svg-IOuPvB7ZiNMBvMbs .node rect,#mermaid-svg-IOuPvB7ZiNMBvMbs .node circle,#mermaid-svg-IOuPvB7ZiNMBvMbs .node ellipse,#mermaid-svg-IOuPvB7ZiNMBvMbs .node polygon,#mermaid-svg-IOuPvB7ZiNMBvMbs .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-IOuPvB7ZiNMBvMbs .rough-node .label text,#mermaid-svg-IOuPvB7ZiNMBvMbs .node .label text,#mermaid-svg-IOuPvB7ZiNMBvMbs .image-shape .label,#mermaid-svg-IOuPvB7ZiNMBvMbs .icon-shape .label{text-anchor:middle;}#mermaid-svg-IOuPvB7ZiNMBvMbs .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-IOuPvB7ZiNMBvMbs .rough-node .label,#mermaid-svg-IOuPvB7ZiNMBvMbs .node .label,#mermaid-svg-IOuPvB7ZiNMBvMbs .image-shape .label,#mermaid-svg-IOuPvB7ZiNMBvMbs .icon-shape .label{text-align:center;}#mermaid-svg-IOuPvB7ZiNMBvMbs .node.clickable{cursor:pointer;}#mermaid-svg-IOuPvB7ZiNMBvMbs .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-IOuPvB7ZiNMBvMbs .arrowheadPath{fill:#333333;}#mermaid-svg-IOuPvB7ZiNMBvMbs .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-IOuPvB7ZiNMBvMbs .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-IOuPvB7ZiNMBvMbs .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-IOuPvB7ZiNMBvMbs .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-IOuPvB7ZiNMBvMbs .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-IOuPvB7ZiNMBvMbs .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-IOuPvB7ZiNMBvMbs .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-IOuPvB7ZiNMBvMbs .cluster text{fill:#333;}#mermaid-svg-IOuPvB7ZiNMBvMbs .cluster span{color:#333;}#mermaid-svg-IOuPvB7ZiNMBvMbs div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-IOuPvB7ZiNMBvMbs .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-IOuPvB7ZiNMBvMbs rect.text{fill:none;stroke-width:0;}#mermaid-svg-IOuPvB7ZiNMBvMbs .icon-shape,#mermaid-svg-IOuPvB7ZiNMBvMbs .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-IOuPvB7ZiNMBvMbs .icon-shape p,#mermaid-svg-IOuPvB7ZiNMBvMbs .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-IOuPvB7ZiNMBvMbs .icon-shape .label rect,#mermaid-svg-IOuPvB7ZiNMBvMbs .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-IOuPvB7ZiNMBvMbs .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-IOuPvB7ZiNMBvMbs .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-IOuPvB7ZiNMBvMbs :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 时间线
now - firstErrorTime ≤ maxRetryDuration
可重试 (ShouldRetry=true)
now - firstErrorTime > maxRetryDuration
firstErrorTime
now
firstErrorTime +

maxRetryDuration
继续重试
停止重试


4.11 ObjectName --- 对象名值类型 (object-names.go, 65行)

go 复制代码
type ObjectName struct {
    Namespace string
    Name      string
}

// String 编码与 MetaNamespaceKeyFunc 一致
func (objName ObjectName) String() string {
    if len(objName.Namespace) > 0 {
        return objName.Namespace + "/" + objName.Name
    }
    return objName.Name
}

// ParseObjectName 反向解析
func ParseObjectName(str string) (ObjectName, error) {
    var objName ObjectName
    objName.Namespace, objName.Name, err = SplitMetaNamespaceKey(str)
    return objName, err
}

与 types.NamespacedName 的区别ObjectName.String() 输出 ns/name,而 NamespacedName.String() 输出 ns/name(格式相同但历史行为不同)。


4.12 event_handler_name --- EventHandler 命名 (event_handler_name.go, 121行)

为 ResourceEventHandler 生成可识别的名称,用于指标和日志。

go 复制代码
func nameForHandler(handler ResourceEventHandler) string {
    switch handler := handler.(type) {
    case *ResourceEventHandlerFuncs:
        return nameForHandlerFuncs(*handler)
    case ResourceEventHandlerFuncs:
        return nameForHandlerFuncs(handler)
    default:
        // 使用反射获取类型名
        value := reflect.ValueOf(handler)
        // 解引用指针/接口 → 获取包路径.类型名
        return value.Type().PkgPath() + "." + value.Type().Name()
    }
}

func nameForFunctions(fs ...any) string {
    // 提取所有函数的限定名
    // 如果所有限定名相同 → 返回公共限定名
    // 否则 → 用 "+" 连接所有限定名
}

4.13 RealFIFO --- 新一代 FIFO 队列 (the_real_fifo.go, 859行)

4.13.1 核心设计差异

RealFIFO 是 DeltaFIFO 的下一代替代品 ,核心区别是不按 key 去重,所有 Delta 按序排列,支持批量处理和原子事件。

go 复制代码
type RealFIFO struct {
    logger                klog.Logger
    name                  string
    lock                  sync.RWMutex
    cond                  sync.Cond
    items                 []Delta            // 有序 Delta 列表(不去重!)

    synced                chan struct{}       // 初始同步完成信号
    syncedClosed          bool
    populated             bool
    initialPopulationCount int

    keyFunc               KeyFunc
    knownObjects          KeyListerGetter     // AtomicEvents=true 时为 nil
    closed                bool
    transformer           TransformFunc
    batchSize             int                 // 默认 1000

    emitAtomicEvents      bool                // 原子事件模式
    unlockWhileProcessing bool                // 处理时解锁
    identifier            InformerNameAndResource
    metrics               *fifoMetrics
    emitDeltaTypeBookmark bool                // Bookmark Delta
}

4.13.2 Add/Update/Delete --- 直接追加

go 复制代码
func (f *RealFIFO) Add(obj interface{}) error {
    f.lock.Lock()
    defer f.lock.Unlock()
    f.populated = true
    f.checkSynced_locked()
    return f.addToItems_locked(Added, false, obj)
    // 直接追加到 items 列表,不去重
}

4.13.3 Pop --- 单条弹出

go 复制代码
func (f *RealFIFO) Pop(process PopProcessFunc) (interface{}, error) {
    f.lock.Lock()
    defer f.lock.Unlock()

    for len(f.items) == 0 {
        if f.closed { return nil, ErrFIFOClosed }
        f.cond.Wait()
    }

    isInInitialList := !f.hasSynced_locked()
    item := f.items[0]
    f.items[0] = Delta{}           // 清除引用,帮助 GC
    f.items = f.items[1:]

    defer func() {
        if f.initialPopulationCount > 0 {
            f.initialPopulationCount--
            f.checkSynced_locked()
        }
    }()

    // 可选:处理时解锁
    err := f.whileProcessing_locked(func() error {
        return process(Deltas{item}, isInInitialList)
    })
    return Deltas{item}, err
}

4.13.4 PopBatch --- 批量弹出

go 复制代码
func (f *RealFIFO) PopBatch(processBatch ProcessBatchFunc, processSingle PopProcessFunc) error {
    // ... 阻塞等待非空 ...

    unique := sets.NewString()
    deltas := make([]Delta, 0, min(len(f.items), f.batchSize))

    for i := 0; i < f.batchSize && i < len(f.items); i++ {
        item := f.items[i]
        if !batchable[item.Type] {
            // 非批处理 Delta (ReplacedAll/SyncAll/Bookmark) → 单独处理
            if len(deltas) == 0 { moveDeltaToProcessList(i) }
            break
        }
        id, err := f.keyOf(item)
        if err != nil || unique.Has(id) {
            break  // key 重复或出错 → 结束批处理
        }
        unique.Insert(id)
        moveDeltaToProcessList(i)
    }

    if len(deltas) == 1 {
        return processSingle(Deltas{deltas[0]}, isInInitialList)
    }
    return processBatch(deltas, isInInitialList)
}

4.13.5 Replace --- 原子 vs 非原子

go 复制代码
func (f *RealFIFO) Replace(newItems []interface{}, rv string) error {
    f.lock.Lock()
    defer f.lock.Unlock()

    if f.emitAtomicEvents {
        // 原子模式:单个 ReplacedAll Delta
        return f.addReplaceToItemsLocked(newItems, rv)
    } else {
        // 非原子模式:逐个 Added/Replaced + 合成 Deleted
        return reconcileReplacement(...)
    }
}

原子 Replace :所有新对象打包进一个 ReplacedAll Delta,消费者一次性处理。

go 复制代码
type ReplacedAllInfo struct {
    ResourceVersion string
    Objects         []interface{}
}

4.13.6 whileProcessing_locked --- 处理时可选解锁

go 复制代码
func (f *RealFIFO) whileProcessing_locked(process func() error) error {
    // 只有 AtomicEvents=true 且队列不太长时才解锁
    if f.unlockWhileProcessing && len(f.items) < f.batchSize*2 {
        f.lock.Unlock()
        defer f.lock.Lock()
    }
    startTime := time.Now()
    err := process()
    f.metrics.processingLatency.Observe(time.Since(startTime).Seconds())
    return err
}

设计权衡:解锁允许 Reflector 在处理期间继续添加 item,提高吞吐;但需要保证处理函数不依赖队列锁。

4.13.7 RealFIFO 构造校验

go 复制代码
func NewRealFIFOWithOptions(opts RealFIFOOptions) *RealFIFO {
    if opts.AtomicEvents {
        // 原子模式 → knownObjects 必须为 nil
        if opts.KnownObjects != nil {
            panic("knownObjects must not be provided when AtomicEvents is true")
        }
    } else {
        // 非原子模式 → 需要 knownObjects 做 Replace 去重
        if opts.UnlockWhileProcessing {
            panic("UnlockWhileProcessing must be false when AtomicEvents is false")
        }
        if opts.KnownObjects == nil {
            panic("knownObjects must be provided when AtomicEvents is false")
        }
        if opts.EmitDeltaTypeBookmark {
            panic("EmitDeltaTypeBookmark must be false when AtomicEvents is false")
        }
    }
    // ...
}

RealFIFO 模式组合
#mermaid-svg-mZutX23NjlKqWa7z{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-mZutX23NjlKqWa7z .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-mZutX23NjlKqWa7z .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-mZutX23NjlKqWa7z .error-icon{fill:#552222;}#mermaid-svg-mZutX23NjlKqWa7z .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-mZutX23NjlKqWa7z .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-mZutX23NjlKqWa7z .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-mZutX23NjlKqWa7z .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-mZutX23NjlKqWa7z .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-mZutX23NjlKqWa7z .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-mZutX23NjlKqWa7z .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-mZutX23NjlKqWa7z .marker{fill:#333333;stroke:#333333;}#mermaid-svg-mZutX23NjlKqWa7z .marker.cross{stroke:#333333;}#mermaid-svg-mZutX23NjlKqWa7z svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-mZutX23NjlKqWa7z p{margin:0;}#mermaid-svg-mZutX23NjlKqWa7z .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-mZutX23NjlKqWa7z .cluster-label text{fill:#333;}#mermaid-svg-mZutX23NjlKqWa7z .cluster-label span{color:#333;}#mermaid-svg-mZutX23NjlKqWa7z .cluster-label span p{background-color:transparent;}#mermaid-svg-mZutX23NjlKqWa7z .label text,#mermaid-svg-mZutX23NjlKqWa7z span{fill:#333;color:#333;}#mermaid-svg-mZutX23NjlKqWa7z .node rect,#mermaid-svg-mZutX23NjlKqWa7z .node circle,#mermaid-svg-mZutX23NjlKqWa7z .node ellipse,#mermaid-svg-mZutX23NjlKqWa7z .node polygon,#mermaid-svg-mZutX23NjlKqWa7z .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-mZutX23NjlKqWa7z .rough-node .label text,#mermaid-svg-mZutX23NjlKqWa7z .node .label text,#mermaid-svg-mZutX23NjlKqWa7z .image-shape .label,#mermaid-svg-mZutX23NjlKqWa7z .icon-shape .label{text-anchor:middle;}#mermaid-svg-mZutX23NjlKqWa7z .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-mZutX23NjlKqWa7z .rough-node .label,#mermaid-svg-mZutX23NjlKqWa7z .node .label,#mermaid-svg-mZutX23NjlKqWa7z .image-shape .label,#mermaid-svg-mZutX23NjlKqWa7z .icon-shape .label{text-align:center;}#mermaid-svg-mZutX23NjlKqWa7z .node.clickable{cursor:pointer;}#mermaid-svg-mZutX23NjlKqWa7z .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-mZutX23NjlKqWa7z .arrowheadPath{fill:#333333;}#mermaid-svg-mZutX23NjlKqWa7z .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-mZutX23NjlKqWa7z .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-mZutX23NjlKqWa7z .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-mZutX23NjlKqWa7z .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-mZutX23NjlKqWa7z .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-mZutX23NjlKqWa7z .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-mZutX23NjlKqWa7z .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-mZutX23NjlKqWa7z .cluster text{fill:#333;}#mermaid-svg-mZutX23NjlKqWa7z .cluster span{color:#333;}#mermaid-svg-mZutX23NjlKqWa7z div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-mZutX23NjlKqWa7z .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-mZutX23NjlKqWa7z rect.text{fill:none;stroke-width:0;}#mermaid-svg-mZutX23NjlKqWa7z .icon-shape,#mermaid-svg-mZutX23NjlKqWa7z .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-mZutX23NjlKqWa7z .icon-shape p,#mermaid-svg-mZutX23NjlKqWa7z .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-mZutX23NjlKqWa7z .icon-shape .label rect,#mermaid-svg-mZutX23NjlKqWa7z .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-mZutX23NjlKqWa7z .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-mZutX23NjlKqWa7z .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-mZutX23NjlKqWa7z :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} true
false
AtomicEvents?
需要 KnownObjects

(Replace 需要合成 Deleted)
不需要 KnownObjects

(ReplacedAll 自包含)
可 UnlockWhileProcessing

(处理时不影响一致性)
不可 UnlockWhileProcessing
可 EmitDeltaTypeBookmark

(RV 有序保证)
不可 Bookmark

相关推荐
张忠琳6 小时前
【client-go v0.36.1】WorkQueue 深度分析(下篇)— 限流队列、限流器、指标、并行化
云原生·kubernetes·informer·workqueue·client-go
张忠琳7 小时前
【client-go v0.36.1】WorkQueue 深度分析(上篇)— 模块定位、结构、基础队列与延迟队列
云原生·kubernetes·informer·workqueue·client-go
jieyucx8 小时前
站在云原生高并发天花板:拆解 Go 语言 GMP 模型与 I/O 多路复用的神级配合
开发语言·云原生·golang
张忠琳8 小时前
【client-go v0.36.1】tools/cache 深度分析(上篇)— 模块定位、整体结构、接口与依赖关系
云原生·kubernetes·cache·informer·client-go
张忠琳8 小时前
【client-go v0.36.1】(Reflector Part 1)Reflector 超深度分析 — 模块定位、整体结构、接口与依赖
云原生·kubernetes·informer·client-go·reflector
Demon1_Coder8 小时前
Day4-微服务-Seata
微服务·云原生·架构
张忠琳9 小时前
【client-go v0.36.1】client-go v0.36.1 系统级架构分析(下篇)
云原生·kubernetes·client-go
IT策士9 小时前
第50篇 k8s之系列总结 + 项目演示与后续扩展
云原生·容器·kubernetes
卧室小白9 小时前
K8S-Pod的生命周期与调度
云原生·容器·kubernetes