Skip to content

Commit c4d24af

Browse files
committed
ts: memoize child metric name transformation
Curently the child metric name transformations are being performed every polling cycle. The memoization will ensure every subsequent poll does not need to allocate memory and cpu to transform the names. Epic: CRDB-55079 Release: None
1 parent fdc57b5 commit c4d24af

File tree

2 files changed

+59
-14
lines changed

2 files changed

+59
-14
lines changed

pkg/server/status/recorder.go

Lines changed: 49 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import (
1010
"context"
1111
"encoding/json"
1212
"fmt"
13+
"hash/fnv"
1314
"io"
1415
"math"
1516
"os"
@@ -190,6 +191,10 @@ type MetricsRecorder struct {
190191
// round-trip) that requires a mutex to be safe for concurrent usage. We
191192
// therefore give it its own mutex to avoid blocking other methods.
192193
writeSummaryMu syncutil.Mutex
194+
195+
// childMetricNameCache caches the encoded names for child metrics to avoid
196+
// rebuilding them on every recording. Uses syncutil.Map for lock-free reads.
197+
childMetricNameCache syncutil.Map[uint64, string]
193198
}
194199

195200
// NewMetricsRecorder initializes a new MetricsRecorder object that uses the
@@ -468,7 +473,7 @@ func (mr *MetricsRecorder) GetTimeSeriesData(childMetrics bool) []tspb.TimeSerie
468473
source: mr.mu.desc.NodeID.String(),
469474
timestampNanos: now.UnixNano(),
470475
}
471-
recorder.recordChangefeedChildMetrics(&data)
476+
recorder.recordChangefeedChildMetrics(&data, &mr.childMetricNameCache)
472477

473478
// Record child metrics from app-level registries for secondary tenants
474479
for tenantID, r := range mr.mu.tenantRegistries {
@@ -478,7 +483,7 @@ func (mr *MetricsRecorder) GetTimeSeriesData(childMetrics bool) []tspb.TimeSerie
478483
source: tsutil.MakeTenantSource(mr.mu.desc.NodeID.String(), tenantID.String()),
479484
timestampNanos: now.UnixNano(),
480485
}
481-
tenantRecorder.recordChangefeedChildMetrics(&data)
486+
tenantRecorder.recordChangefeedChildMetrics(&data, &mr.childMetricNameCache)
482487
}
483488

484489
atomic.CompareAndSwapInt64(&mr.lastDataCount, lastDataCount, int64(len(data)))
@@ -949,10 +954,40 @@ func (rr registryRecorder) recordChild(
949954
})
950955
}
951956

957+
func hashLabels(labels []*prometheusgo.LabelPair) uint64 {
958+
h := fnv.New64a()
959+
for _, label := range labels {
960+
h.Write([]byte(label.GetName()))
961+
h.Write([]byte(label.GetValue()))
962+
}
963+
return h.Sum64()
964+
}
965+
966+
// getOrComputeMetricName looks up the encoded metric name in the cache,
967+
// or computes it using the provided computeFn if not found.
968+
func getOrComputeMetricName(
969+
cache *syncutil.Map[uint64, string],
970+
labels []*prometheusgo.LabelPair,
971+
computeFn func() string,
972+
) string {
973+
if cache == nil {
974+
return computeFn()
975+
}
976+
labelHash := hashLabels(labels)
977+
if cached, ok := cache.Load(labelHash); ok {
978+
return *cached
979+
}
980+
name := computeFn()
981+
cache.Store(labelHash, &name)
982+
return name
983+
}
984+
952985
// recordChangefeedChildMetrics iterates through changefeed metrics in the registry and processes child metrics
953986
// for those that have TsdbRecordLabeled set to true in their metadata.
954987
// Records up to 1024 child metrics per metric to prevent unbounded memory usage and performance issues.
955-
func (rr registryRecorder) recordChangefeedChildMetrics(dest *[]tspb.TimeSeriesData) {
988+
func (rr registryRecorder) recordChangefeedChildMetrics(
989+
dest *[]tspb.TimeSeriesData, cache *syncutil.Map[uint64, string],
990+
) {
956991
maxChildMetricsPerMetric := 1024
957992

958993
labels := rr.registry.GetLabels()
@@ -1020,8 +1055,10 @@ func (rr registryRecorder) recordChangefeedChildMetrics(dest *[]tspb.TimeSeriesD
10201055
return
10211056
}
10221057

1023-
baseName := metadata.Name + metric.EncodeLabeledName(&prometheusgo.Metric{Label: childLabels})
1024-
1058+
// Check cache for encoded name
1059+
baseName := getOrComputeMetricName(cache, childLabels, func() string {
1060+
return metadata.Name + metric.EncodeLabeledName(&prometheusgo.Metric{Label: childLabels})
1061+
})
10251062
// Record all histogram computed metrics using child-specific snapshots
10261063
for _, c := range metric.HistogramMetricComputers {
10271064
var value float64
@@ -1063,7 +1100,13 @@ func (rr registryRecorder) recordChangefeedChildMetrics(dest *[]tspb.TimeSeriesD
10631100
} else {
10641101
return
10651102
}
1066-
recordMetric(prom.GetName(false /* useStaticLabels */)+metric.EncodeLabeledName(childMetric), value)
1103+
1104+
// Check cache for encoded name
1105+
metricName := getOrComputeMetricName(cache, childMetric.Label, func() string {
1106+
return prom.GetName(false /* useStaticLabels */) + metric.EncodeLabeledName(childMetric)
1107+
})
1108+
1109+
recordMetric(metricName, value)
10671110
childMetricsCount++
10681111
}
10691112
promIter.Each(m.Label, processChildMetric)

pkg/server/status/recorder_test.go

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -301,6 +301,7 @@ func TestMetricsRecorderLabels(t *testing.T) {
301301
},
302302
Duration: 10 * time.Second,
303303
BucketConfig: metric.IOLatencyBuckets,
304+
Mode: metric.HistogramModePrometheus,
304305
},
305306
"scope",
306307
)
@@ -1004,7 +1005,7 @@ func TestRecordChangefeedChildMetrics(t *testing.T) {
10041005
}
10051006

10061007
var dest []tspb.TimeSeriesData
1007-
recorder.recordChangefeedChildMetrics(&dest)
1008+
recorder.recordChangefeedChildMetrics(&dest, nil)
10081009

10091010
require.Empty(t, dest)
10101011
})
@@ -1029,7 +1030,7 @@ func TestRecordChangefeedChildMetrics(t *testing.T) {
10291030
}
10301031

10311032
var dest []tspb.TimeSeriesData
1032-
recorder.recordChangefeedChildMetrics(&dest)
1033+
recorder.recordChangefeedChildMetrics(&dest, nil)
10331034

10341035
require.Empty(t, dest)
10351036
})
@@ -1061,7 +1062,7 @@ func TestRecordChangefeedChildMetrics(t *testing.T) {
10611062
}
10621063

10631064
var dest []tspb.TimeSeriesData
1064-
recorder.recordChangefeedChildMetrics(&dest)
1065+
recorder.recordChangefeedChildMetrics(&dest, nil)
10651066

10661067
// Should be empty since TsdbRecordLabeled is false
10671068
require.Empty(t, dest)
@@ -1092,7 +1093,7 @@ func TestRecordChangefeedChildMetrics(t *testing.T) {
10921093
}
10931094

10941095
var dest []tspb.TimeSeriesData
1095-
recorder.recordChangefeedChildMetrics(&dest)
1096+
recorder.recordChangefeedChildMetrics(&dest, nil)
10961097

10971098
// Should have data for child metrics (TsdbRecordLabeled is defaulted true and metric is in allowed list)
10981099
require.Len(t, dest, 2)
@@ -1130,7 +1131,7 @@ func TestRecordChangefeedChildMetrics(t *testing.T) {
11301131
}
11311132

11321133
var dest []tspb.TimeSeriesData
1133-
recorder.recordChangefeedChildMetrics(&dest)
1134+
recorder.recordChangefeedChildMetrics(&dest, nil)
11341135

11351136
// Should be limited to 1024 child metrics
11361137
require.Len(t, dest, 1024)
@@ -1157,7 +1158,7 @@ func TestRecordChangefeedChildMetrics(t *testing.T) {
11571158
}
11581159

11591160
var dest []tspb.TimeSeriesData
1160-
recorder.recordChangefeedChildMetrics(&dest)
1161+
recorder.recordChangefeedChildMetrics(&dest, nil)
11611162

11621163
require.Len(t, dest, 1)
11631164

@@ -1196,6 +1197,7 @@ func TestRecordChangefeedChildMetrics(t *testing.T) {
11961197
},
11971198
Duration: 10 * time.Second,
11981199
BucketConfig: metric.IOLatencyBuckets,
1200+
Mode: metric.HistogramModePrometheus,
11991201
},
12001202
"scope",
12011203
)
@@ -1213,7 +1215,7 @@ func TestRecordChangefeedChildMetrics(t *testing.T) {
12131215
}
12141216

12151217
var dest []tspb.TimeSeriesData
1216-
recorder.recordChangefeedChildMetrics(&dest)
1218+
recorder.recordChangefeedChildMetrics(&dest, nil)
12171219

12181220
require.Len(t, dest, 13) // 1 gauge + 1 counter + 11 histogram metrics
12191221

@@ -1323,7 +1325,7 @@ func BenchmarkRecordChangefeedChildMetrics(b *testing.B) {
13231325

13241326
for n := 0; n < b.N; n++ {
13251327
var dest []tspb.TimeSeriesData
1326-
recorder.recordChangefeedChildMetrics(&dest)
1328+
recorder.recordChangefeedChildMetrics(&dest, nil)
13271329
}
13281330
})
13291331
}

0 commit comments

Comments
 (0)