mirror of https://github.com/prometheus/prometheus
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
207 lines
5.2 KiB
207 lines
5.2 KiB
// 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 ( |
|
"code.google.com/p/goprotobuf/proto" |
|
"encoding/binary" |
|
"fmt" |
|
dto "github.com/prometheus/prometheus/model/generated" |
|
"hash/fnv" |
|
"sort" |
|
"strconv" |
|
"strings" |
|
) |
|
|
|
const ( |
|
// rowKeyDelimiter is used to separate formatted versions of a metric's row |
|
// key. |
|
rowKeyDelimiter = "-" |
|
) |
|
|
|
// Builds a Fingerprint from a row key. |
|
func NewFingerprintFromRowKey(rowKey string) *Fingerprint { |
|
components := strings.Split(rowKey, 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) |
|
} |
|
|
|
return &Fingerprint{ |
|
hash: hash, |
|
firstCharacterOfFirstLabelName: components[1], |
|
labelMatterLength: uint(labelMatterLength), |
|
lastCharacterOfLastLabelValue: components[3], |
|
} |
|
} |
|
|
|
// Builds a Fingerprint from a datastore entry. |
|
func NewFingerprintFromDTO(f *dto.Fingerprint) *Fingerprint { |
|
return NewFingerprintFromRowKey(*f.Signature) |
|
} |
|
|
|
// Decomposes a Metric into a Fingerprint. |
|
func NewFingerprintFromMetric(metric Metric) *Fingerprint { |
|
labelLength := len(metric) |
|
labelNames := make([]string, 0, labelLength) |
|
|
|
for labelName := range metric { |
|
labelNames = append(labelNames, string(labelName)) |
|
} |
|
|
|
sort.Strings(labelNames) |
|
|
|
summer := fnv.New64a() |
|
firstCharacterOfFirstLabelName := "" |
|
lastCharacterOfLastLabelValue := "" |
|
labelMatterLength := 0 |
|
|
|
for i, labelName := range labelNames { |
|
labelValue := metric[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)) |
|
} |
|
|
|
return &Fingerprint{ |
|
firstCharacterOfFirstLabelName: firstCharacterOfFirstLabelName, |
|
hash: binary.LittleEndian.Uint64(summer.Sum(nil)), |
|
labelMatterLength: uint(labelMatterLength % 10), |
|
lastCharacterOfLastLabelValue: lastCharacterOfLastLabelValue, |
|
} |
|
} |
|
|
|
// A simplified representation of an entity. |
|
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 f.ToRowKey() |
|
} |
|
|
|
// Transforms the Fingerprint into a database row key. |
|
func (f *Fingerprint) ToRowKey() string { |
|
return strings.Join([]string{fmt.Sprintf("%020d", f.hash), f.firstCharacterOfFirstLabelName, fmt.Sprint(f.labelMatterLength), f.lastCharacterOfLastLabelValue}, rowKeyDelimiter) |
|
} |
|
|
|
func (f *Fingerprint) ToDTO() *dto.Fingerprint { |
|
return &dto.Fingerprint{ |
|
Signature: proto.String(f.ToRowKey()), |
|
} |
|
} |
|
|
|
func (f *Fingerprint) Hash() uint64 { |
|
return f.hash |
|
} |
|
|
|
func (f *Fingerprint) FirstCharacterOfFirstLabelName() string { |
|
return f.firstCharacterOfFirstLabelName |
|
} |
|
|
|
func (f *Fingerprint) LabelMatterLength() uint { |
|
return f.labelMatterLength |
|
} |
|
|
|
func (f *Fingerprint) LastCharacterOfLastLabelValue() string { |
|
return f.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 |
|
} |
|
|
|
func (f *Fingerprint) Equal(o *Fingerprint) (equal bool) { |
|
equal = f.Hash() == o.Hash() |
|
if !equal { |
|
return |
|
} |
|
|
|
equal = f.FirstCharacterOfFirstLabelName() == o.FirstCharacterOfFirstLabelName() |
|
if !equal { |
|
return |
|
} |
|
|
|
equal = f.LabelMatterLength() == o.LabelMatterLength() |
|
if !equal { |
|
return |
|
} |
|
|
|
equal = f.LastCharacterOfLastLabelValue() == o.LastCharacterOfLastLabelValue() |
|
|
|
return |
|
} |
|
|
|
// Represents a collection of Fingerprint subject to a given natural sorting |
|
// scheme. |
|
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] |
|
}
|
|
|