From 953f9d2da05c3789428beb4d409d17dbda28fee1 Mon Sep 17 00:00:00 2001 From: CMGS Date: Wed, 9 Sep 2026 18:37:33 +0900 Subject: [PATCH] perf: list nodes for the metrics scrape without their resource info The scrape only needs each node's name and pod; the plugins read their own stores in GetNodesMetrics. Listing with resource info asked every plugin for every node on each scrape and discarded the answer, two plugin processes per scrape with the plural verb and one per node per plugin without it. --- cluster/calcium/node.go | 8 +++++--- cluster/calcium/node_test.go | 7 +++++++ metrics/handler.go | 2 +- metrics/handler_test.go | 2 +- types/options.go | 9 +++++---- 5 files changed, 19 insertions(+), 9 deletions(-) diff --git a/cluster/calcium/node.go b/cluster/calcium/node.go index fe9ab7fb..71386f64 100644 --- a/cluster/calcium/node.go +++ b/cluster/calcium/node.go @@ -142,9 +142,11 @@ func (c *Calcium) ListPodNodes(ctx context.Context, opts *types.ListNodesOptions logger.Error(ctx, err) return nil, err } - infos, err := c.rmgr.GetNodesResourceInfo(ctx, utils.Map(nodes, func(node *types.Node) string { return node.Name })) - if err != nil { - logger.Error(ctx, err, "failed to get nodes resource info") + var infos map[string]*types.NodeResourceInfo + if !opts.WithoutResourceInfo { + if infos, err = c.rmgr.GetNodesResourceInfo(ctx, utils.Map(nodes, func(node *types.Node) string { return node.Name })); err != nil { + logger.Error(ctx, err, "failed to get nodes resource info") + } } return perNode(c, nodes, func(node *types.Node, ch chan<- *types.Node) { if info, ok := infos[node.Name]; ok { diff --git a/cluster/calcium/node_test.go b/cluster/calcium/node_test.go index 738b5c65..a1980e09 100644 --- a/cluster/calcium/node_test.go +++ b/cluster/calcium/node_test.go @@ -174,6 +174,13 @@ func TestListPodNodes(t *testing.T) { } assert.Equal(t, map[string]any{name1: 8, name2: nil}, listed) rmgr.AssertExpectations(t) + + opts.WithoutResourceInfo = true + ns, err = c.ListPodNodes(ctx, opts) + assert.NoError(t, err) + for range ns { + } + rmgr.AssertNumberOfCalls(t, "GetNodesResourceInfo", 2) store.AssertExpectations(t) } diff --git a/metrics/handler.go b/metrics/handler.go index 79702060..45dc2916 100644 --- a/metrics/handler.go +++ b/metrics/handler.go @@ -20,7 +20,7 @@ func (m *Metrics) ResourceMiddleware(ctx context.Context, cluster cluster.Cluste refreshed := scrapes.DoChan("refresh", func() (any, error) { refreshCtx, cancel := context.WithTimeout(ctx, m.refreshTimeout) defer cancel() - nodeCh, err := cluster.ListPodNodes(refreshCtx, &types.ListNodesOptions{All: true}) + nodeCh, err := cluster.ListPodNodes(refreshCtx, &types.ListNodesOptions{All: true, WithoutResourceInfo: true}) if err != nil { logger.Error(refreshCtx, err, "failed to list nodes") return nil, err diff --git a/metrics/handler_test.go b/metrics/handler_test.go index 2b00d166..3a65ccb2 100644 --- a/metrics/handler_test.go +++ b/metrics/handler_test.go @@ -20,7 +20,7 @@ import ( func TestResourceMiddlewareRefreshesEveryNodeInOneCall(t *testing.T) { cluster := &clustermocks.Cluster{} - cluster.On("ListPodNodes", mock.Anything, mock.Anything).Return(twoNodes(), nil).Once() + cluster.On("ListPodNodes", mock.Anything, mock.MatchedBy(func(opts *types.ListNodesOptions) bool { return opts.All && opts.WithoutResourceInfo })).Return(twoNodes(), nil).Once() rmgr := &resourcemocks.Manager{} rmgr.On("GetNodesMetrics", mock.Anything, mock.MatchedBy(func(nodes []*types.Node) bool { return len(nodes) == 2 })).Return(nil, nil).Once() diff --git a/types/options.go b/types/options.go index d0e8e72d..6014ba96 100644 --- a/types/options.go +++ b/types/options.go @@ -34,10 +34,11 @@ type ListWorkloadsOptions struct { } type ListNodesOptions struct { - Podname string - Labels map[string]string - All bool - CallInfo bool + Podname string + Labels map[string]string + All bool + CallInfo bool + WithoutResourceInfo bool } type TriOptions int