From a4387ccdab2e0c6bec64bdd1348adf517310b3ac Mon Sep 17 00:00:00 2001 From: heyitao Date: Fri, 2 Feb 2024 14:17:02 +0800 Subject: [PATCH] fix metric sort error Signed-off-by: heyitao --- prometheus/internal/metric.go | 19 ++++++- prometheus/internal/metric_sort_test.go | 67 +++++++++++++++++++++++++ 2 files changed, 85 insertions(+), 1 deletion(-) create mode 100644 prometheus/internal/metric_sort_test.go diff --git a/prometheus/internal/metric.go b/prometheus/internal/metric.go index 6515c1148..fc9dc32b9 100644 --- a/prometheus/internal/metric.go +++ b/prometheus/internal/metric.go @@ -14,7 +14,9 @@ package internal import ( + "regexp" "sort" + "strconv" dto "github.com/prometheus/client_model/go" ) @@ -46,6 +48,21 @@ func (s MetricSorter) Swap(i, j int) { s[i], s[j] = s[j], s[i] } +func (s MetricSorter) compare(si, sj string) bool { + // When the number of devices exceeds two digits, a sorting error occurs. + // dealing with sorting problems, especially CPU sorting errors + re := regexp.MustCompile(`\d+$`) + if re.Match([]byte(si)) && re.Match([]byte(sj)) { + siCode := re.FindStringSubmatch(si)[0] + sjCode := re.FindStringSubmatch(sj)[0] + siValue, _ := strconv.Atoi(siCode) + sjValue, _ := strconv.Atoi(sjCode) + return siValue < sjValue + } + // Non-numeric identification device, use original sorting + return si < sj +} + func (s MetricSorter) Less(i, j int) bool { if len(s[i].Label) != len(s[j].Label) { // This should not happen. The metrics are @@ -60,7 +77,7 @@ func (s MetricSorter) Less(i, j int) bool { vi := lp.GetValue() vj := s[j].Label[n].GetValue() if vi != vj { - return vi < vj + return s.compare(vi, vj) } } diff --git a/prometheus/internal/metric_sort_test.go b/prometheus/internal/metric_sort_test.go new file mode 100644 index 000000000..1739a8d73 --- /dev/null +++ b/prometheus/internal/metric_sort_test.go @@ -0,0 +1,67 @@ +// Copyright 2022 The Prometheus Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +package internal + +import ( + "sort" + "strings" + "testing" + + "github.com/prometheus/common/expfmt" +) + +func BenchmarkSort(b *testing.B) { + var parser expfmt.TextParser + text := ` + # HELP node_cpu_guest_seconds_total Seconds the CPUs spent in guests (VMs) for each mode. + # TYPE node_cpu_guest_seconds_total counter + node_cpu_guest_seconds_total{cpu="0",mode="nice"} 0 + node_cpu_guest_seconds_total{cpu="0",mode="user"} 0 + node_cpu_guest_seconds_total{cpu="1",mode="nice"} 0 + node_cpu_guest_seconds_total{cpu="1",mode="user"} 0 + node_cpu_guest_seconds_total{cpu="10",mode="nice"} 0 + node_cpu_guest_seconds_total{cpu="10",mode="user"} 0 + node_cpu_guest_seconds_total{cpu="11",mode="nice"} 0 + node_cpu_guest_seconds_total{cpu="11",mode="user"} 0 + node_cpu_guest_seconds_total{cpu="12",mode="nice"} 0 + node_cpu_guest_seconds_total{cpu="12",mode="user"} 0 + node_cpu_guest_seconds_total{cpu="13",mode="nice"} 0 + node_cpu_guest_seconds_total{cpu="13",mode="user"} 0 + node_cpu_guest_seconds_total{cpu="14",mode="nice"} 0 + node_cpu_guest_seconds_total{cpu="14",mode="user"} 0 + node_cpu_guest_seconds_total{cpu="15",mode="nice"} 0 + node_cpu_guest_seconds_total{cpu="15",mode="user"} 0 + node_cpu_guest_seconds_total{cpu="2",mode="nice"} 0 + node_cpu_guest_seconds_total{cpu="2",mode="user"} 0 + node_cpu_guest_seconds_total{cpu="3",mode="nice"} 0 + node_cpu_guest_seconds_total{cpu="3",mode="user"} 0 + node_cpu_guest_seconds_total{cpu="4",mode="nice"} 0 + node_cpu_guest_seconds_total{cpu="4",mode="user"} 0 + node_cpu_guest_seconds_total{cpu="5",mode="nice"} 0 + node_cpu_guest_seconds_total{cpu="5",mode="user"} 0 + node_cpu_guest_seconds_total{cpu="6",mode="nice"} 0 + node_cpu_guest_seconds_total{cpu="6",mode="user"} 0 + node_cpu_guest_seconds_total{cpu="7",mode="nice"} 0 + node_cpu_guest_seconds_total{cpu="7",mode="user"} 0 + node_cpu_guest_seconds_total{cpu="8",mode="nice"} 0 + node_cpu_guest_seconds_total{cpu="8",mode="user"} 0 + node_cpu_guest_seconds_total{cpu="9",mode="nice"} 0 + node_cpu_guest_seconds_total{cpu="9",mode="user"} 0 + ` + notNormalized, _ := parser.TextToMetricFamilies(strings.NewReader(text)) + for i := 0; i < b.N; i++ { + for _, mf := range notNormalized { + sort.Sort(MetricSorter(mf.Metric)) + } + } +}