client_golang/model/fingerprinting.go

211 lines
5.2 KiB
Go

// Copyright 2013 Prometheus Team
// 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 model
import (
"encoding/binary"
"fmt"
"hash/fnv"
"sort"
"strconv"
"strings"
)
// Fingerprint provides a hash-capable representation of a Metric.
type Fingerprint struct {
// A hashed representation of the underyling entity. For our purposes, FNV-1A
// 64-bit is used.
Hash uint64
FirstCharacterOfFirstLabelName string
LabelMatterLength uint
LastCharacterOfLastLabelValue string
}
func (f *Fingerprint) String() string {
return strings.Join([]string{fmt.Sprintf("%020d", f.Hash), f.FirstCharacterOfFirstLabelName, fmt.Sprint(f.LabelMatterLength), f.LastCharacterOfLastLabelValue}, "-")
}
// Less compares first the Hash, then the FirstCharacterOfFirstLabelName, then
// the LabelMatterLength, then the LastCharacterOfLastLabelValue.
func (f *Fingerprint) Less(o *Fingerprint) bool {
if f.Hash < o.Hash {
return true
}
if f.Hash > o.Hash {
return false
}
if f.FirstCharacterOfFirstLabelName < o.FirstCharacterOfFirstLabelName {
return true
}
if f.FirstCharacterOfFirstLabelName > o.FirstCharacterOfFirstLabelName {
return false
}
if f.LabelMatterLength < o.LabelMatterLength {
return true
}
if f.LabelMatterLength > o.LabelMatterLength {
return false
}
if f.LastCharacterOfLastLabelValue < o.LastCharacterOfLastLabelValue {
return true
}
if f.LastCharacterOfLastLabelValue > o.LastCharacterOfLastLabelValue {
return false
}
return false
}
// Equal uses the same semantics as Less.
func (f *Fingerprint) Equal(o *Fingerprint) bool {
if f.Hash != o.Hash {
return false
}
if f.FirstCharacterOfFirstLabelName != o.FirstCharacterOfFirstLabelName {
return false
}
if f.LabelMatterLength != o.LabelMatterLength {
return false
}
return f.LastCharacterOfLastLabelValue == o.LastCharacterOfLastLabelValue
}
const rowKeyDelimiter = "-"
// LoadFromString transforms a string representation into a Fingerprint,
// resetting any previous attributes.
func (f *Fingerprint) LoadFromString(s string) {
components := strings.Split(s, rowKeyDelimiter)
hash, err := strconv.ParseUint(components[0], 10, 64)
if err != nil {
panic(err)
}
labelMatterLength, err := strconv.ParseUint(components[2], 10, 0)
if err != nil {
panic(err)
}
f.Hash = hash
f.FirstCharacterOfFirstLabelName = components[1]
f.LabelMatterLength = uint(labelMatterLength)
f.LastCharacterOfLastLabelValue = components[3]
}
const reservedDelimiter = `"`
// LoadFromMetric decomposes a Metric into this Fingerprint
func (f *Fingerprint) LoadFromMetric(m Metric) {
labelLength := len(m)
labelNames := make([]string, 0, labelLength)
for labelName := range m {
labelNames = append(labelNames, string(labelName))
}
sort.Strings(labelNames)
summer := fnv.New64a()
firstCharacterOfFirstLabelName := ""
lastCharacterOfLastLabelValue := ""
labelMatterLength := 0
for i, labelName := range labelNames {
labelValue := m[LabelName(labelName)]
labelNameLength := len(labelName)
labelValueLength := len(labelValue)
labelMatterLength += labelNameLength + labelValueLength
if i == 0 {
firstCharacterOfFirstLabelName = labelName[0:1]
}
if i == labelLength-1 {
lastCharacterOfLastLabelValue = string(labelValue[labelValueLength-1 : labelValueLength])
}
summer.Write([]byte(labelName))
summer.Write([]byte(reservedDelimiter))
summer.Write([]byte(labelValue))
}
f.FirstCharacterOfFirstLabelName = firstCharacterOfFirstLabelName
f.Hash = binary.LittleEndian.Uint64(summer.Sum(nil))
f.LabelMatterLength = uint(labelMatterLength % 10)
f.LastCharacterOfLastLabelValue = lastCharacterOfLastLabelValue
}
// Fingerprints represents a collection of Fingerprint subject to a given
// natural sorting scheme. It implements sort.Interface.
type Fingerprints []*Fingerprint
func (f Fingerprints) Len() int {
return len(f)
}
func (f Fingerprints) Less(i, j int) bool {
return f[i].Less(f[j])
}
func (f Fingerprints) Swap(i, j int) {
f[i], f[j] = f[j], f[i]
}
// FingerprintSet is a set of Fingerprints.
type FingerprintSet map[Fingerprint]struct{}
// Equal returns true if both sets contain the same elements (and not more).
func (s FingerprintSet) Equal(o FingerprintSet) bool {
if len(s) != len(o) {
return false
}
for k := range s {
if _, ok := o[k]; !ok {
return false
}
}
return true
}
// Intersection returns the elements contained in both sets.
func (s FingerprintSet) Intersection(o FingerprintSet) FingerprintSet {
myLength, otherLength := len(s), len(o)
if myLength == 0 || otherLength == 0 {
return FingerprintSet{}
}
subSet := s
superSet := o
if otherLength < myLength {
subSet = o
superSet = s
}
out := FingerprintSet{}
for k := range subSet {
if _, ok := superSet[k]; ok {
out[k] = struct{}{}
}
}
return out
}