Skip to content

Commit fe1b057

Browse files
committed
Remove usually redundant processing for cluster/ns parent/child relationships based on input keys; defer to namespace scan
Signed-off-by: Jonathan Ogilvie <jonathan.ogilvie@sumologic.com>
1 parent b3792f8 commit fe1b057

2 files changed

Lines changed: 16 additions & 65 deletions

File tree

gitops-engine/pkg/cache/cluster.go

Lines changed: 16 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1087,22 +1087,17 @@ func (c *clusterCache) IterateHierarchyV2(keys []kube.ResourceKey, action func(r
10871087

10881088
// Build namespace map with resource validation
10891089
keysPerNamespace := make(map[string][]kube.ResourceKey)
1090-
hasClusterNamespace := false
10911090
for _, key := range keys {
10921091
// PERFORMANCE HOTSPOT: This resource lookup is the most expensive operation in the function
10931092
// (~50% of CPU time). The map access triggers hash computation for ResourceKey.
10941093
if _, ok := c.resources[key]; ok {
10951094
keysPerNamespace[key.Namespace] = append(keysPerNamespace[key.Namespace], key)
1096-
if key.Namespace == "" {
1097-
hasClusterNamespace = true
1098-
}
10991095
}
11001096
}
11011097

1102-
// Fast path: feature disabled, no keys involve cluster namespace, or no cluster resources
1103-
// PERFORMANCE NOTE: Condition order matters! Check cheaper boolean conditions before map lookups.
1104-
// The len(c.nsIndex[""]) check involves a map access and should be evaluated last.
1105-
if c.disableClusterScopedParentRefs || !hasClusterNamespace || len(c.nsIndex[""]) == 0 {
1098+
// Fast path: feature disabled or no orphaned namespace scanning
1099+
// All cross-namespace complexity is deferred to orphaned resource processing
1100+
if c.disableClusterScopedParentRefs || orphanedResourceNamespace == "" {
11061101
// Process all namespaces with simple graph building (no cross-namespace overhead)
11071102
for namespace, namespaceKeys := range keysPerNamespace {
11081103
nsNodes := c.nsIndex[namespace]
@@ -1114,39 +1109,29 @@ func (c *clusterCache) IterateHierarchyV2(keys []kube.ResourceKey, action func(r
11141109
return
11151110
}
11161111

1117-
// Slow path: cross-namespace refs enabled, cluster resources exist, and we're processing cluster keys
1112+
// Slow path: cross-namespace refs enabled and orphaned namespace scanning requested
1113+
// Process explicit keys with simple graphs, then do comprehensive orphaned scanning
11181114

11191115
// Use a shared visited map to prevent duplicate visits across namespaces
11201116
visited := make(map[kube.ResourceKey]int, len(keys))
11211117

1122-
// Build cluster nodes map for cross-namespace lookups
1123-
clusterNodes := c.nsIndex[""]
1124-
clusterNodesByUID := make(map[types.UID][]*Resource, len(clusterNodes))
1125-
for _, node := range clusterNodes {
1126-
clusterNodesByUID[node.Ref.UID] = append(clusterNodesByUID[node.Ref.UID], node)
1127-
}
1128-
1129-
// First process the namespaces we have explicit keys for
1118+
// Process explicit keys with simple per-namespace graphs
11301119
for namespace, namespaceKeys := range keysPerNamespace {
11311120
nsNodes := c.nsIndex[namespace]
1132-
1133-
// Only use cross-namespace graph for namespaced resources that might have cluster parents
1134-
var graph map[kube.ResourceKey]map[types.UID]*Resource
1135-
if namespace != "" && len(clusterNodesByUID) > 0 {
1136-
// This is a namespace that might have cluster-scoped parents
1137-
graph = buildGraphWithCrossNamespace(nsNodes, clusterNodesByUID)
1138-
} else {
1139-
// Cluster namespace or no cluster resources - use simple graph
1140-
graph = buildGraph(nsNodes)
1141-
}
1121+
graph := buildGraph(nsNodes) // Always simple graph for explicit keys
11421122
c.processNamespaceHierarchy(namespaceKeys, nsNodes, graph, visited, action)
11431123
}
11441124

1145-
// Check for orphaned resources in the specified namespace (if any)
1146-
// These are children of cluster-scoped resources that weren't in the initial keys
1147-
if orphanedResourceNamespace != "" /* hasClusterNamespace is always true here */ {
1148-
c.processOrphanedResources(orphanedResourceNamespace, keysPerNamespace, clusterNodesByUID, visited, action)
1125+
// Build cluster nodes map for cross-namespace lookups in orphaned processing
1126+
clusterNodes := c.nsIndex[""]
1127+
clusterNodesByUID := make(map[types.UID][]*Resource, len(clusterNodes))
1128+
for _, node := range clusterNodes {
1129+
clusterNodesByUID[node.Ref.UID] = append(clusterNodesByUID[node.Ref.UID], node)
11491130
}
1131+
1132+
// Check for orphaned resources in the specified namespace
1133+
// All cross-namespace relationships discovered here
1134+
c.processOrphanedResources(orphanedResourceNamespace, keysPerNamespace, clusterNodesByUID, visited, action)
11501135
}
11511136

11521137
// processOrphanedResources processes orphaned resources in a specified namespace that are children of cluster-scoped resources

gitops-engine/pkg/cache/cluster_test.go

Lines changed: 0 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1217,40 +1217,6 @@ func testClusterChild() *rbacv1.ClusterRole {
12171217
}
12181218
}
12191219

1220-
func TestIterateHierarchyV2_ClusterScopedParents(t *testing.T) {
1221-
cluster := newCluster(t, testClusterParent(), testNamespacedChild(), testClusterChild()).WithAPIResources([]kube.APIResourceInfo{{
1222-
GroupKind: schema.GroupKind{Group: "", Kind: "Namespace"},
1223-
GroupVersionResource: schema.GroupVersionResource{Group: "", Version: "v1", Resource: "namespaces"},
1224-
Meta: metav1.APIResource{Namespaced: false},
1225-
}, {
1226-
GroupKind: schema.GroupKind{Group: "rbac.authorization.k8s.io", Kind: "ClusterRole"},
1227-
GroupVersionResource: schema.GroupVersionResource{Group: "rbac.authorization.k8s.io", Version: "v1", Resource: "clusterroles"},
1228-
Meta: metav1.APIResource{Namespaced: false},
1229-
}})
1230-
err := cluster.EnsureSynced()
1231-
require.NoError(t, err)
1232-
1233-
keys := []kube.ResourceKey{}
1234-
// Edge case: cluster-scoped resource and its namespaced children, both referred to in the manifest. They should
1235-
// link up in the graph.
1236-
cluster.IterateHierarchyV2(
1237-
[]kube.ResourceKey{
1238-
kube.GetResourceKey(mustToUnstructured(testClusterParent())),
1239-
kube.GetResourceKey(mustToUnstructured(testNamespacedChild())),
1240-
},
1241-
func(resource *Resource, _ map[kube.ResourceKey]*Resource) bool {
1242-
keys = append(keys, resource.ResourceKey())
1243-
return true
1244-
},
1245-
"", // No orphaned resource namespace
1246-
)
1247-
1248-
assert.ElementsMatch(t, []kube.ResourceKey{
1249-
kube.GetResourceKey(mustToUnstructured(testClusterParent())),
1250-
kube.GetResourceKey(mustToUnstructured(testNamespacedChild())),
1251-
kube.GetResourceKey(mustToUnstructured(testClusterChild())),
1252-
}, keys)
1253-
}
12541220

12551221
func TestIterateHierarchyV2_ClusterScopedParentOnly_NoNamespaceScanning(t *testing.T) {
12561222
// Test that without namespace scanning, only cluster-scoped children are found

0 commit comments

Comments
 (0)