Skip to content

Commit 0c39568

Browse files
VirrageSaaronnw
authored andcommitted
stats: add primary info metric
Signed-off-by: Janusz Marcinkiewicz <januszm@nvidia.com>
1 parent d230097 commit 0c39568

4 files changed

Lines changed: 162 additions & 0 deletions

File tree

ais/proxy.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -211,6 +211,7 @@ func (p *proxy) Run() error {
211211

212212
p.notifs.init(p)
213213
p.ic.init(p)
214+
stats.RegSmapMetrics(p.owner.smap)
214215

215216
p.initRecvHandlers()
216217

ais/target.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -480,6 +480,7 @@ func (t *target) Run() error {
480480
tstats.RegMetrics(t.si)
481481

482482
t.initBackends() // (+ reg backend metrics)
483+
stats.RegSmapMetrics(t.owner.smap)
483484
// end target metrics -----------------------
484485

485486
// probe xattrs

stats/primary.go

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
// Package stats provides methods and functionality to register, track, log,
2+
// and export metrics that, for the most part, include "counter" and "latency" kinds.
3+
/*
4+
* Copyright (c) 2026, NVIDIA CORPORATION. All rights reserved.
5+
*/
6+
package stats
7+
8+
import (
9+
"github.com/NVIDIA/aistore/core/meta"
10+
11+
"github.com/prometheus/client_golang/prometheus"
12+
)
13+
14+
const primaryIDLabel = "primary_id"
15+
16+
type primaryInfo struct {
17+
sowner meta.Sowner
18+
desc *prometheus.Desc
19+
}
20+
21+
func RegSmapMetrics(sowner meta.Sowner) {
22+
promRegistry.MustRegister(newPrimaryInfo(sowner))
23+
}
24+
25+
func newPrimaryInfo(sowner meta.Sowner) *primaryInfo {
26+
return &primaryInfo{
27+
sowner: sowner,
28+
desc: prometheus.NewDesc(
29+
prometheus.BuildFQName("ais", "node", "primary_info"),
30+
"Primary proxy information for this node.",
31+
[]string{primaryIDLabel},
32+
prometheus.Labels{ConstlabNode: staticLabs[ConstlabNode]},
33+
),
34+
}
35+
}
36+
37+
func (p *primaryInfo) Describe(ch chan<- *prometheus.Desc) {
38+
ch <- p.desc
39+
}
40+
41+
func (p *primaryInfo) Collect(ch chan<- prometheus.Metric) {
42+
smap := p.sowner.Get()
43+
if smap == nil || smap.Primary == nil {
44+
return
45+
}
46+
47+
ch <- prometheus.MustNewConstMetric(
48+
p.desc,
49+
prometheus.GaugeValue,
50+
1,
51+
smap.Primary.ID(),
52+
)
53+
}

stats/primary_internal_test.go

Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
// Package stats provides methods and functionality to register, track, log,
2+
// and export metrics that, for the most part, include "counter" and "latency" kinds.
3+
/*
4+
* Copyright (c) 2026, NVIDIA CORPORATION. All rights reserved.
5+
*/
6+
package stats
7+
8+
import (
9+
"testing"
10+
11+
"github.com/NVIDIA/aistore/api/apc"
12+
"github.com/NVIDIA/aistore/core/meta"
13+
14+
"github.com/prometheus/client_golang/prometheus"
15+
)
16+
17+
func TestPrimaryInfoMetricTracksSmapPrimary(t *testing.T) {
18+
prevNodeID, hadNodeID := staticLabs[ConstlabNode]
19+
t.Cleanup(func() {
20+
if hadNodeID {
21+
staticLabs[ConstlabNode] = prevNodeID
22+
} else {
23+
delete(staticLabs, ConstlabNode)
24+
}
25+
})
26+
staticLabs[ConstlabNode] = "node1"
27+
sowner := &testSowner{smap: primarySmap("primary1")}
28+
29+
reg := prometheus.NewRegistry()
30+
reg.MustRegister(newPrimaryInfo(sowner))
31+
32+
val, nodeID, primaryID := gatherPrimaryInfo(t, reg)
33+
if val != 1 {
34+
t.Fatalf("expected primary1 metric value 1, got %v", val)
35+
}
36+
if nodeID != staticLabs[ConstlabNode] {
37+
t.Fatalf("expected node_id label %q, got %q", staticLabs[ConstlabNode], nodeID)
38+
}
39+
if primaryID != "primary1" {
40+
t.Fatalf("expected primary_id label primary1, got %q", primaryID)
41+
}
42+
43+
sowner.smap = primarySmap("primary2")
44+
val, nodeID, primaryID = gatherPrimaryInfo(t, reg)
45+
if val != 1 {
46+
t.Fatalf("expected primary2 metric value 1, got %v", val)
47+
}
48+
if nodeID != staticLabs[ConstlabNode] {
49+
t.Fatalf("expected node_id label %q, got %q", staticLabs[ConstlabNode], nodeID)
50+
}
51+
if primaryID != "primary2" {
52+
t.Fatalf("expected primary_id label primary2, got %q", primaryID)
53+
}
54+
}
55+
56+
func gatherPrimaryInfo(t *testing.T, reg *prometheus.Registry) (val float64, nodeID, primaryID string) {
57+
t.Helper()
58+
59+
families, err := reg.Gather()
60+
if err != nil {
61+
t.Fatal(err)
62+
}
63+
for _, fam := range families {
64+
if fam.GetName() != "ais_node_primary_info" {
65+
continue
66+
}
67+
ms := fam.GetMetric()
68+
if len(ms) != 1 {
69+
t.Fatalf("expected 1 sample, got %d", len(ms))
70+
}
71+
m := ms[0]
72+
g := m.GetGauge()
73+
if g == nil {
74+
t.Fatal("expected gauge metric")
75+
}
76+
for _, lp := range m.GetLabel() {
77+
switch lp.GetName() {
78+
case ConstlabNode:
79+
nodeID = lp.GetValue()
80+
case primaryIDLabel:
81+
primaryID = lp.GetValue()
82+
}
83+
}
84+
if nodeID == "" {
85+
t.Fatal("expected node_id label")
86+
}
87+
if primaryID == "" {
88+
t.Fatal("expected primary_id label")
89+
}
90+
return g.GetValue(), nodeID, primaryID
91+
}
92+
t.Fatal("metric ais_node_primary_info not found")
93+
return 0, "", ""
94+
}
95+
96+
func primarySmap(primaryID string) *meta.Smap {
97+
primary := &meta.Snode{}
98+
primary.Init(primaryID, apc.Proxy)
99+
return &meta.Smap{Primary: primary}
100+
}
101+
102+
type testSowner struct {
103+
smap *meta.Smap
104+
}
105+
106+
func (s *testSowner) Get() *meta.Smap { return s.smap }
107+
func (*testSowner) Listeners() meta.SmapListeners { return nil }

0 commit comments

Comments
 (0)