Store 超深度分析 --- 集成模式、完整数据流、不变量、与 DeltaFIFO 协作
基于
client-go v0.36.1tools/cache/store.go+thread_safe_store.go+index.go
二十四、Store 在 Informer Pipeline 中的完整角色
24.1 数据流全景
用户 Controller storeIndex threadSafeMap cache (Store/Indexer) handleDeltas Controller DeltaFIFO Reflector API Server 用户 Controller storeIndex threadSafeMap cache (Store/Indexer) handleDeltas Controller DeltaFIFO Reflector API Server #mermaid-svg-GWKONcaZK4610CWE{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-GWKONcaZK4610CWE .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-GWKONcaZK4610CWE .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-GWKONcaZK4610CWE .error-icon{fill:#552222;}#mermaid-svg-GWKONcaZK4610CWE .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-GWKONcaZK4610CWE .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-GWKONcaZK4610CWE .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-GWKONcaZK4610CWE .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-GWKONcaZK4610CWE .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-GWKONcaZK4610CWE .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-GWKONcaZK4610CWE .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-GWKONcaZK4610CWE .marker{fill:#333333;stroke:#333333;}#mermaid-svg-GWKONcaZK4610CWE .marker.cross{stroke:#333333;}#mermaid-svg-GWKONcaZK4610CWE svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-GWKONcaZK4610CWE p{margin:0;}#mermaid-svg-GWKONcaZK4610CWE .actor{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-GWKONcaZK4610CWE text.actor>tspan{fill:black;stroke:none;}#mermaid-svg-GWKONcaZK4610CWE .actor-line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-GWKONcaZK4610CWE .innerArc{stroke-width:1.5;stroke-dasharray:none;}#mermaid-svg-GWKONcaZK4610CWE .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333;}#mermaid-svg-GWKONcaZK4610CWE .messageLine1{stroke-width:1.5;stroke-dasharray:2,2;stroke:#333;}#mermaid-svg-GWKONcaZK4610CWE #arrowhead path{fill:#333;stroke:#333;}#mermaid-svg-GWKONcaZK4610CWE .sequenceNumber{fill:white;}#mermaid-svg-GWKONcaZK4610CWE #sequencenumber{fill:#333;}#mermaid-svg-GWKONcaZK4610CWE #crosshead path{fill:#333;stroke:#333;}#mermaid-svg-GWKONcaZK4610CWE .messageText{fill:#333;stroke:none;}#mermaid-svg-GWKONcaZK4610CWE .labelBox{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-GWKONcaZK4610CWE .labelText,#mermaid-svg-GWKONcaZK4610CWE .labelText>tspan{fill:black;stroke:none;}#mermaid-svg-GWKONcaZK4610CWE .loopText,#mermaid-svg-GWKONcaZK4610CWE .loopText>tspan{fill:black;stroke:none;}#mermaid-svg-GWKONcaZK4610CWE .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-GWKONcaZK4610CWE .note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-GWKONcaZK4610CWE .noteText,#mermaid-svg-GWKONcaZK4610CWE .noteText>tspan{fill:black;stroke:none;}#mermaid-svg-GWKONcaZK4610CWE .activation0{fill:#f4f4f4;stroke:#666;}#mermaid-svg-GWKONcaZK4610CWE .activation1{fill:#f4f4f4;stroke:#666;}#mermaid-svg-GWKONcaZK4610CWE .activation2{fill:#f4f4f4;stroke:#666;}#mermaid-svg-GWKONcaZK4610CWE .actorPopupMenu{position:absolute;}#mermaid-svg-GWKONcaZK4610CWE .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-GWKONcaZK4610CWE .actor-man line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-GWKONcaZK4610CWE .actor-man circle,#mermaid-svg-GWKONcaZK4610CWE line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;stroke-width:2px;}#mermaid-svg-GWKONcaZK4610CWE :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 初始 List 逐个入队 Sync/Replaced Delta检测消失对象生成墓碑 namespace→'default'→{'default/pod-A'} initialPopulationCount-- loopPop 循环 Watch 正常运行 namespace 未变 → 快速路径跳过 'default' Set 删除 'default/pod-B'空 Set 被清理 用户查询 List podsReplace(pods-list, rv)Pop(handleDeltas)Deltas={Sync, pod-A}Add(pod-A)Update("default/pod-A", pod-A) LockupdateIndices(nil, pod-A, key)returnWatch Added pod-CAdd(pod-C)Pop → Deltas={Added, pod-C}handleDeltasAdd(pod-C)Update("default/pod-C", pod-C) LockupdateIndices(nil, pod-C, key)Watch Modified pod-AUpdate(pod-A)Pop → Deltas={Updated, pod-A}Update(pod-A)Update("default/pod-A", pod-A-v2) LockupdateIndices(pod-A-v1, pod-A-v2, key)Watch Deleted pod-BDelete(pod-B)Pop → Deltas={Deleted, pod-B}Delete(pod-B)DeleteWithObject("default/pod-B", pod-B) LockupdateIndices(pod-B, nil, key)ByIndex("namespace", "default")ByIndex RLockgetKeysByIndex("namespace", "default")Set{"default/pod-A", "default/pod-C"}pod-A, pod-Cpod-A, pod-C
24.2 cache 作为 DeltaFIFO 的 knownObjects
#mermaid-svg-q8LRcLB2MVXzxn0X{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-q8LRcLB2MVXzxn0X .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-q8LRcLB2MVXzxn0X .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-q8LRcLB2MVXzxn0X .error-icon{fill:#552222;}#mermaid-svg-q8LRcLB2MVXzxn0X .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-q8LRcLB2MVXzxn0X .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-q8LRcLB2MVXzxn0X .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-q8LRcLB2MVXzxn0X .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-q8LRcLB2MVXzxn0X .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-q8LRcLB2MVXzxn0X .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-q8LRcLB2MVXzxn0X .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-q8LRcLB2MVXzxn0X .marker{fill:#333333;stroke:#333333;}#mermaid-svg-q8LRcLB2MVXzxn0X .marker.cross{stroke:#333333;}#mermaid-svg-q8LRcLB2MVXzxn0X svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-q8LRcLB2MVXzxn0X p{margin:0;}#mermaid-svg-q8LRcLB2MVXzxn0X .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-q8LRcLB2MVXzxn0X .cluster-label text{fill:#333;}#mermaid-svg-q8LRcLB2MVXzxn0X .cluster-label span{color:#333;}#mermaid-svg-q8LRcLB2MVXzxn0X .cluster-label span p{background-color:transparent;}#mermaid-svg-q8LRcLB2MVXzxn0X .label text,#mermaid-svg-q8LRcLB2MVXzxn0X span{fill:#333;color:#333;}#mermaid-svg-q8LRcLB2MVXzxn0X .node rect,#mermaid-svg-q8LRcLB2MVXzxn0X .node circle,#mermaid-svg-q8LRcLB2MVXzxn0X .node ellipse,#mermaid-svg-q8LRcLB2MVXzxn0X .node polygon,#mermaid-svg-q8LRcLB2MVXzxn0X .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-q8LRcLB2MVXzxn0X .rough-node .label text,#mermaid-svg-q8LRcLB2MVXzxn0X .node .label text,#mermaid-svg-q8LRcLB2MVXzxn0X .image-shape .label,#mermaid-svg-q8LRcLB2MVXzxn0X .icon-shape .label{text-anchor:middle;}#mermaid-svg-q8LRcLB2MVXzxn0X .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-q8LRcLB2MVXzxn0X .rough-node .label,#mermaid-svg-q8LRcLB2MVXzxn0X .node .label,#mermaid-svg-q8LRcLB2MVXzxn0X .image-shape .label,#mermaid-svg-q8LRcLB2MVXzxn0X .icon-shape .label{text-align:center;}#mermaid-svg-q8LRcLB2MVXzxn0X .node.clickable{cursor:pointer;}#mermaid-svg-q8LRcLB2MVXzxn0X .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-q8LRcLB2MVXzxn0X .arrowheadPath{fill:#333333;}#mermaid-svg-q8LRcLB2MVXzxn0X .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-q8LRcLB2MVXzxn0X .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-q8LRcLB2MVXzxn0X .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-q8LRcLB2MVXzxn0X .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-q8LRcLB2MVXzxn0X .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-q8LRcLB2MVXzxn0X .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-q8LRcLB2MVXzxn0X .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-q8LRcLB2MVXzxn0X .cluster text{fill:#333;}#mermaid-svg-q8LRcLB2MVXzxn0X .cluster span{color:#333;}#mermaid-svg-q8LRcLB2MVXzxn0X 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-q8LRcLB2MVXzxn0X .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-q8LRcLB2MVXzxn0X rect.text{fill:none;stroke-width:0;}#mermaid-svg-q8LRcLB2MVXzxn0X .icon-shape,#mermaid-svg-q8LRcLB2MVXzxn0X .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-q8LRcLB2MVXzxn0X .icon-shape p,#mermaid-svg-q8LRcLB2MVXzxn0X .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-q8LRcLB2MVXzxn0X .icon-shape .label rect,#mermaid-svg-q8LRcLB2MVXzxn0X .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-q8LRcLB2MVXzxn0X .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-q8LRcLB2MVXzxn0X .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-q8LRcLB2MVXzxn0X :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} DeltaFIFO 使用 knownObjects 的3个场景
cache (Indexer)
DeltaFIFO
ListKeys()
GetByKey(key)
items mapstringDeltas
queue \[\]string
knownObjects KeyListerGetter
threadSafeMap
items mapstringinterface{}
storeIndex
Delete: 检查对象是否在 knownObjects 中
Replace: 检测 knownObjects 中消失的对象
Resync: 将 knownObjects 中的对象重新入队
关键 :cache 的 items 就是 DeltaFIFO 通过 knownObjects 接口查询的数据源。cache 中已 Pop 的对象在 DeltaFIFO 的 items 中不存在,但在 cache 的 items 中存在。这就是为什么 Replace 的"阶段3"需要检查 knownObjects。
二十五、handleDeltas 中 Store 操作详解
25.1 handleDeltas 伪代码(SharedInformer)
go
func (s *sharedIndexInformer) HandleDeltas(obj interface{}, isInInitialList bool) error {
deltas := obj.(Deltas)
// 从最老到最新遍历
for _, d := range deltas {
switch d.Type {
case Sync, Replaced, Added, Updated:
// 对象可能已存在或不存在
if exists, _ := s.indexer.GetByKey(key); exists {
// 已存在 → Update
s.indexer.Update(d.Object)
// 分发 OnUpdate 通知
} else {
// 不存在 → Add
s.indexer.Add(d.Object)
// 分发 OnAdd 通知
}
case Deleted:
s.indexer.Delete(d.Object)
// 分发 OnDelete 通知
}
}
return nil
}
25.2 索引更新场景
场景1:Add 新 Pod (ns=kube-system)
updateIndices(nil, pod-coredns, "kube-system/coredns")
→ updateSingleIndex("namespace", nil, pod-coredns, key)
→ oldIndexValues = [] (nil → 空)
→ indexValues = ["kube-system"]
→ addKeyToIndex("kube-system/coredns", "kube-system", idx)
→ indices["namespace"]["kube-system"].Insert("kube-system/coredns")
场景2:Update Pod(namespace 不变)
updateIndices(pod-old, pod-new, "default/nginx")
→ updateSingleIndex("namespace", pod-old, pod-new, key)
→ oldIndexValues = ["default"]
→ indexValues = ["default"]
→ len==1 && old[0]==new[0] → return (快速路径!)
场景3:Delete Pod
updateIndices(pod-coredns, nil, "kube-system/coredns")
→ updateSingleIndex("namespace", pod-coredns, nil, key)
→ oldIndexValues = ["kube-system"]
→ indexValues = [] (nil → 空)
→ deleteKeyFromIndex("kube-system/coredns", "kube-system", idx)
→ indices["namespace"]["kube-system"].Delete("kube-system/coredns")
→ len(set)==0 → delete(indices["namespace"], "kube-system")
场景4:Update Pod(namespace 变更 --- 极罕见)
updateIndices(pod-old(ns=old), pod-new(ns=new), "old/nginx")
→ updateSingleIndex("namespace", pod-old, pod-new, key)
→ oldIndexValues = ["old"]
→ indexValues = ["new"]
→ len==1 && len==1 but old[0]!="new" → 慢路径
→ deleteKeyFromIndex(key, "old", idx) // 从 "old" 索引中删除
→ addKeyToIndex(key, "new", idx) // 添加到 "new" 索引中
二十六、Replace 与 Index 重建的完整流程
26.1 Replace 触发场景
- Reflector 初始 List:首次从 API Server 获取对象列表
- Reflector 重连:Watch 断连后重新 List
- WatchList (KEP-3157):使用 Watch + initialEventsToList 代替 List
26.2 Index 重建的必要性
Replace 是原子替换 items,旧 items 被完全丢弃。索引必须从零重建,因为:
- 旧索引中的 key 可能已不存在(对象被删除)
- 新对象可能有新的索引值(新 namespace、新 label)
- 索引数据与 items 不一致会导致查询结果错误
#mermaid-svg-tYxCsVwNJrq50f8c{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-tYxCsVwNJrq50f8c .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-tYxCsVwNJrq50f8c .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-tYxCsVwNJrq50f8c .error-icon{fill:#552222;}#mermaid-svg-tYxCsVwNJrq50f8c .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-tYxCsVwNJrq50f8c .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-tYxCsVwNJrq50f8c .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-tYxCsVwNJrq50f8c .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-tYxCsVwNJrq50f8c .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-tYxCsVwNJrq50f8c .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-tYxCsVwNJrq50f8c .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-tYxCsVwNJrq50f8c .marker{fill:#333333;stroke:#333333;}#mermaid-svg-tYxCsVwNJrq50f8c .marker.cross{stroke:#333333;}#mermaid-svg-tYxCsVwNJrq50f8c svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-tYxCsVwNJrq50f8c p{margin:0;}#mermaid-svg-tYxCsVwNJrq50f8c .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-tYxCsVwNJrq50f8c .cluster-label text{fill:#333;}#mermaid-svg-tYxCsVwNJrq50f8c .cluster-label span{color:#333;}#mermaid-svg-tYxCsVwNJrq50f8c .cluster-label span p{background-color:transparent;}#mermaid-svg-tYxCsVwNJrq50f8c .label text,#mermaid-svg-tYxCsVwNJrq50f8c span{fill:#333;color:#333;}#mermaid-svg-tYxCsVwNJrq50f8c .node rect,#mermaid-svg-tYxCsVwNJrq50f8c .node circle,#mermaid-svg-tYxCsVwNJrq50f8c .node ellipse,#mermaid-svg-tYxCsVwNJrq50f8c .node polygon,#mermaid-svg-tYxCsVwNJrq50f8c .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-tYxCsVwNJrq50f8c .rough-node .label text,#mermaid-svg-tYxCsVwNJrq50f8c .node .label text,#mermaid-svg-tYxCsVwNJrq50f8c .image-shape .label,#mermaid-svg-tYxCsVwNJrq50f8c .icon-shape .label{text-anchor:middle;}#mermaid-svg-tYxCsVwNJrq50f8c .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-tYxCsVwNJrq50f8c .rough-node .label,#mermaid-svg-tYxCsVwNJrq50f8c .node .label,#mermaid-svg-tYxCsVwNJrq50f8c .image-shape .label,#mermaid-svg-tYxCsVwNJrq50f8c .icon-shape .label{text-align:center;}#mermaid-svg-tYxCsVwNJrq50f8c .node.clickable{cursor:pointer;}#mermaid-svg-tYxCsVwNJrq50f8c .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-tYxCsVwNJrq50f8c .arrowheadPath{fill:#333333;}#mermaid-svg-tYxCsVwNJrq50f8c .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-tYxCsVwNJrq50f8c .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-tYxCsVwNJrq50f8c .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-tYxCsVwNJrq50f8c .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-tYxCsVwNJrq50f8c .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-tYxCsVwNJrq50f8c .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-tYxCsVwNJrq50f8c .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-tYxCsVwNJrq50f8c .cluster text{fill:#333;}#mermaid-svg-tYxCsVwNJrq50f8c .cluster span{color:#333;}#mermaid-svg-tYxCsVwNJrq50f8c 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-tYxCsVwNJrq50f8c .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-tYxCsVwNJrq50f8c rect.text{fill:none;stroke-width:0;}#mermaid-svg-tYxCsVwNJrq50f8c .icon-shape,#mermaid-svg-tYxCsVwNJrq50f8c .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-tYxCsVwNJrq50f8c .icon-shape p,#mermaid-svg-tYxCsVwNJrq50f8c .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-tYxCsVwNJrq50f8c .icon-shape .label rect,#mermaid-svg-tYxCsVwNJrq50f8c .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-tYxCsVwNJrq50f8c .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-tYxCsVwNJrq50f8c .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-tYxCsVwNJrq50f8c :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 索引重建完成
Replace 后 (list = pod-A, pod-D)
Replace 前
Replace(items, rv)
reset()
updateIndices(nil, item, key)
items: {pod-A, pod-B, pod-C}
indices'namespace':
'default' → {pod-A, pod-B}
'kube-system' → {pod-C}
items: {pod-A, pod-D}
indices = {} (reset)
indices'namespace':
'default' → {pod-A, pod-D}
二十七、核心不变量
27.1 Store 不变量
| # | 不变量 | 维护位置 |
|---|---|---|
| 1 | items 与 index 一致 | updateIndices 在每次写操作后调用 |
| 2 | index 中不存在空 Set | deleteKeyFromIndex 清理 len==0 的 Set |
| 3 | key 在 items 中 ⟺ key 在所有相关 index Set 中 | updateLocked/deleteLocked 同步更新 |
| 4 | indexers 创建后不变 | AddIndexers 只追加不删除 |
| 5 | rv 单调递增(逻辑上) | 每次 Update/Replace/Bookmark 更新 rv |
| 6 | Get/List 返回的是指针,调用者不能修改 | 文档约束(无法代码强制) |
27.2 Index 不变量
| # | 不变量 | 描述 |
|---|---|---|
| 1 | key ∈ items → key ∈ indices[name][indexFunc(obj)] |
存储的对象一定出现在索引中 |
| 2 | key ∈ indices[name][value] → key ∈ items |
索引中的 key 一定有对应对象 |
| 3 | indices[name][value] 非空 ⟹ len(Set) > 0 |
空Set被及时清理 |
| 4 | indexers[name] 不可覆盖 |
AddIndexers 拒绝同名索引 |
27.3 不变量违反的后果
| 违反 | 后果 | 检测方式 |
|---|---|---|
| items 与 index 不一致 | ByIndex 返回过时/遗漏对象 | 用户 Controller 行为异常 |
| 空Set未清理 | 内存泄漏 | 监控 items vs indices 大小 |
| indexers 被覆盖 | 索引函数变更导致查询结果变化 | AddIndexers 返回 error |
| 返回对象被修改 | 索引值与实际对象不一致 | MutationDetector 检测 |
二十八、MutationDetector --- 深拷贝检测
Store 的 Get/List 返回的是指针,不是深拷贝。如果用户修改了返回的对象,会破坏索引一致性。MutationDetector 是一个可选的运行时检测机制:
#mermaid-svg-FyZ2EYnTDXc9caEa{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-FyZ2EYnTDXc9caEa .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-FyZ2EYnTDXc9caEa .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-FyZ2EYnTDXc9caEa .error-icon{fill:#552222;}#mermaid-svg-FyZ2EYnTDXc9caEa .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-FyZ2EYnTDXc9caEa .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-FyZ2EYnTDXc9caEa .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-FyZ2EYnTDXc9caEa .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-FyZ2EYnTDXc9caEa .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-FyZ2EYnTDXc9caEa .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-FyZ2EYnTDXc9caEa .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-FyZ2EYnTDXc9caEa .marker{fill:#333333;stroke:#333333;}#mermaid-svg-FyZ2EYnTDXc9caEa .marker.cross{stroke:#333333;}#mermaid-svg-FyZ2EYnTDXc9caEa svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-FyZ2EYnTDXc9caEa p{margin:0;}#mermaid-svg-FyZ2EYnTDXc9caEa .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-FyZ2EYnTDXc9caEa .cluster-label text{fill:#333;}#mermaid-svg-FyZ2EYnTDXc9caEa .cluster-label span{color:#333;}#mermaid-svg-FyZ2EYnTDXc9caEa .cluster-label span p{background-color:transparent;}#mermaid-svg-FyZ2EYnTDXc9caEa .label text,#mermaid-svg-FyZ2EYnTDXc9caEa span{fill:#333;color:#333;}#mermaid-svg-FyZ2EYnTDXc9caEa .node rect,#mermaid-svg-FyZ2EYnTDXc9caEa .node circle,#mermaid-svg-FyZ2EYnTDXc9caEa .node ellipse,#mermaid-svg-FyZ2EYnTDXc9caEa .node polygon,#mermaid-svg-FyZ2EYnTDXc9caEa .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-FyZ2EYnTDXc9caEa .rough-node .label text,#mermaid-svg-FyZ2EYnTDXc9caEa .node .label text,#mermaid-svg-FyZ2EYnTDXc9caEa .image-shape .label,#mermaid-svg-FyZ2EYnTDXc9caEa .icon-shape .label{text-anchor:middle;}#mermaid-svg-FyZ2EYnTDXc9caEa .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-FyZ2EYnTDXc9caEa .rough-node .label,#mermaid-svg-FyZ2EYnTDXc9caEa .node .label,#mermaid-svg-FyZ2EYnTDXc9caEa .image-shape .label,#mermaid-svg-FyZ2EYnTDXc9caEa .icon-shape .label{text-align:center;}#mermaid-svg-FyZ2EYnTDXc9caEa .node.clickable{cursor:pointer;}#mermaid-svg-FyZ2EYnTDXc9caEa .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-FyZ2EYnTDXc9caEa .arrowheadPath{fill:#333333;}#mermaid-svg-FyZ2EYnTDXc9caEa .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-FyZ2EYnTDXc9caEa .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-FyZ2EYnTDXc9caEa .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-FyZ2EYnTDXc9caEa .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-FyZ2EYnTDXc9caEa .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-FyZ2EYnTDXc9caEa .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-FyZ2EYnTDXc9caEa .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-FyZ2EYnTDXc9caEa .cluster text{fill:#333;}#mermaid-svg-FyZ2EYnTDXc9caEa .cluster span{color:#333;}#mermaid-svg-FyZ2EYnTDXc9caEa 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-FyZ2EYnTDXc9caEa .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-FyZ2EYnTDXc9caEa rect.text{fill:none;stroke-width:0;}#mermaid-svg-FyZ2EYnTDXc9caEa .icon-shape,#mermaid-svg-FyZ2EYnTDXc9caEa .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-FyZ2EYnTDXc9caEa .icon-shape p,#mermaid-svg-FyZ2EYnTDXc9caEa .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-FyZ2EYnTDXc9caEa .icon-shape .label rect,#mermaid-svg-FyZ2EYnTDXc9caEa .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-FyZ2EYnTDXc9caEa .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-FyZ2EYnTDXc9caEa .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-FyZ2EYnTDXc9caEa :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} Yes
No
MutationDetector.Run()
定期检查
比较存储对象的深拷贝与当前值
发现差异?
panic('对象被修改!')
继续监控
设计哲学:无法在代码层面阻止修改返回对象,所以用 MutationDetector 检测并 panic,强制开发者修复。
二十九、Store 与 DeltaFIFO 的协作关系
29.1 数据一致性保证
#mermaid-svg-hZFm75rLHP1urzxm{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-hZFm75rLHP1urzxm .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-hZFm75rLHP1urzxm .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-hZFm75rLHP1urzxm .error-icon{fill:#552222;}#mermaid-svg-hZFm75rLHP1urzxm .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-hZFm75rLHP1urzxm .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-hZFm75rLHP1urzxm .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-hZFm75rLHP1urzxm .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-hZFm75rLHP1urzxm .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-hZFm75rLHP1urzxm .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-hZFm75rLHP1urzxm .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-hZFm75rLHP1urzxm .marker{fill:#333333;stroke:#333333;}#mermaid-svg-hZFm75rLHP1urzxm .marker.cross{stroke:#333333;}#mermaid-svg-hZFm75rLHP1urzxm svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-hZFm75rLHP1urzxm p{margin:0;}#mermaid-svg-hZFm75rLHP1urzxm .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-hZFm75rLHP1urzxm .cluster-label text{fill:#333;}#mermaid-svg-hZFm75rLHP1urzxm .cluster-label span{color:#333;}#mermaid-svg-hZFm75rLHP1urzxm .cluster-label span p{background-color:transparent;}#mermaid-svg-hZFm75rLHP1urzxm .label text,#mermaid-svg-hZFm75rLHP1urzxm span{fill:#333;color:#333;}#mermaid-svg-hZFm75rLHP1urzxm .node rect,#mermaid-svg-hZFm75rLHP1urzxm .node circle,#mermaid-svg-hZFm75rLHP1urzxm .node ellipse,#mermaid-svg-hZFm75rLHP1urzxm .node polygon,#mermaid-svg-hZFm75rLHP1urzxm .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-hZFm75rLHP1urzxm .rough-node .label text,#mermaid-svg-hZFm75rLHP1urzxm .node .label text,#mermaid-svg-hZFm75rLHP1urzxm .image-shape .label,#mermaid-svg-hZFm75rLHP1urzxm .icon-shape .label{text-anchor:middle;}#mermaid-svg-hZFm75rLHP1urzxm .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-hZFm75rLHP1urzxm .rough-node .label,#mermaid-svg-hZFm75rLHP1urzxm .node .label,#mermaid-svg-hZFm75rLHP1urzxm .image-shape .label,#mermaid-svg-hZFm75rLHP1urzxm .icon-shape .label{text-align:center;}#mermaid-svg-hZFm75rLHP1urzxm .node.clickable{cursor:pointer;}#mermaid-svg-hZFm75rLHP1urzxm .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-hZFm75rLHP1urzxm .arrowheadPath{fill:#333333;}#mermaid-svg-hZFm75rLHP1urzxm .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-hZFm75rLHP1urzxm .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-hZFm75rLHP1urzxm .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-hZFm75rLHP1urzxm .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-hZFm75rLHP1urzxm .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-hZFm75rLHP1urzxm .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-hZFm75rLHP1urzxm .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-hZFm75rLHP1urzxm .cluster text{fill:#333;}#mermaid-svg-hZFm75rLHP1urzxm .cluster span{color:#333;}#mermaid-svg-hZFm75rLHP1urzxm 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-hZFm75rLHP1urzxm .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-hZFm75rLHP1urzxm rect.text{fill:none;stroke-width:0;}#mermaid-svg-hZFm75rLHP1urzxm .icon-shape,#mermaid-svg-hZFm75rLHP1urzxm .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-hZFm75rLHP1urzxm .icon-shape p,#mermaid-svg-hZFm75rLHP1urzxm .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-hZFm75rLHP1urzxm .icon-shape .label rect,#mermaid-svg-hZFm75rLHP1urzxm .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-hZFm75rLHP1urzxm .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-hZFm75rLHP1urzxm .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-hZFm75rLHP1urzxm :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 一致性关键
cache (存储)
DeltaFIFO (队列)
Add → itemskey = Deltas{Added, obj}
Pop → delete(items, key) → process(Deltas)
handleDeltas → cache.Add(obj) → itemskey = obj
Pop 在 DeltaFIFO 锁内调用 process
process → cache.Add/Update/Delete → items 更新
Pop 保证 FIFO 顺序
关键保证:
- Pop 在 DeltaFIFO 的锁内调用 handleDeltas
- handleDeltas 同步调用 cache.Add/Update/Delete
- cache 操作完成后,DeltaFIFO 的 items 和 cache 的 items 保持一致
- 因为 Pop 是单线程的(注释明确说明),不存在并发 Pop
29.2 knownObjects 查询时序
cache (knownObjects) DeltaFIFO Reflector cache (knownObjects) DeltaFIFO Reflector #mermaid-svg-JVwsWcxlazBTS4mb{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-JVwsWcxlazBTS4mb .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-JVwsWcxlazBTS4mb .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-JVwsWcxlazBTS4mb .error-icon{fill:#552222;}#mermaid-svg-JVwsWcxlazBTS4mb .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-JVwsWcxlazBTS4mb .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-JVwsWcxlazBTS4mb .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-JVwsWcxlazBTS4mb .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-JVwsWcxlazBTS4mb .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-JVwsWcxlazBTS4mb .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-JVwsWcxlazBTS4mb .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-JVwsWcxlazBTS4mb .marker{fill:#333333;stroke:#333333;}#mermaid-svg-JVwsWcxlazBTS4mb .marker.cross{stroke:#333333;}#mermaid-svg-JVwsWcxlazBTS4mb svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-JVwsWcxlazBTS4mb p{margin:0;}#mermaid-svg-JVwsWcxlazBTS4mb .actor{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-JVwsWcxlazBTS4mb text.actor>tspan{fill:black;stroke:none;}#mermaid-svg-JVwsWcxlazBTS4mb .actor-line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-JVwsWcxlazBTS4mb .innerArc{stroke-width:1.5;stroke-dasharray:none;}#mermaid-svg-JVwsWcxlazBTS4mb .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333;}#mermaid-svg-JVwsWcxlazBTS4mb .messageLine1{stroke-width:1.5;stroke-dasharray:2,2;stroke:#333;}#mermaid-svg-JVwsWcxlazBTS4mb #arrowhead path{fill:#333;stroke:#333;}#mermaid-svg-JVwsWcxlazBTS4mb .sequenceNumber{fill:white;}#mermaid-svg-JVwsWcxlazBTS4mb #sequencenumber{fill:#333;}#mermaid-svg-JVwsWcxlazBTS4mb #crosshead path{fill:#333;stroke:#333;}#mermaid-svg-JVwsWcxlazBTS4mb .messageText{fill:#333;stroke:none;}#mermaid-svg-JVwsWcxlazBTS4mb .labelBox{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-JVwsWcxlazBTS4mb .labelText,#mermaid-svg-JVwsWcxlazBTS4mb .labelText>tspan{fill:black;stroke:none;}#mermaid-svg-JVwsWcxlazBTS4mb .loopText,#mermaid-svg-JVwsWcxlazBTS4mb .loopText>tspan{fill:black;stroke:none;}#mermaid-svg-JVwsWcxlazBTS4mb .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-JVwsWcxlazBTS4mb .note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-JVwsWcxlazBTS4mb .noteText,#mermaid-svg-JVwsWcxlazBTS4mb .noteText>tspan{fill:black;stroke:none;}#mermaid-svg-JVwsWcxlazBTS4mb .activation0{fill:#f4f4f4;stroke:#666;}#mermaid-svg-JVwsWcxlazBTS4mb .activation1{fill:#f4f4f4;stroke:#666;}#mermaid-svg-JVwsWcxlazBTS4mb .activation2{fill:#f4f4f4;stroke:#666;}#mermaid-svg-JVwsWcxlazBTS4mb .actorPopupMenu{position:absolute;}#mermaid-svg-JVwsWcxlazBTS4mb .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-JVwsWcxlazBTS4mb .actor-man line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-JVwsWcxlazBTS4mb .actor-man circle,#mermaid-svg-JVwsWcxlazBTS4mb line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;stroke-width:2px;}#mermaid-svg-JVwsWcxlazBTS4mb :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} Replace 触发 阶段3: 检查 knownObjects pod-B 不在 list 中且不在 DF.items 中→ 需要生成墓碑 Replace(pod-A, pod-D, rv)ListKeys()"default/pod-A", "default/pod-B", "default/pod-C"GetByKey("default/pod-B")(pod-B, true, nil)queueActionLocked(Deleted, DeletedFinalStateUnknown{key: "default/pod-B", obj: pod-B})
三十、GenericLister --- 用户查询 API
30.1 GenericLister 构建
Store/Indexer 是底层存储,用户通过 GenericLister 查询:
#mermaid-svg-ItcUxPwa0XtTWuvF{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-ItcUxPwa0XtTWuvF .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-ItcUxPwa0XtTWuvF .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-ItcUxPwa0XtTWuvF .error-icon{fill:#552222;}#mermaid-svg-ItcUxPwa0XtTWuvF .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-ItcUxPwa0XtTWuvF .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-ItcUxPwa0XtTWuvF .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-ItcUxPwa0XtTWuvF .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-ItcUxPwa0XtTWuvF .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-ItcUxPwa0XtTWuvF .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-ItcUxPwa0XtTWuvF .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-ItcUxPwa0XtTWuvF .marker{fill:#333333;stroke:#333333;}#mermaid-svg-ItcUxPwa0XtTWuvF .marker.cross{stroke:#333333;}#mermaid-svg-ItcUxPwa0XtTWuvF svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-ItcUxPwa0XtTWuvF p{margin:0;}#mermaid-svg-ItcUxPwa0XtTWuvF .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-ItcUxPwa0XtTWuvF .cluster-label text{fill:#333;}#mermaid-svg-ItcUxPwa0XtTWuvF .cluster-label span{color:#333;}#mermaid-svg-ItcUxPwa0XtTWuvF .cluster-label span p{background-color:transparent;}#mermaid-svg-ItcUxPwa0XtTWuvF .label text,#mermaid-svg-ItcUxPwa0XtTWuvF span{fill:#333;color:#333;}#mermaid-svg-ItcUxPwa0XtTWuvF .node rect,#mermaid-svg-ItcUxPwa0XtTWuvF .node circle,#mermaid-svg-ItcUxPwa0XtTWuvF .node ellipse,#mermaid-svg-ItcUxPwa0XtTWuvF .node polygon,#mermaid-svg-ItcUxPwa0XtTWuvF .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-ItcUxPwa0XtTWuvF .rough-node .label text,#mermaid-svg-ItcUxPwa0XtTWuvF .node .label text,#mermaid-svg-ItcUxPwa0XtTWuvF .image-shape .label,#mermaid-svg-ItcUxPwa0XtTWuvF .icon-shape .label{text-anchor:middle;}#mermaid-svg-ItcUxPwa0XtTWuvF .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-ItcUxPwa0XtTWuvF .rough-node .label,#mermaid-svg-ItcUxPwa0XtTWuvF .node .label,#mermaid-svg-ItcUxPwa0XtTWuvF .image-shape .label,#mermaid-svg-ItcUxPwa0XtTWuvF .icon-shape .label{text-align:center;}#mermaid-svg-ItcUxPwa0XtTWuvF .node.clickable{cursor:pointer;}#mermaid-svg-ItcUxPwa0XtTWuvF .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-ItcUxPwa0XtTWuvF .arrowheadPath{fill:#333333;}#mermaid-svg-ItcUxPwa0XtTWuvF .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-ItcUxPwa0XtTWuvF .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-ItcUxPwa0XtTWuvF .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-ItcUxPwa0XtTWuvF .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-ItcUxPwa0XtTWuvF .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-ItcUxPwa0XtTWuvF .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-ItcUxPwa0XtTWuvF .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-ItcUxPwa0XtTWuvF .cluster text{fill:#333;}#mermaid-svg-ItcUxPwa0XtTWuvF .cluster span{color:#333;}#mermaid-svg-ItcUxPwa0XtTWuvF 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-ItcUxPwa0XtTWuvF .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-ItcUxPwa0XtTWuvF rect.text{fill:none;stroke-width:0;}#mermaid-svg-ItcUxPwa0XtTWuvF .icon-shape,#mermaid-svg-ItcUxPwa0XtTWuvF .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-ItcUxPwa0XtTWuvF .icon-shape p,#mermaid-svg-ItcUxPwa0XtTWuvF .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-ItcUxPwa0XtTWuvF .icon-shape .label rect,#mermaid-svg-ItcUxPwa0XtTWuvF .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-ItcUxPwa0XtTWuvF .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-ItcUxPwa0XtTWuvF .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-ItcUxPwa0XtTWuvF :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} Namespace(namespace)
List(labels.Selector)
Get(name)
List(labels.Selector)
Get(name)
Indexer
NewGenericLister(indexer, groupResource)
GenericLister
GenericNamespaceLister
30.2 查询路径
lister.List(selector) →
indexer.List() → 全部对象 → 过滤匹配 selector 的对象
lister.Namespace("default").Get("nginx") →
indexer.ByIndex("namespace", "default") → 该 namespace 的对象 → 按 name 过滤
性能对比:
| 查询方式 | 路径 | 复杂度 |
|---|---|---|
ByIndex("namespace", ns) |
索引加速 | O(k),k=该 ns 的对象数 |
List() + 过滤 |
全扫描 | O(n),n=总对象数 |
三十一、完整组件协作关系总览
31.1 组件调用链
#mermaid-svg-SHD0xGLKCwFHEFFc{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-SHD0xGLKCwFHEFFc .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-SHD0xGLKCwFHEFFc .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-SHD0xGLKCwFHEFFc .error-icon{fill:#552222;}#mermaid-svg-SHD0xGLKCwFHEFFc .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-SHD0xGLKCwFHEFFc .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-SHD0xGLKCwFHEFFc .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-SHD0xGLKCwFHEFFc .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-SHD0xGLKCwFHEFFc .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-SHD0xGLKCwFHEFFc .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-SHD0xGLKCwFHEFFc .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-SHD0xGLKCwFHEFFc .marker{fill:#333333;stroke:#333333;}#mermaid-svg-SHD0xGLKCwFHEFFc .marker.cross{stroke:#333333;}#mermaid-svg-SHD0xGLKCwFHEFFc svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-SHD0xGLKCwFHEFFc p{margin:0;}#mermaid-svg-SHD0xGLKCwFHEFFc .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-SHD0xGLKCwFHEFFc .cluster-label text{fill:#333;}#mermaid-svg-SHD0xGLKCwFHEFFc .cluster-label span{color:#333;}#mermaid-svg-SHD0xGLKCwFHEFFc .cluster-label span p{background-color:transparent;}#mermaid-svg-SHD0xGLKCwFHEFFc .label text,#mermaid-svg-SHD0xGLKCwFHEFFc span{fill:#333;color:#333;}#mermaid-svg-SHD0xGLKCwFHEFFc .node rect,#mermaid-svg-SHD0xGLKCwFHEFFc .node circle,#mermaid-svg-SHD0xGLKCwFHEFFc .node ellipse,#mermaid-svg-SHD0xGLKCwFHEFFc .node polygon,#mermaid-svg-SHD0xGLKCwFHEFFc .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-SHD0xGLKCwFHEFFc .rough-node .label text,#mermaid-svg-SHD0xGLKCwFHEFFc .node .label text,#mermaid-svg-SHD0xGLKCwFHEFFc .image-shape .label,#mermaid-svg-SHD0xGLKCwFHEFFc .icon-shape .label{text-anchor:middle;}#mermaid-svg-SHD0xGLKCwFHEFFc .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-SHD0xGLKCwFHEFFc .rough-node .label,#mermaid-svg-SHD0xGLKCwFHEFFc .node .label,#mermaid-svg-SHD0xGLKCwFHEFFc .image-shape .label,#mermaid-svg-SHD0xGLKCwFHEFFc .icon-shape .label{text-align:center;}#mermaid-svg-SHD0xGLKCwFHEFFc .node.clickable{cursor:pointer;}#mermaid-svg-SHD0xGLKCwFHEFFc .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-SHD0xGLKCwFHEFFc .arrowheadPath{fill:#333333;}#mermaid-svg-SHD0xGLKCwFHEFFc .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-SHD0xGLKCwFHEFFc .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-SHD0xGLKCwFHEFFc .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-SHD0xGLKCwFHEFFc .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-SHD0xGLKCwFHEFFc .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-SHD0xGLKCwFHEFFc .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-SHD0xGLKCwFHEFFc .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-SHD0xGLKCwFHEFFc .cluster text{fill:#333;}#mermaid-svg-SHD0xGLKCwFHEFFc .cluster span{color:#333;}#mermaid-svg-SHD0xGLKCwFHEFFc 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-SHD0xGLKCwFHEFFc .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-SHD0xGLKCwFHEFFc rect.text{fill:none;stroke-width:0;}#mermaid-svg-SHD0xGLKCwFHEFFc .icon-shape,#mermaid-svg-SHD0xGLKCwFHEFFc .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-SHD0xGLKCwFHEFFc .icon-shape p,#mermaid-svg-SHD0xGLKCwFHEFFc .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-SHD0xGLKCwFHEFFc .icon-shape .label rect,#mermaid-svg-SHD0xGLKCwFHEFFc .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-SHD0xGLKCwFHEFFc .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-SHD0xGLKCwFHEFFc .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-SHD0xGLKCwFHEFFc :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} storeIndex
threadSafeMap
cache (Store/Indexer)
DeltaFIFO
Reflector
knownObjects.ListKeys()
handleDeltas
handleDeltas
handleDeltas
knownObjects
knownObjects
list()
watch()
syncWith()
Add()
Update()
Delete()
Replace()
Resync()
Pop()
Add()
Update()
Delete()
Replace()
ByIndex()
updateLocked()
deleteLocked()
Replace()
rv 更新
updateIndices()
reset()
getKeysByIndex()
31.2 源文件依赖矩阵
| 文件 | 依赖 | 被依赖 |
|---|---|---|
store.go |
meta, metav1, errors, fmt |
shared_informer.go, controller.go |
thread_safe_store.go |
sync, sets, meta, utiltrace, clientgofeaturegate, strconv, time |
store.go |
index.go |
meta, sets, fmt |
thread_safe_store.go, shared_informer.go |
三十二、错误处理策略
| 错误场景 | 处理方式 | 后果 |
|---|---|---|
| KeyFunc 失败 | 返回 KeyError | 操作不执行,调用者决定重试 |
| IndexFunc 失败 | panic | 程序崩溃(索引不一致更危险) |
| TransformFunc 失败 | 返回 wrapped error | 操作不执行 |
| AddIndexers 冲突 | 返回 error | 新索引不添加 |
| Transaction 部分失败 | 执行成功的,返回 TransactionError | 部分对象已变更 |
| meta.Accessor 失败 (rvFromObject) | rvErr != nil → 不更新 rv | rv 可能落后 |
| rv 解析失败 (parseRV) | parseErr != nil → 不更新 metrics | 指标不更新,rv 字符串仍更新 |
关键设计选择:IndexFunc 失败 → panic。这与其他操作的 error 返回不同。
原因:
- 索引是 Store 的核心功能,索引不一致会导致所有查询结果错误
- IndexFunc 失败意味着严重的程序 bug(如传入了错误类型的对象)
- 在持锁状态下返回 error 后的处理极其复杂
三十三、性能优化总结
33.1 已实现的优化
| 优化 | 位置 | 效果 |
|---|---|---|
| 快速路径:单值索引未变 | updateSingleIndex | Update 时 namespace 不变 → 跳过索引更新 |
| 快速路径:单值查询 | getKeysFromIndex | 单 indexedValue → 直接取 Set,不去重 |
| 空Set清理 | deleteKeyFromIndex | 防止内存泄漏(高基数短生命周期资源) |
| 批量锁 | Transaction | N个操作1次锁获取 |
| RLock 读操作 | Get/List/Index/ByIndex | 允许并发读 |
| Replace 原子替换 | threadSafeMap.Replace | 直接替换 map,不逐个操作 |
| items map 预分配 | List/Replace | make(\[\]T, 0, len(items)) |
33.2 潜在风险
| 风险 | 描述 | 缓解 |
|---|---|---|
| List 返回指针 | 调用者修改会破坏索引 | MutationDetector 检测 |
| Replace 期间持锁 | 替换+重建索引期间阻塞所有操作 | Replace 应尽量少调用 |
| IndexFunc panic | 会导致整个进程崩溃 | IndexFunc 应做类型检查 |
| AddIndexers 对已有对象建索引 | O(n*m) 复杂度 | 应在首次使用前注册索引 |
三十四、设计模式完整总结
| # | 模式 | 体现 | 价值 |
|---|---|---|---|
| 1 | 三层分离 | cache→threadSafeMap→storeIndex | 职责清晰,每层可独立测试 |
| 2 | 门面模式 | cache 委托 threadSafeMap | 简化上层调用(自动 Key 提取+变换) |
| 3 | 选项模式 | StoreOption/ThreadSafeStoreOption | 向后兼容+可扩展 |
| 4 | 接口隔离 | Store vs Indexer vs ThreadSafeStore | 最小依赖 |
| 5 | 接口组合 | Indexer = Store + 索引方法 | 按需组合能力 |
| 6 | 快速路径 | 单值索引未变→跳过 | 最常见场景零开销 |
| 7 | 空Set清理 | deleteKeyFromIndex | 防止内存泄漏 |
| 8 | panic 一致性 | IndexFunc 失败→panic | 索引不一致比崩溃更危险 |
| 9 | RV 追踪 | 每次写操作更新 | Watch 断连重续 |
| 10 | Feature Gate | AtomicFIFO 控制 RV 暴露 | 渐进式演进 |
| 11 | 批量锁 | Transaction 单次锁 | 减少锁竞争 |
| 12 | 安全 Delete | cache 用 DeleteWithObject | 确保 RV 不落后 |
| 13 | Add=Update | Store 只存最新值 | 简化语义 |
| 14 | 编译时检查 | var _ Store = &cache{} |
保证接口实现 |
| 15 | 空对象 | noopMetric | 无指标时零开销 |
| 16 | Key-Value 分离 | cache 做 obj→key,底层只处理 key | 底层不关心对象类型 |