Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
68 changes: 64 additions & 4 deletions model/metric.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,14 +26,68 @@ var separator = []byte{0}
// a singleton and refers to one and only one stream of samples.
type Metric map[LabelName]LabelValue

// Equal compares the fingerprints of both metrics.
// Equal compares the metrics.
func (m Metric) Equal(o Metric) bool {
return m.Fingerprint().Equal(o.Fingerprint())
if len(m) != len(o) {
return false
}
for ln, lv := range m {
olv, ok := o[ln]
if !ok {
return false
}
if olv != lv {
return false
}
}
return true
}

// Before compares the fingerprints of both metrics.
// Before compares the metrics, using the following criteria:
//
// If m has fewer labels than o, it is before o. If it has more, it is not.
//
// If the number of labels is the same, the superset of all label names is
// sorted alphanumerically. The first differing label pair found in that order
// determines the outcome: If the label does not exist at all in m, then m is
// before o, and vice versa. Otherwise the label value is compared
// alphanumerically.
//
// If m and o are equal, the method returns false.
func (m Metric) Before(o Metric) bool {
return m.Fingerprint().Less(o.Fingerprint())
if len(m) < len(o) {
return true
}
if len(m) > len(o) {
return false
}

lns := make(LabelNames, 0, len(m)+len(o))
for ln := range m {
lns = append(lns, ln)
}
for ln := range o {
lns = append(lns, ln)
}
// It's probably not worth it to de-dup lns.
sort.Sort(lns)
for _, ln := range lns {
mlv, ok := m[ln]
if !ok {
return true
}
olv, ok := o[ln]
if !ok {
return false
}
if mlv < olv {
return true
}
if mlv > olv {
return false
}
}
return false
}

// String implements Stringer.
Expand Down Expand Up @@ -67,6 +121,12 @@ func (m Metric) Fingerprint() Fingerprint {
return metricToFingerprint(m)
}

// Fingerprint returns a Metric's Fingerprint calculated by a faster hashing
// algorithm, which is, however, more susceptible to hash collisions.
func (m Metric) FastFingerprint() Fingerprint {
return metricToFastFingerprint(m)
}

// Clone returns a copy of the Metric.
func (m Metric) Clone() Metric {
clone := Metric{}
Expand Down
25 changes: 17 additions & 8 deletions model/metric_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,47 +17,56 @@ import "testing"

func testMetric(t testing.TB) {
var scenarios = []struct {
input Metric
fingerprint Fingerprint
input Metric
fingerprint Fingerprint
fastFingerprint Fingerprint
}{
{
input: Metric{},
fingerprint: 14695981039346656037,
input: Metric{},
fingerprint: 14695981039346656037,
fastFingerprint: 14695981039346656037,
},
{
input: Metric{
"first_name": "electro",
"occupation": "robot",
"manufacturer": "westinghouse",
},
fingerprint: 11310079640881077873,
fingerprint: 5911716720268894962,
fastFingerprint: 11310079640881077873,
},
{
input: Metric{
"x": "y",
},
fingerprint: 13948396922932177635,
fingerprint: 8241431561484471700,
fastFingerprint: 13948396922932177635,
},
{
input: Metric{
"a": "bb",
"b": "c",
},
fingerprint: 3198632812309449502,
fingerprint: 3016285359649981711,
fastFingerprint: 3198632812309449502,
},
{
input: Metric{
"a": "b",
"bb": "c",
},
fingerprint: 5774953389407657638,
fingerprint: 7122421792099404749,
fastFingerprint: 5774953389407657638,
},
}

for i, scenario := range scenarios {
if scenario.fingerprint != scenario.input.Fingerprint() {
t.Errorf("%d. expected %d, got %d", i, scenario.fingerprint, scenario.input.Fingerprint())
}
if scenario.fastFingerprint != scenario.input.FastFingerprint() {
t.Errorf("%d. expected %d, got %d", i, scenario.fastFingerprint, scenario.input.FastFingerprint())
}
}
}

Expand Down
20 changes: 4 additions & 16 deletions model/sample_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,42 +21,36 @@ import (
func TestSamplesSort(t *testing.T) {
input := Samples{
&Sample{
// Fingerprint: 81f9c9ed24563f8f.
Metric: Metric{
MetricNameLabel: "A",
},
Timestamp: 1,
},
&Sample{
// Fingerprint: 81f9c9ed24563f8f.
Metric: Metric{
MetricNameLabel: "A",
},
Timestamp: 2,
},
&Sample{
// Fingerprint: 1bf6c9ed24543f8f.
Metric: Metric{
MetricNameLabel: "C",
},
Timestamp: 1,
},
&Sample{
// Fingerprint: 1bf6c9ed24543f8f.
Metric: Metric{
MetricNameLabel: "C",
},
Timestamp: 2,
},
&Sample{
// Fingerprint: 68f4c9ed24533f8f.
Metric: Metric{
MetricNameLabel: "B",
},
Timestamp: 1,
},
&Sample{
// Fingerprint: 68f4c9ed24533f8f.
Metric: Metric{
MetricNameLabel: "B",
},
Expand All @@ -66,44 +60,38 @@ func TestSamplesSort(t *testing.T) {

expected := Samples{
&Sample{
// Fingerprint: 1bf6c9ed24543f8f.
Metric: Metric{
MetricNameLabel: "C",
MetricNameLabel: "A",
},
Timestamp: 1,
},
&Sample{
// Fingerprint: 1bf6c9ed24543f8f.
Metric: Metric{
MetricNameLabel: "C",
MetricNameLabel: "A",
},
Timestamp: 2,
},
&Sample{
// Fingerprint: 68f4c9ed24533f8f.
Metric: Metric{
MetricNameLabel: "B",
},
Timestamp: 1,
},
&Sample{
// Fingerprint: 68f4c9ed24533f8f.
Metric: Metric{
MetricNameLabel: "B",
},
Timestamp: 2,
},
&Sample{
// Fingerprint: 81f9c9ed24563f8f.
Metric: Metric{
MetricNameLabel: "A",
MetricNameLabel: "C",
},
Timestamp: 1,
},
&Sample{
// Fingerprint: 81f9c9ed24563f8f.
Metric: Metric{
MetricNameLabel: "A",
MetricNameLabel: "C",
},
Timestamp: 2,
},
Expand Down
Loading