Skip to content

Commit

Permalink
Merge pull request #3 from crenshaw-dev/iterate-improvements
Browse files Browse the repository at this point in the history
make childrenByUID sparse
  • Loading branch information
andrii-korotkov-verkada authored Jul 17, 2024
2 parents 0f77c57 + 8dbcf05 commit 9a98c83
Show file tree
Hide file tree
Showing 2 changed files with 28 additions and 25 deletions.
43 changes: 22 additions & 21 deletions pkg/cache/cluster.go
Original file line number Diff line number Diff line change
Expand Up @@ -1021,7 +1021,7 @@ func (c *clusterCache) IterateHierarchyV2(keys []kube.ResourceKey, action func(r
}
for namespace, namespaceKeys := range keysPerNamespace {
nsNodes := c.nsIndex[namespace]
graph, childrenByUID := buildGraph(nsNodes)
graph := buildGraph(nsNodes)
visited := make(map[kube.ResourceKey]int)
for _, key := range namespaceKeys {
visited[key] = 0
Expand All @@ -1033,15 +1033,17 @@ func (c *clusterCache) IterateHierarchyV2(keys []kube.ResourceKey, action func(r
continue
}
visited[key] = 1
for _, child := range childrenByUID[key] {
if visited[child.ResourceKey()] == 0 && action(child, nsNodes) {
child.iterateChildrenV2(graph, nsNodes, visited, func(err error, child *Resource, namespaceResources map[kube.ResourceKey]*Resource) bool {
if err != nil {
c.log.V(2).Info(err.Error())
return false
}
return action(child, namespaceResources)
})
if _, ok := graph[key]; ok {
for _, child := range graph[key] {
if visited[child.ResourceKey()] == 0 && action(child, nsNodes) {
child.iterateChildrenV2(graph, nsNodes, visited, func(err error, child *Resource, namespaceResources map[kube.ResourceKey]*Resource) bool {
if err != nil {
c.log.V(2).Info(err.Error())
return false
}
return action(child, namespaceResources)
})
}
}
}
visited[key] = 2
Expand All @@ -1055,19 +1057,17 @@ type graphKey struct {
name string
}

func buildGraph(nsNodes map[kube.ResourceKey]*Resource) (map[kube.ResourceKey][]kube.ResourceKey, map[kube.ResourceKey]map[types.UID]*Resource) {
func buildGraph(nsNodes map[kube.ResourceKey]*Resource) map[kube.ResourceKey]map[types.UID]*Resource {
// Prepare to construct a graph
nodesByUID := make(map[types.UID][]*Resource, len(nsNodes))
nodeByGraphKey := make(map[graphKey]*Resource, len(nsNodes))
childrenByUID := make(map[kube.ResourceKey]map[types.UID]*Resource, len(nsNodes))
for _, node := range nsNodes {
nodesByUID[node.Ref.UID] = append(nodesByUID[node.Ref.UID], node)
nodeByGraphKey[graphKey{node.Ref.Kind, node.Ref.APIVersion, node.Ref.Name}] = node
childrenByUID[node.ResourceKey()] = make(map[types.UID]*Resource)
}

// In graph, they key is the parent and the value is a list of children.
graph := make(map[kube.ResourceKey][]kube.ResourceKey)
graph := make(map[kube.ResourceKey]map[types.UID]*Resource)

// Loop through all nodes, calling each one "childNode," because we're only bothering with it if it has a parent.
for _, childNode := range nsNodes {
Expand All @@ -1084,30 +1084,31 @@ func buildGraph(nsNodes map[kube.ResourceKey]*Resource) (map[kube.ResourceKey][]
}
}

// Now that we have the UID of the parent, update the graph and the childrenByUID map.
// Now that we have the UID of the parent, update the graph.
uidNodes, ok := nodesByUID[ownerRef.UID]
if ok {
for _, uidNode := range uidNodes {
// Update the graph for this owner to include the child.
graph[uidNode.ResourceKey()] = append(graph[uidNode.ResourceKey()], childNode.ResourceKey())

r, ok := childrenByUID[uidNode.ResourceKey()][childNode.Ref.UID]
if _, ok := graph[uidNode.ResourceKey()]; !ok {
graph[uidNode.ResourceKey()] = make(map[types.UID]*Resource)
}
r, ok := graph[uidNode.ResourceKey()][childNode.Ref.UID]
if !ok {
childrenByUID[uidNode.ResourceKey()][childNode.Ref.UID] = childNode
graph[uidNode.ResourceKey()][childNode.Ref.UID] = childNode
} else if r != nil {
// The object might have multiple children with the same UID (e.g. replicaset from apps and extensions group).
// It is ok to pick any object, but we need to make sure we pick the same child after every refresh.
key1 := r.ResourceKey()
key2 := childNode.ResourceKey()
if strings.Compare(key1.String(), key2.String()) > 0 {
childrenByUID[uidNode.ResourceKey()][childNode.Ref.UID] = childNode
graph[uidNode.ResourceKey()][childNode.Ref.UID] = childNode
}
}
}
}
}
}
return graph, childrenByUID
return graph
}

// IsNamespaced answers if specified group/kind is a namespaced resource API or not
Expand Down
10 changes: 6 additions & 4 deletions pkg/cache/resource.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package cache

import (
"fmt"
"k8s.io/apimachinery/pkg/types"

v1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
Expand Down Expand Up @@ -101,7 +102,7 @@ func (r *Resource) iterateChildren(ns map[kube.ResourceKey]*Resource, parents ma
}

// iterateChildrenV2 is a depth-first traversal of the graph of resources starting from the current resource.
func (r *Resource) iterateChildrenV2(graph map[kube.ResourceKey][]kube.ResourceKey, ns map[kube.ResourceKey]*Resource, visited map[kube.ResourceKey]int, action func(err error, child *Resource, namespaceResources map[kube.ResourceKey]*Resource) bool) {
func (r *Resource) iterateChildrenV2(graph map[kube.ResourceKey]map[types.UID]*Resource, ns map[kube.ResourceKey]*Resource, visited map[kube.ResourceKey]int, action func(err error, child *Resource, namespaceResources map[kube.ResourceKey]*Resource) bool) {
key := r.ResourceKey()
if visited[key] == 2 {
return
Expand All @@ -112,11 +113,12 @@ func (r *Resource) iterateChildrenV2(graph map[kube.ResourceKey][]kube.ResourceK
// this indicates that we've finished processing this node's children
visited[key] = 2
}()
childKeys, ok := graph[key]
if !ok || childKeys == nil {
children, ok := graph[key]
if !ok || children == nil {
return
}
for _, childKey := range childKeys {
for _, c := range children {
childKey := c.ResourceKey()
child := ns[childKey]
if visited[childKey] == 1 {
// Since we encountered a node that we're currently processing, we know we have a circular dependency.
Expand Down

0 comments on commit 9a98c83

Please sign in to comment.