diff --git a/rules/ast/ast.go b/rules/ast/ast.go index 84c4aa284..0eeb3aae4 100644 --- a/rules/ast/ast.go +++ b/rules/ast/ast.go @@ -17,7 +17,6 @@ import ( "errors" "flag" "fmt" - "hash/fnv" "math" "time" @@ -383,23 +382,6 @@ func (node *ScalarFunctionCall) Eval(timestamp clientmodel.Timestamp) clientmode return node.function.callFn(timestamp, node.args).(clientmodel.SampleValue) } -// hashForLabels returns a hash value taken from the label/value pairs of the -// specified labels in the metric. -func hashForLabels(metric clientmodel.Metric, labels clientmodel.LabelNames) uint64 { - var result uint64 - s := fnv.New64a() - - for _, label := range labels { - s.Write([]byte(label)) - s.Write([]byte{clientmodel.SeparatorByte}) - s.Write([]byte(metric[label])) - result ^= s.Sum64() - s.Reset() - } - - return result -} - // EvalVectorInstant evaluates a VectorNode with an instant query. func EvalVectorInstant(node VectorNode, timestamp clientmodel.Timestamp, storage local.Storage, queryStats *stats.TimerGroup) (Vector, error) { totalEvalTimer := queryStats.GetTimer(stats.TotalEvalTime).Start() @@ -503,7 +485,7 @@ func (node *VectorAggregation) Eval(timestamp clientmodel.Timestamp) Vector { vector := node.vector.Eval(timestamp) result := map[uint64]*groupedAggregation{} for _, sample := range vector { - groupingKey := hashForLabels(sample.Metric.Metric, node.groupBy) + groupingKey := clientmodel.SignatureForLabels(sample.Metric.Metric, node.groupBy) if groupedResult, ok := result[groupingKey]; ok { if node.keepExtraLabels { groupedResult.labels = labelIntersection(groupedResult.labels, sample.Metric) @@ -879,7 +861,7 @@ func (node *VectorArithExpr) evalVectors(timestamp clientmodel.Timestamp, lhs, r metric := node.resultMetric(ls, rs) // Check if the same label set has been added for a many-to-one matching before. if node.matchCardinality == MatchManyToOne || node.matchCardinality == MatchOneToMany { - insHash := hashForLabels(metric.Metric, node.includeLabels) + insHash := clientmodel.SignatureForLabels(metric.Metric, node.includeLabels) if ihs, exists := added[hash]; exists { for _, ih := range ihs { if ih == insHash { @@ -971,7 +953,7 @@ func (node *VectorArithExpr) hashForMetric(metric clientmodel.Metric) uint64 { } } } - return hashForLabels(metric, labels) + return clientmodel.SignatureForLabels(metric, labels) } // Eval implements the MatrixNode interface and returns the value of diff --git a/rules/ast/functions.go b/rules/ast/functions.go index 3a207b976..79908cd5b 100644 --- a/rules/ast/functions.go +++ b/rules/ast/functions.go @@ -504,7 +504,7 @@ func histogramQuantileImpl(timestamp clientmodel.Timestamp, args []Node) interfa q := args[0].(ScalarNode).Eval(timestamp) inVec := args[1].(VectorNode).Eval(timestamp) outVec := Vector{} - fpToMetricWithBuckets := map[clientmodel.Fingerprint]*metricWithBuckets{} + signatureToMetricWithBuckets := map[uint64]*metricWithBuckets{} for _, el := range inVec { upperBound, err := strconv.ParseFloat( string(el.Metric.Metric[clientmodel.BucketLabel]), 64, @@ -514,18 +514,18 @@ func histogramQuantileImpl(timestamp clientmodel.Timestamp, args []Node) interfa // TODO(beorn7): Issue a warning somehow. continue } - fp := bucketFingerprint(el.Metric.Metric) - mb, ok := fpToMetricWithBuckets[fp] + signature := clientmodel.SignatureWithoutLabels(el.Metric.Metric, excludedLabels) + mb, ok := signatureToMetricWithBuckets[signature] if !ok { el.Metric.Delete(clientmodel.BucketLabel) el.Metric.Delete(clientmodel.MetricNameLabel) mb = &metricWithBuckets{el.Metric, nil} - fpToMetricWithBuckets[fp] = mb + signatureToMetricWithBuckets[signature] = mb } mb.buckets = append(mb.buckets, bucket{upperBound, el.Value}) } - for _, mb := range fpToMetricWithBuckets { + for _, mb := range signatureToMetricWithBuckets { outVec = append(outVec, &Sample{ Metric: mb.metric, Value: clientmodel.SampleValue(quantile(q, mb.buckets)), diff --git a/rules/ast/quantile.go b/rules/ast/quantile.go index 0f628153f..bf43c777d 100644 --- a/rules/ast/quantile.go +++ b/rules/ast/quantile.go @@ -14,8 +14,6 @@ package ast import ( - "encoding/binary" - "hash/fnv" "math" "sort" @@ -24,6 +22,13 @@ import ( // Helpers to calculate quantiles. +// excludedLabels are the labels to exclude from signature calculation for +// quantiles. +var excludedLabels = map[clientmodel.LabelName]struct{}{ + clientmodel.MetricNameLabel: struct{}{}, + clientmodel.BucketLabel: struct{}{}, +} + type bucket struct { upperBound float64 count clientmodel.SampleValue @@ -99,46 +104,3 @@ func quantile(q clientmodel.SampleValue, buckets buckets) float64 { } return bucketStart + (bucketEnd-bucketStart)*float64(rank/count) } - -// bucketFingerprint works like the Fingerprint method of Metric, but ignores -// the name and the bucket label. -func bucketFingerprint(m clientmodel.Metric) clientmodel.Fingerprint { - numLabels := 0 - if len(m) > 2 { - numLabels = len(m) - 2 - } - labelNames := make([]string, 0, numLabels) - maxLength := 0 - - for labelName, labelValue := range m { - if labelName == clientmodel.MetricNameLabel || labelName == clientmodel.BucketLabel { - continue - } - labelNames = append(labelNames, string(labelName)) - if len(labelName) > maxLength { - maxLength = len(labelName) - } - if len(labelValue) > maxLength { - maxLength = len(labelValue) - } - } - - sort.Strings(labelNames) - - summer := fnv.New64a() - buf := make([]byte, maxLength) - - for _, labelName := range labelNames { - labelValue := m[clientmodel.LabelName(labelName)] - - copy(buf, labelName) - summer.Write(buf[:len(labelName)]) - summer.Write([]byte{clientmodel.SeparatorByte}) - - copy(buf, labelValue) - summer.Write(buf[:len(labelValue)]) - summer.Write([]byte{clientmodel.SeparatorByte}) - } - - return clientmodel.Fingerprint(binary.LittleEndian.Uint64(summer.Sum(nil))) -}