Skip to content

Commit cbe3ef2

Browse files
committed
Merge pull request #87 from prometheus/beorn7/fingerprint
Unify fingerprinting aka signature calculation.
2 parents de5f7a2 + af21d45 commit cbe3ef2

File tree

5 files changed

+208
-89
lines changed

5 files changed

+208
-89
lines changed

model/metric.go

Lines changed: 1 addition & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,8 @@
1414
package model
1515

1616
import (
17-
"encoding/binary"
1817
"encoding/json"
1918
"fmt"
20-
"hash/fnv"
2119
"sort"
2220
"strings"
2321
)
@@ -66,37 +64,7 @@ func (m Metric) String() string {
6664

6765
// Fingerprint returns a Metric's Fingerprint.
6866
func (m Metric) Fingerprint() Fingerprint {
69-
labelNames := make([]string, 0, len(m))
70-
maxLength := 0
71-
72-
for labelName, labelValue := range m {
73-
labelNames = append(labelNames, string(labelName))
74-
if len(labelName) > maxLength {
75-
maxLength = len(labelName)
76-
}
77-
if len(labelValue) > maxLength {
78-
maxLength = len(labelValue)
79-
}
80-
}
81-
82-
sort.Strings(labelNames)
83-
84-
summer := fnv.New64a()
85-
buf := make([]byte, maxLength)
86-
87-
for _, labelName := range labelNames {
88-
labelValue := m[LabelName(labelName)]
89-
90-
copy(buf, labelName)
91-
summer.Write(buf[:len(labelName)])
92-
93-
summer.Write(separator)
94-
95-
copy(buf, labelValue)
96-
summer.Write(buf[:len(labelValue)])
97-
}
98-
99-
return Fingerprint(binary.LittleEndian.Uint64(summer.Sum(nil)))
67+
return metricToFingerprint(m)
10068
}
10169

10270
// Clone returns a copy of the Metric.

model/metric_test.go

Lines changed: 5 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -22,39 +22,35 @@ func testMetric(t testing.TB) {
2222
}{
2323
{
2424
input: Metric{},
25-
fingerprint: 2676020557754725067,
25+
fingerprint: 14695981039346656037,
2626
},
2727
{
2828
input: Metric{
2929
"first_name": "electro",
3030
"occupation": "robot",
3131
"manufacturer": "westinghouse",
3232
},
33-
fingerprint: 13260944541294022935,
33+
fingerprint: 11310079640881077873,
3434
},
3535
{
3636
input: Metric{
3737
"x": "y",
3838
},
39-
fingerprint: 1470933794305433534,
39+
fingerprint: 13948396922932177635,
4040
},
41-
// The following two demonstrate a bug in fingerprinting. They
42-
// should not have the same fingerprint with a sane
43-
// fingerprinting function. See
44-
// https://github.com/prometheus/client_golang/issues/74 .
4541
{
4642
input: Metric{
4743
"a": "bb",
4844
"b": "c",
4945
},
50-
fingerprint: 3734646176939799877,
46+
fingerprint: 3198632812309449502,
5147
},
5248
{
5349
input: Metric{
5450
"a": "b",
5551
"bb": "c",
5652
},
57-
fingerprint: 3734646176939799877,
53+
fingerprint: 5774953389407657638,
5854
},
5955
}
6056

model/signature.go

Lines changed: 65 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -63,10 +63,10 @@ func LabelsToSignature(labels map[string]string) uint64 {
6363
hb := getHashAndBuf()
6464
defer putHashAndBuf(hb)
6565

66-
for k, v := range labels {
67-
hb.b.WriteString(k)
66+
for labelName, labelValue := range labels {
67+
hb.b.WriteString(labelName)
6868
hb.b.WriteByte(SeparatorByte)
69-
hb.b.WriteString(v)
69+
hb.b.WriteString(labelValue)
7070
hb.h.Write(hb.b.Bytes())
7171
result ^= hb.h.Sum64()
7272
hb.h.Reset()
@@ -75,23 +75,79 @@ func LabelsToSignature(labels map[string]string) uint64 {
7575
return result
7676
}
7777

78-
// LabelValuesToSignature returns a unique signature (i.e., fingerprint) for the
79-
// values of a given label set.
80-
func LabelValuesToSignature(labels map[string]string) uint64 {
81-
if len(labels) == 0 {
78+
// metricToFingerprint works exactly as LabelsToSignature but takes a Metric as
79+
// parameter (rather than a label map) and returns a Fingerprint.
80+
func metricToFingerprint(m Metric) Fingerprint {
81+
if len(m) == 0 {
82+
return Fingerprint(emptyLabelSignature)
83+
}
84+
85+
var result uint64
86+
hb := getHashAndBuf()
87+
defer putHashAndBuf(hb)
88+
89+
for labelName, labelValue := range m {
90+
hb.b.WriteString(string(labelName))
91+
hb.b.WriteByte(SeparatorByte)
92+
hb.b.WriteString(string(labelValue))
93+
hb.h.Write(hb.b.Bytes())
94+
result ^= hb.h.Sum64()
95+
hb.h.Reset()
96+
hb.b.Reset()
97+
}
98+
return Fingerprint(result)
99+
}
100+
101+
// SignatureForLabels works like LabelsToSignature but takes a Metric as
102+
// parameter (rather than a label map) and only includes the labels with the
103+
// specified LabelNames into the signature calculation.
104+
func SignatureForLabels(m Metric, labels LabelNames) uint64 {
105+
if len(m) == 0 || len(labels) == 0 {
106+
return emptyLabelSignature
107+
}
108+
109+
var result uint64
110+
hb := getHashAndBuf()
111+
defer putHashAndBuf(hb)
112+
113+
for _, label := range labels {
114+
hb.b.WriteString(string(label))
115+
hb.b.WriteByte(SeparatorByte)
116+
hb.b.WriteString(string(m[label]))
117+
hb.h.Write(hb.b.Bytes())
118+
result ^= hb.h.Sum64()
119+
hb.h.Reset()
120+
hb.b.Reset()
121+
}
122+
return result
123+
}
124+
125+
// SignatureWithoutLabels works like LabelsToSignature but takes a Metric as
126+
// parameter (rather than a label map) and excludes the labels with any of the
127+
// specified LabelNames from the signature calculation.
128+
func SignatureWithoutLabels(m Metric, labels map[LabelName]struct{}) uint64 {
129+
if len(m) == 0 {
82130
return emptyLabelSignature
83131
}
84132

85133
var result uint64
86134
hb := getHashAndBuf()
87135
defer putHashAndBuf(hb)
88136

89-
for _, v := range labels {
90-
hb.b.WriteString(v)
137+
for labelName, labelValue := range m {
138+
if _, exclude := labels[labelName]; exclude {
139+
continue
140+
}
141+
hb.b.WriteString(string(labelName))
142+
hb.b.WriteByte(SeparatorByte)
143+
hb.b.WriteString(string(labelValue))
91144
hb.h.Write(hb.b.Bytes())
92145
result ^= hb.h.Sum64()
93146
hb.h.Reset()
94147
hb.b.Reset()
95148
}
149+
if result == 0 {
150+
return emptyLabelSignature
151+
}
96152
return result
97153
}

0 commit comments

Comments
 (0)