diff --git a/prometheus/fnv.go b/prometheus/fnv.go deleted file mode 100644 index 3d383a735..000000000 --- a/prometheus/fnv.go +++ /dev/null @@ -1,42 +0,0 @@ -// Copyright 2018 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 prometheus - -// Inline and byte-free variant of hash/fnv's fnv64a. - -const ( - offset64 = 14695981039346656037 - prime64 = 1099511628211 -) - -// hashNew initializies a new fnv64a hash value. -func hashNew() uint64 { - return offset64 -} - -// hashAdd adds a string to a fnv64a hash value, returning the updated hash. -func hashAdd(h uint64, s string) uint64 { - for i := 0; i < len(s); i++ { - h ^= uint64(s[i]) - h *= prime64 - } - return h -} - -// hashAddByte adds a byte to a fnv64a hash value, returning the updated hash. -func hashAddByte(h uint64, b byte) uint64 { - h ^= uint64(b) - h *= prime64 - return h -} diff --git a/prometheus/vec.go b/prometheus/vec.go index 14ed9e856..0f98c29d2 100644 --- a/prometheus/vec.go +++ b/prometheus/vec.go @@ -17,9 +17,22 @@ import ( "fmt" "sync" - "github.com/prometheus/common/model" + "github.com/cespare/xxhash/v2" ) +// hasher represents the partial API from xxhash, to enable replacing for +// collision tests +type hasher interface { + WriteString(string) (int, error) + Write([]byte) (int, error) + Sum64() uint64 +} + +// newHasher wraps xxhash.New to satisfy the hasher interface +func newHasher() hasher { + return xxhash.New() +} + // metricVec is a Collector to bundle metrics of the same name that differ in // their label values. metricVec is not used directly (and therefore // unexported). It is used as a building block for implementations of vectors of @@ -30,9 +43,8 @@ type metricVec struct { curry []curriedLabelValue - // hashAdd and hashAddByte can be replaced for testing collision handling. - hashAdd func(h uint64, s string) uint64 - hashAddByte func(h uint64, b byte) uint64 + // hasher can be replaced for testing collision handling. + newHasher func() hasher } // newMetricVec returns an initialized metricVec. @@ -43,8 +55,7 @@ func newMetricVec(desc *Desc, newMetric func(lvs ...string) Metric) *metricVec { desc: desc, newMetric: newMetric, }, - hashAdd: hashAdd, - hashAddByte: hashAddByte, + newHasher: newHasher, } } @@ -117,10 +128,9 @@ func (m *metricVec) curryWith(labels Labels) (*metricVec, error) { } return &metricVec{ - metricMap: m.metricMap, - curry: newCurry, - hashAdd: m.hashAdd, - hashAddByte: m.hashAddByte, + metricMap: m.metricMap, + curry: newCurry, + newHasher: m.newHasher, }, nil } @@ -148,21 +158,21 @@ func (m *metricVec) hashLabelValues(vals []string) (uint64, error) { } var ( - h = hashNew() + h = m.newHasher() curry = m.curry iVals, iCurry int ) for i := 0; i < len(m.desc.variableLabels); i++ { if iCurry < len(curry) && curry[iCurry].index == i { - h = m.hashAdd(h, curry[iCurry].value) + h.WriteString(curry[iCurry].value) iCurry++ } else { - h = m.hashAdd(h, vals[iVals]) + h.WriteString(vals[iVals]) iVals++ } - h = m.hashAddByte(h, model.SeparatorByte) + h.Write(separatorByteSlice) } - return h, nil + return h.Sum64(), nil } func (m *metricVec) hashLabels(labels Labels) (uint64, error) { @@ -171,7 +181,7 @@ func (m *metricVec) hashLabels(labels Labels) (uint64, error) { } var ( - h = hashNew() + h = m.newHasher() curry = m.curry iCurry int ) @@ -181,17 +191,17 @@ func (m *metricVec) hashLabels(labels Labels) (uint64, error) { if ok { return 0, fmt.Errorf("label name %q is already curried", label) } - h = m.hashAdd(h, curry[iCurry].value) + h.WriteString(curry[iCurry].value) iCurry++ } else { if !ok { return 0, fmt.Errorf("label name %q missing in label map", label) } - h = m.hashAdd(h, val) + h.WriteString(val) } - h = m.hashAddByte(h, model.SeparatorByte) + h.Write(separatorByteSlice) } - return h, nil + return h.Sum64(), nil } // metricWithLabelValues provides the metric and its label values for diff --git a/prometheus/vec_test.go b/prometheus/vec_test.go index bd18a9f4e..7f99dd142 100644 --- a/prometheus/vec_test.go +++ b/prometheus/vec_test.go @@ -20,6 +20,12 @@ import ( dto "github.com/prometheus/client_model/go" ) +type testHasher uint64 + +func (th testHasher) Write(b []byte) (int, error) { return len(b), nil } +func (th testHasher) WriteString(s string) (int, error) { return len(s), nil } +func (th testHasher) Sum64() uint64 { return uint64(th) } + func TestDelete(t *testing.T) { vec := NewGaugeVec( GaugeOpts{ @@ -39,8 +45,7 @@ func TestDeleteWithCollisions(t *testing.T) { }, []string{"l1", "l2"}, ) - vec.hashAdd = func(h uint64, s string) uint64 { return 1 } - vec.hashAddByte = func(h uint64, b byte) uint64 { return 1 } + vec.newHasher = func() hasher { return testHasher(1) } testDelete(t, vec) } @@ -93,8 +98,7 @@ func TestDeleteLabelValuesWithCollisions(t *testing.T) { }, []string{"l1", "l2"}, ) - vec.hashAdd = func(h uint64, s string) uint64 { return 1 } - vec.hashAddByte = func(h uint64, b byte) uint64 { return 1 } + vec.newHasher = func() hasher { return testHasher(1) } testDeleteLabelValues(t, vec) } @@ -144,8 +148,7 @@ func TestMetricVecWithCollisions(t *testing.T) { }, []string{"l1", "l2"}, ) - vec.hashAdd = func(h uint64, s string) uint64 { return 1 } - vec.hashAddByte = func(h uint64, b byte) uint64 { return 1 } + vec.newHasher = func() hasher { return testHasher(1) } testMetricVec(t, vec) } @@ -258,8 +261,7 @@ func TestCurryVecWithCollisions(t *testing.T) { }, []string{"one", "two", "three"}, ) - vec.hashAdd = func(h uint64, s string) uint64 { return 1 } - vec.hashAddByte = func(h uint64, b byte) uint64 { return 1 } + vec.newHasher = func() hasher { return testHasher(1) } testCurryVec(t, vec) }