From ad8e8f9f245d87f0e58b0d1fc2124f62310df7a0 Mon Sep 17 00:00:00 2001 From: Fabian Reinartz Date: Mon, 24 Aug 2015 18:26:17 +0200 Subject: [PATCH 1/3] Upgrade common vendoring --- Godeps/Godeps.json | 4 ++-- .../src/github.com/prometheus/common/model/value.go | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/Godeps/Godeps.json b/Godeps/Godeps.json index 2b5748250c..bb4d654fb7 100644 --- a/Godeps/Godeps.json +++ b/Godeps/Godeps.json @@ -60,11 +60,11 @@ }, { "ImportPath": "github.com/prometheus/common/expfmt", - "Rev": "2502df85be1b9482ed669faa6b7cfe7f850eb08e" + "Rev": "675b18b12eb9d452d0377f1688d62e724f15b882" }, { "ImportPath": "github.com/prometheus/common/model", - "Rev": "2502df85be1b9482ed669faa6b7cfe7f850eb08e" + "Rev": "675b18b12eb9d452d0377f1688d62e724f15b882" }, { "ImportPath": "github.com/prometheus/client_model/go", diff --git a/Godeps/_workspace/src/github.com/prometheus/common/model/value.go b/Godeps/_workspace/src/github.com/prometheus/common/model/value.go index a2f85cfcf5..d32c299a7e 100644 --- a/Godeps/_workspace/src/github.com/prometheus/common/model/value.go +++ b/Godeps/_workspace/src/github.com/prometheus/common/model/value.go @@ -243,8 +243,8 @@ func (e ValueType) String() string { // Scalar is a scalar value evaluated at the set timestamp. type Scalar struct { - Value SampleValue `json:"value"` - Timestamp Time `json:"timestamp"` + Value SampleValue + Timestamp Time } func (s *Scalar) String() string { @@ -253,8 +253,8 @@ func (s *Scalar) String() string { // String is a string value evaluated at the set timestamp. type String struct { - Value string `json:"value"` - Timestamp Time `json:"timestamp"` + Value string + Timestamp Time } func (s *String) String() string { From e0615953527b4c4481bd635455ca1524752beffd Mon Sep 17 00:00:00 2001 From: Fabian Reinartz Date: Mon, 24 Aug 2015 18:04:26 +0200 Subject: [PATCH 2/3] Move COWMetric into storage/metric package --- .../prometheus/common/model/labels.go | 14 +- .../prometheus/common/model/metric.go | 39 --- .../prometheus/common/model/metric_test.go | 49 ---- .../prometheus/common/model/signature_test.go | 4 +- .../prometheus/common/model/time.go | 4 +- .../prometheus/common/model/value.go | 99 +++++-- .../prometheus/common/model/value_test.go | 248 ++++++++++++++++++ storage/local/interface.go | 4 +- storage/local/storage.go | 16 +- storage/local/storage_test.go | 4 +- storage/metric/metric.go | 63 +++++ storage/metric/metric_test.go | 70 +++++ 12 files changed, 488 insertions(+), 126 deletions(-) create mode 100644 storage/metric/metric.go create mode 100644 storage/metric/metric_test.go diff --git a/Godeps/_workspace/src/github.com/prometheus/common/model/labels.go b/Godeps/_workspace/src/github.com/prometheus/common/model/labels.go index a73a016c2c..ba694b98ad 100644 --- a/Godeps/_workspace/src/github.com/prometheus/common/model/labels.go +++ b/Godeps/_workspace/src/github.com/prometheus/common/model/labels.go @@ -24,23 +24,23 @@ import ( const ( // ExportedLabelPrefix is the prefix to prepend to the label names present in // exported metrics if a label of the same name is added by the server. - ExportedLabelPrefix LabelName = "exported_" + ExportedLabelPrefix = "exported_" // MetricNameLabel is the label name indicating the metric name of a // timeseries. - MetricNameLabel LabelName = "__name__" + MetricNameLabel = "__name__" // SchemeLabel is the name of the label that holds the scheme on which to // scrape a target. - SchemeLabel LabelName = "__scheme__" + SchemeLabel = "__scheme__" // AddressLabel is the name of the label that holds the address of // a scrape target. - AddressLabel LabelName = "__address__" + AddressLabel = "__address__" // MetricsPathLabel is the name of the label that holds the path on which to // scrape a target. - MetricsPathLabel LabelName = "__metrics_path__" + MetricsPathLabel = "__metrics_path__" // ReservedLabelPrefix is a prefix which is not legal in user-supplied // label names. @@ -63,10 +63,10 @@ const ( // JobLabel is the label name indicating the job from which a timeseries // was scraped. - JobLabel LabelName = "job" + JobLabel = "job" // InstanceLabel is the label name used for the instance label. - InstanceLabel LabelName = "instance" + InstanceLabel = "instance" // BucketLabel is used for the label that defines the upper bound of a // bucket of a histogram ("le" -> "less or equal"). diff --git a/Godeps/_workspace/src/github.com/prometheus/common/model/metric.go b/Godeps/_workspace/src/github.com/prometheus/common/model/metric.go index 34b044386a..25fc3c9425 100644 --- a/Godeps/_workspace/src/github.com/prometheus/common/model/metric.go +++ b/Godeps/_workspace/src/github.com/prometheus/common/model/metric.go @@ -14,7 +14,6 @@ package model import ( - "encoding/json" "fmt" "sort" "strings" @@ -80,41 +79,3 @@ func (m Metric) Fingerprint() Fingerprint { func (m Metric) FastFingerprint() Fingerprint { return LabelSet(m).FastFingerprint() } - -// COWMetric wraps a Metric to enable copy-on-write access patterns. -type COWMetric struct { - Copied bool - Metric Metric -} - -// Set sets a label name in the wrapped Metric to a given value and copies the -// Metric initially, if it is not already a copy. -func (m *COWMetric) Set(ln LabelName, lv LabelValue) { - m.doCOW() - m.Metric[ln] = lv -} - -// Delete deletes a given label name from the wrapped Metric and copies the -// Metric initially, if it is not already a copy. -func (m *COWMetric) Del(ln LabelName) { - m.doCOW() - delete(m.Metric, ln) -} - -// doCOW copies the underlying Metric if it is not already a copy. -func (m *COWMetric) doCOW() { - if !m.Copied { - m.Metric = m.Metric.Clone() - m.Copied = true - } -} - -// String implements fmt.Stringer. -func (m COWMetric) String() string { - return m.Metric.String() -} - -// MarshalJSON implements json.Marshaler. -func (m COWMetric) MarshalJSON() ([]byte, error) { - return json.Marshal(m.Metric) -} diff --git a/Godeps/_workspace/src/github.com/prometheus/common/model/metric_test.go b/Godeps/_workspace/src/github.com/prometheus/common/model/metric_test.go index 29577aca6e..5c7cfceafe 100644 --- a/Godeps/_workspace/src/github.com/prometheus/common/model/metric_test.go +++ b/Godeps/_workspace/src/github.com/prometheus/common/model/metric_test.go @@ -81,52 +81,3 @@ func BenchmarkMetric(b *testing.B) { testMetric(b) } } - -func TestCOWMetric(t *testing.T) { - testMetric := Metric{ - "to_delete": "test1", - "to_change": "test2", - } - - scenarios := []struct { - fn func(*COWMetric) - out Metric - }{ - { - fn: func(cm *COWMetric) { - cm.Del("to_delete") - }, - out: Metric{ - "to_change": "test2", - }, - }, - { - fn: func(cm *COWMetric) { - cm.Set("to_change", "changed") - }, - out: Metric{ - "to_delete": "test1", - "to_change": "changed", - }, - }, - } - - for i, s := range scenarios { - orig := testMetric.Clone() - cm := &COWMetric{ - Metric: orig, - } - - s.fn(cm) - - // Test that the original metric was not modified. - if !orig.Equal(testMetric) { - t.Fatalf("%d. original metric changed; expected %v, got %v", i, testMetric, orig) - } - - // Test that the new metric has the right changes. - if !cm.Metric.Equal(s.out) { - t.Fatalf("%d. copied metric doesn't contain expected changes; expected %v, got %v", i, s.out, cm.Metric) - } - } -} diff --git a/Godeps/_workspace/src/github.com/prometheus/common/model/signature_test.go b/Godeps/_workspace/src/github.com/prometheus/common/model/signature_test.go index e3f1235d28..d9c665f8c7 100644 --- a/Godeps/_workspace/src/github.com/prometheus/common/model/signature_test.go +++ b/Godeps/_workspace/src/github.com/prometheus/common/model/signature_test.go @@ -247,7 +247,7 @@ func BenchmarkMetricToFastFingerprintTriple(b *testing.B) { benchmarkMetricToFastFingerprint(b, LabelSet{"first-label": "first-label-value", "second-label": "second-label-value", "third-label": "third-label-value"}, 15738406913934009676) } -func TestEmptyLabelSignature(t *testing.T) { +func BenchmarkEmptyLabelSignature(b *testing.B) { input := []map[string]string{nil, {}} var ms runtime.MemStats @@ -262,7 +262,7 @@ func TestEmptyLabelSignature(t *testing.T) { runtime.ReadMemStats(&ms) if got := ms.Alloc; alloc != got { - t.Fatal("expected LabelsToSignature with empty labels not to perform allocations") + b.Fatal("expected LabelsToSignature with empty labels not to perform allocations") } } diff --git a/Godeps/_workspace/src/github.com/prometheus/common/model/time.go b/Godeps/_workspace/src/github.com/prometheus/common/model/time.go index 43fd0d8082..ebc8bf6cc8 100644 --- a/Godeps/_workspace/src/github.com/prometheus/common/model/time.go +++ b/Godeps/_workspace/src/github.com/prometheus/common/model/time.go @@ -112,9 +112,7 @@ var dotPrecision = int(math.Log10(float64(second))) // String returns a string representation of the Time. func (t Time) String() string { - s := strconv.FormatInt(int64(t), 10) - i := len(s) - dotPrecision - return s[:i] + "." + s[i:] + return strconv.FormatFloat(float64(t)/float64(second), 'f', -1, 64) } // MarshalJSON implements the json.Marshaler interface. diff --git a/Godeps/_workspace/src/github.com/prometheus/common/model/value.go b/Godeps/_workspace/src/github.com/prometheus/common/model/value.go index d32c299a7e..97c49c990b 100644 --- a/Godeps/_workspace/src/github.com/prometheus/common/model/value.go +++ b/Godeps/_workspace/src/github.com/prometheus/common/model/value.go @@ -67,18 +67,13 @@ func (s SamplePair) MarshalJSON() ([]byte, error) { if err != nil { return nil, err } - return json.Marshal([...]interface{}{t, v}) + return []byte(fmt.Sprintf("[%s,%s]", t, v)), nil } // UnmarshalJSON implements json.Unmarshaler. func (s *SamplePair) UnmarshalJSON(b []byte) error { - if len(b) < 2 || b[0] != '"' || b[len(b)-1] != '"' { - return fmt.Errorf("sample pair must be array") - } - - b = b[1 : len(b)-1] - - return json.Unmarshal(b, [...]json.Unmarshaler{&s.Timestamp, &s.Value}) + v := [...]json.Unmarshaler{&s.Timestamp, &s.Value} + return json.Unmarshal(b, &v) } // Equal returns true if this SamplePair and o have equal Values and equal @@ -87,15 +82,15 @@ func (s *SamplePair) Equal(o *SamplePair) bool { return s == o || (s.Value == o.Value && s.Timestamp.Equal(o.Timestamp)) } -func (s *SamplePair) String() string { +func (s SamplePair) String() string { return fmt.Sprintf("%s @[%s]", s.Value, s.Timestamp) } // Sample is a sample pair associated with a metric. type Sample struct { - Metric Metric - Value SampleValue - Timestamp Time + Metric Metric `json:"metric"` + Value SampleValue `json:"value"` + Timestamp Time `json:"timestamp"` } // Equal compares first the metrics, then the timestamp, then the value. @@ -117,13 +112,53 @@ func (s *Sample) Equal(o *Sample) bool { return true } -func (s *Sample) String() string { +func (s Sample) String() string { return fmt.Sprintf("%s => %s", s.Metric, SamplePair{ Timestamp: s.Timestamp, Value: s.Value, }) } +// MarshalJSON implements json.Marshaler. +func (s Sample) MarshalJSON() ([]byte, error) { + v := struct { + Metric Metric `json:"metric"` + Value SamplePair `json:"value"` + }{ + Metric: s.Metric, + Value: SamplePair{ + Timestamp: s.Timestamp, + Value: s.Value, + }, + } + + return json.Marshal(&v) +} + +// UnmarshalJSON implements json.Unmarshaler. +func (s *Sample) UnmarshalJSON(b []byte) error { + v := struct { + Metric Metric `json:"metric"` + Value SamplePair `json:"value"` + }{ + Metric: s.Metric, + Value: SamplePair{ + Timestamp: s.Timestamp, + Value: s.Value, + }, + } + + if err := json.Unmarshal(b, &v); err != nil { + return err + } + + s.Metric = v.Metric + s.Timestamp = v.Value.Timestamp + s.Value = v.Value.Value + + return nil +} + // Samples is a sortable Sample slice. It implements sort.Interface. type Samples []*Sample @@ -169,7 +204,7 @@ type SampleStream struct { Values []SamplePair `json:"values"` } -func (ss *SampleStream) String() string { +func (ss SampleStream) String() string { vals := make([]string, len(ss.Values)) for i, v := range ss.Values { vals[i] = v.String() @@ -247,10 +282,33 @@ type Scalar struct { Timestamp Time } -func (s *Scalar) String() string { +func (s Scalar) String() string { return fmt.Sprintf("scalar: %v @[%v]", s.Value, s.Timestamp) } +// MarshalJSON implements json.Marshaler. +func (s Scalar) MarshalJSON() ([]byte, error) { + v := strconv.FormatFloat(float64(s.Value), 'f', -1, 64) + return json.Marshal([...]interface{}{s.Timestamp, string(v)}) +} + +// UnmarshalJSON implements json.Unmarshaler. +func (s *Scalar) UnmarshalJSON(b []byte) error { + var f string + v := [...]interface{}{&s.Timestamp, &f} + + if err := json.Unmarshal(b, &v); err != nil { + return err + } + + value, err := strconv.ParseFloat(f, 64) + if err != nil { + return fmt.Errorf("error parsing sample value: %s", err) + } + s.Value = SampleValue(value) + return nil +} + // String is a string value evaluated at the set timestamp. type String struct { Value string @@ -261,6 +319,17 @@ func (s *String) String() string { return s.Value } +// MarshalJSON implements json.Marshaler. +func (s String) MarshalJSON() ([]byte, error) { + return json.Marshal([]interface{}{s.Timestamp, s.Value}) +} + +// UnmarshalJSON implements json.Unmarshaler. +func (s *String) UnmarshalJSON(b []byte) error { + v := [...]interface{}{&s.Timestamp, &s.Value} + return json.Unmarshal(b, &v) +} + // Vector is basically only an alias for Samples, but the // contract is that in a Vector, all Samples have the same timestamp. type Vector []*Sample diff --git a/Godeps/_workspace/src/github.com/prometheus/common/model/value_test.go b/Godeps/_workspace/src/github.com/prometheus/common/model/value_test.go index 9c9b58e4de..2e9c7eb09d 100644 --- a/Godeps/_workspace/src/github.com/prometheus/common/model/value_test.go +++ b/Godeps/_workspace/src/github.com/prometheus/common/model/value_test.go @@ -14,10 +14,258 @@ package model import ( + "encoding/json" + "math" + "reflect" "sort" "testing" ) +func TestSamplePairJSON(t *testing.T) { + input := []struct { + plain string + value SamplePair + }{ + { + plain: `[1234.567,"123.1"]`, + value: SamplePair{ + Value: 123.1, + Timestamp: 1234567, + }, + }, + } + + for _, test := range input { + b, err := json.Marshal(test.value) + if err != nil { + t.Error(err) + continue + } + + if string(b) != test.plain { + t.Errorf("encoding error: expected %q, got %q", test.plain, b) + continue + } + + var sp SamplePair + err = json.Unmarshal(b, &sp) + if err != nil { + t.Error(err) + continue + } + + if sp != test.value { + t.Errorf("decoding error: expected %v, got %v", test.value, sp) + } + } +} + +func TestSampleJSON(t *testing.T) { + input := []struct { + plain string + value Sample + }{ + { + plain: `{"metric":{"__name__":"test_metric"},"value":[1234.567,"123.1"]}`, + value: Sample{ + Metric: Metric{ + MetricNameLabel: "test_metric", + }, + Value: 123.1, + Timestamp: 1234567, + }, + }, + } + + for _, test := range input { + b, err := json.Marshal(test.value) + if err != nil { + t.Error(err) + continue + } + + if string(b) != test.plain { + t.Errorf("encoding error: expected %q, got %q", test.plain, b) + continue + } + + var sv Sample + err = json.Unmarshal(b, &sv) + if err != nil { + t.Error(err) + continue + } + + if !reflect.DeepEqual(sv, test.value) { + t.Errorf("decoding error: expected %v, got %v", test.value, sv) + } + } +} + +func TestVectorJSON(t *testing.T) { + input := []struct { + plain string + value Vector + }{ + { + plain: `[]`, + value: Vector{}, + }, + { + plain: `[{"metric":{"__name__":"test_metric"},"value":[1234.567,"123.1"]}]`, + value: Vector{&Sample{ + Metric: Metric{ + MetricNameLabel: "test_metric", + }, + Value: 123.1, + Timestamp: 1234567, + }}, + }, + { + plain: `[{"metric":{"__name__":"test_metric"},"value":[1234.567,"123.1"]},{"metric":{"foo":"bar"},"value":[1.234,"+Inf"]}]`, + value: Vector{ + &Sample{ + Metric: Metric{ + MetricNameLabel: "test_metric", + }, + Value: 123.1, + Timestamp: 1234567, + }, + &Sample{ + Metric: Metric{ + "foo": "bar", + }, + Value: SampleValue(math.Inf(1)), + Timestamp: 1234, + }, + }, + }, + } + + for _, test := range input { + b, err := json.Marshal(test.value) + if err != nil { + t.Error(err) + continue + } + + if string(b) != test.plain { + t.Errorf("encoding error: expected %q, got %q", test.plain, b) + continue + } + + var vec Vector + err = json.Unmarshal(b, &vec) + if err != nil { + t.Error(err) + continue + } + + if !reflect.DeepEqual(vec, test.value) { + t.Errorf("decoding error: expected %v, got %v", test.value, vec) + } + } +} + +func TestScalarJSON(t *testing.T) { + input := []struct { + plain string + value Scalar + }{ + { + plain: `[123.456,"456"]`, + value: Scalar{ + Timestamp: 123456, + Value: 456, + }, + }, + { + plain: `[123123.456,"+Inf"]`, + value: Scalar{ + Timestamp: 123123456, + Value: SampleValue(math.Inf(1)), + }, + }, + { + plain: `[123123.456,"-Inf"]`, + value: Scalar{ + Timestamp: 123123456, + Value: SampleValue(math.Inf(-1)), + }, + }, + } + + for _, test := range input { + b, err := json.Marshal(test.value) + if err != nil { + t.Error(err) + continue + } + + if string(b) != test.plain { + t.Errorf("encoding error: expected %q, got %q", test.plain, b) + continue + } + + var sv Scalar + err = json.Unmarshal(b, &sv) + if err != nil { + t.Error(err) + continue + } + + if sv != test.value { + t.Errorf("decoding error: expected %v, got %v", test.value, sv) + } + } +} + +func TestStringJSON(t *testing.T) { + input := []struct { + plain string + value String + }{ + { + plain: `[123.456,"test"]`, + value: String{ + Timestamp: 123456, + Value: "test", + }, + }, + { + plain: `[123123.456,"台北"]`, + value: String{ + Timestamp: 123123456, + Value: "台北", + }, + }, + } + + for _, test := range input { + b, err := json.Marshal(test.value) + if err != nil { + t.Error(err) + continue + } + + if string(b) != test.plain { + t.Errorf("encoding error: expected %q, got %q", test.plain, b) + continue + } + + var sv String + err = json.Unmarshal(b, &sv) + if err != nil { + t.Error(err) + continue + } + + if sv != test.value { + t.Errorf("decoding error: expected %v, got %v", test.value, sv) + } + } +} + func TestVectorSort(t *testing.T) { input := Vector{ &Sample{ diff --git a/storage/local/interface.go b/storage/local/interface.go index bf0b356d90..3e77e4d957 100644 --- a/storage/local/interface.go +++ b/storage/local/interface.go @@ -40,7 +40,7 @@ type Storage interface { // MetricsForLabelMatchers returns the metrics from storage that satisfy the given // label matchers. At least one label matcher must be specified that does not // match the empty string. - MetricsForLabelMatchers(...*metric.LabelMatcher) map[model.Fingerprint]model.COWMetric + MetricsForLabelMatchers(...*metric.LabelMatcher) map[model.Fingerprint]metric.Metric // LastSamplePairForFingerprint returns the last sample pair for the // provided fingerprint. If the respective time series does not exist or // has an evicted head chunk, nil is returned. @@ -48,7 +48,7 @@ type Storage interface { // Get all of the label values that are associated with a given label name. LabelValuesForLabelName(model.LabelName) model.LabelValues // Get the metric associated with the provided fingerprint. - MetricForFingerprint(model.Fingerprint) model.COWMetric + MetricForFingerprint(model.Fingerprint) metric.Metric // Construct an iterator for a given fingerprint. // The iterator will never return samples older than retention time, // relative to the time NewIterator was called. diff --git a/storage/local/storage.go b/storage/local/storage.go index 7883641df0..7c1ae97269 100644 --- a/storage/local/storage.go +++ b/storage/local/storage.go @@ -410,7 +410,7 @@ func (s *memorySeriesStorage) fingerprintsForLabelPairs(pairs ...model.LabelPair } // MetricsForLabelMatchers implements Storage. -func (s *memorySeriesStorage) MetricsForLabelMatchers(matchers ...*metric.LabelMatcher) map[model.Fingerprint]model.COWMetric { +func (s *memorySeriesStorage) MetricsForLabelMatchers(matchers ...*metric.LabelMatcher) map[model.Fingerprint]metric.Metric { var ( equals []model.LabelPair filters []*metric.LabelMatcher @@ -462,7 +462,7 @@ func (s *memorySeriesStorage) MetricsForLabelMatchers(matchers ...*metric.LabelM filters = remaining } - result := make(map[model.Fingerprint]model.COWMetric, len(resFPs)) + result := make(map[model.Fingerprint]metric.Metric, len(resFPs)) for fp := range resFPs { result[fp] = s.MetricForFingerprint(fp) } @@ -486,7 +486,7 @@ func (s *memorySeriesStorage) LabelValuesForLabelName(labelName model.LabelName) } // MetricForFingerprint implements Storage. -func (s *memorySeriesStorage) MetricForFingerprint(fp model.Fingerprint) model.COWMetric { +func (s *memorySeriesStorage) MetricForFingerprint(fp model.Fingerprint) metric.Metric { s.fpLocker.Lock(fp) defer s.fpLocker.Unlock(fp) @@ -494,16 +494,18 @@ func (s *memorySeriesStorage) MetricForFingerprint(fp model.Fingerprint) model.C if ok { // Wrap the returned metric in a copy-on-write (COW) metric here because // the caller might mutate it. - return model.COWMetric{ + return metric.Metric{ Metric: series.metric, } } - metric, err := s.persistence.archivedMetric(fp) + met, err := s.persistence.archivedMetric(fp) if err != nil { log.Errorf("Error retrieving archived metric for fingerprint %v: %v", fp, err) } - return model.COWMetric{ - Metric: metric, + + return metric.Metric{ + Metric: met, + Copied: false, } } diff --git a/storage/local/storage_test.go b/storage/local/storage_test.go index f7ed9a9351..a3f11de651 100644 --- a/storage/local/storage_test.go +++ b/storage/local/storage_test.go @@ -276,7 +276,7 @@ func TestFingerprintsForLabels(t *testing.T) { } } -var benchLabelMatchingRes map[model.Fingerprint]model.COWMetric +var benchLabelMatchingRes map[model.Fingerprint]metric.Metric func BenchmarkLabelMatching(b *testing.B) { s, closer := NewTestStorage(b, 1) @@ -359,7 +359,7 @@ func BenchmarkLabelMatching(b *testing.B) { b.ResetTimer() for i := 0; i < b.N; i++ { - benchLabelMatchingRes = map[model.Fingerprint]model.COWMetric{} + benchLabelMatchingRes = map[model.Fingerprint]metric.Metric{} for _, mt := range matcherTests { benchLabelMatchingRes = s.MetricsForLabelMatchers(mt...) } diff --git a/storage/metric/metric.go b/storage/metric/metric.go new file mode 100644 index 0000000000..7328ac7a88 --- /dev/null +++ b/storage/metric/metric.go @@ -0,0 +1,63 @@ +// Copyright 2014 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 metric + +import "github.com/prometheus/common/model" + +// Metric wraps a model.Metric and copies it upon modification if Copied is false. +type Metric struct { + Copied bool + Metric model.Metric +} + +// Set sets a label name in the wrapped Metric to a given value and copies the +// Metric initially, if it is not already a copy. +func (m *Metric) Set(ln model.LabelName, lv model.LabelValue) { + m.Copy() + m.Metric[ln] = lv +} + +// Del deletes a given label name from the wrapped Metric and copies the +// Metric initially, if it is not already a copy. +func (m *Metric) Del(ln model.LabelName) { + m.Copy() + delete(m.Metric, ln) +} + +// Get the value for the given label name. An empty value is returned +// if the label does not exist in the metric. +func (m *Metric) Get(ln model.LabelName) model.LabelValue { + return m.Metric[ln] +} + +// Gets behaves as Get but the returned boolean is false iff the label +// does not exist. +func (m *Metric) Gets(ln model.LabelName) (model.LabelValue, bool) { + lv, ok := m.Metric[ln] + return lv, ok +} + +// Copy the underlying Metric if it is not already a copy. +func (m *Metric) Copy() *Metric { + if !m.Copied { + m.Metric = m.Metric.Clone() + m.Copied = true + } + return m +} + +// String implements fmt.Stringer. +func (m Metric) String() string { + return m.Metric.String() +} diff --git a/storage/metric/metric_test.go b/storage/metric/metric_test.go new file mode 100644 index 0000000000..855bdcb2d7 --- /dev/null +++ b/storage/metric/metric_test.go @@ -0,0 +1,70 @@ +// Copyright 2014 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 metric + +import ( + "testing" + + "github.com/prometheus/common/model" +) + +func TestMetric(t *testing.T) { + testMetric := model.Metric{ + "to_delete": "test1", + "to_change": "test2", + } + + scenarios := []struct { + fn func(*Metric) + out Metric + }{ + { + fn: func(cm *etric) { + cm.Del("to_delete") + }, + out: Metric{ + "to_change": "test2", + }, + }, + { + fn: func(cm *COWMetric) { + cm.Set("to_change", "changed") + }, + out: Metric{ + "to_delete": "test1", + "to_change": "changed", + }, + }, + } + + for i, s := range scenarios { + orig := testMetric.Clone() + cm := &Metric{ + Metric: orig, + Copied: false, + } + + s.fn(cm) + + // Test that the original metric was not modified. + if !orig.Equal(testMetric) { + t.Fatalf("%d. original metric changed; expected %v, got %v", i, testMetric, orig) + } + + // Test that the new metric has the right changes. + if !cm.Metric.Equal(s.out) { + t.Fatalf("%d. copied metric doesn't contain expected changes; expected %v, got %v", i, s.out, cm.Metric) + } + } +} From d6b8da8d433dee6712a10eaaca6f6a453e67e230 Mon Sep 17 00:00:00 2001 From: Fabian Reinartz Date: Mon, 24 Aug 2015 18:04:41 +0200 Subject: [PATCH 3/3] Switch promql types to common/model --- Godeps/Godeps.json | 4 +- .../prometheus/common/model/value.go | 8 +- promql/ast.go | 63 +-- promql/engine.go | 314 +++++++-------- promql/functions.go | 376 +++++++++--------- promql/parse.go | 26 +- promql/printer.go | 41 -- promql/quantile.go | 4 +- promql/test.go | 24 +- rules/alerting.go | 19 +- rules/manager.go | 8 +- rules/recording.go | 24 +- storage/metric/metric_test.go | 10 +- template/template.go | 21 +- web/api/legacy/query.go | 32 +- web/api/v1/api.go | 7 +- web/api/v1/api_test.go | 21 +- web/federate.go | 3 +- 18 files changed, 449 insertions(+), 556 deletions(-) diff --git a/Godeps/Godeps.json b/Godeps/Godeps.json index bb4d654fb7..efae49efff 100644 --- a/Godeps/Godeps.json +++ b/Godeps/Godeps.json @@ -60,11 +60,11 @@ }, { "ImportPath": "github.com/prometheus/common/expfmt", - "Rev": "675b18b12eb9d452d0377f1688d62e724f15b882" + "Rev": "7cd9f43750daf997c60f33f46680780067410f28" }, { "ImportPath": "github.com/prometheus/common/model", - "Rev": "675b18b12eb9d452d0377f1688d62e724f15b882" + "Rev": "7cd9f43750daf997c60f33f46680780067410f28" }, { "ImportPath": "github.com/prometheus/client_model/go", diff --git a/Godeps/_workspace/src/github.com/prometheus/common/model/value.go b/Godeps/_workspace/src/github.com/prometheus/common/model/value.go index 97c49c990b..10ffb0bd61 100644 --- a/Godeps/_workspace/src/github.com/prometheus/common/model/value.go +++ b/Godeps/_workspace/src/github.com/prometheus/common/model/value.go @@ -278,8 +278,8 @@ func (e ValueType) String() string { // Scalar is a scalar value evaluated at the set timestamp. type Scalar struct { - Value SampleValue - Timestamp Time + Value SampleValue `json:"value"` + Timestamp Time `json:"timestamp"` } func (s Scalar) String() string { @@ -311,8 +311,8 @@ func (s *Scalar) UnmarshalJSON(b []byte) error { // String is a string value evaluated at the set timestamp. type String struct { - Value string - Timestamp Time + Value string `json:"value"` + Timestamp Time `json:"timestamp"` } func (s *String) String() string { diff --git a/promql/ast.go b/promql/ast.go index 6ca10ae219..e90a94a06f 100644 --- a/promql/ast.go +++ b/promql/ast.go @@ -14,7 +14,6 @@ package promql import ( - "encoding/json" "fmt" "time" @@ -88,45 +87,13 @@ func (*AlertStmt) stmt() {} func (*EvalStmt) stmt() {} func (*RecordStmt) stmt() {} -// ExprType is the type an evaluated expression returns. -type ExprType int - -const ( - ExprNone ExprType = iota - ExprScalar - ExprVector - ExprMatrix - ExprString -) - -// MarshalJSON implements json.Marshaler. -func (et ExprType) MarshalJSON() ([]byte, error) { - return json.Marshal(et.String()) -} - -func (e ExprType) String() string { - switch e { - case ExprNone: - return "" - case ExprScalar: - return "scalar" - case ExprVector: - return "vector" - case ExprMatrix: - return "matrix" - case ExprString: - return "string" - } - panic("promql.ExprType.String: unhandled expression type") -} - // Expr is a generic interface for all expression types. type Expr interface { Node // Type returns the type the expression evaluates to. It does not perform // in-depth checks as this is done at parsing-time. - Type() ExprType + Type() model.ValueType // expr ensures that no other types accidentally implement the interface. expr() } @@ -167,7 +134,7 @@ type MatrixSelector struct { // The series iterators are populated at query analysis time. iterators map[model.Fingerprint]local.SeriesIterator - metrics map[model.Fingerprint]model.COWMetric + metrics map[model.Fingerprint]metric.Metric } // NumberLiteral represents a number. @@ -201,23 +168,23 @@ type VectorSelector struct { // The series iterators are populated at query analysis time. iterators map[model.Fingerprint]local.SeriesIterator - metrics map[model.Fingerprint]model.COWMetric + metrics map[model.Fingerprint]metric.Metric } -func (e *AggregateExpr) Type() ExprType { return ExprVector } -func (e *Call) Type() ExprType { return e.Func.ReturnType } -func (e *MatrixSelector) Type() ExprType { return ExprMatrix } -func (e *NumberLiteral) Type() ExprType { return ExprScalar } -func (e *ParenExpr) Type() ExprType { return e.Expr.Type() } -func (e *StringLiteral) Type() ExprType { return ExprString } -func (e *UnaryExpr) Type() ExprType { return e.Expr.Type() } -func (e *VectorSelector) Type() ExprType { return ExprVector } +func (e *AggregateExpr) Type() model.ValueType { return model.ValVector } +func (e *Call) Type() model.ValueType { return e.Func.ReturnType } +func (e *MatrixSelector) Type() model.ValueType { return model.ValMatrix } +func (e *NumberLiteral) Type() model.ValueType { return model.ValScalar } +func (e *ParenExpr) Type() model.ValueType { return e.Expr.Type() } +func (e *StringLiteral) Type() model.ValueType { return model.ValString } +func (e *UnaryExpr) Type() model.ValueType { return e.Expr.Type() } +func (e *VectorSelector) Type() model.ValueType { return model.ValVector } -func (e *BinaryExpr) Type() ExprType { - if e.LHS.Type() == ExprScalar && e.RHS.Type() == ExprScalar { - return ExprScalar +func (e *BinaryExpr) Type() model.ValueType { + if e.LHS.Type() == model.ValScalar && e.RHS.Type() == model.ValScalar { + return model.ValScalar } - return ExprVector + return model.ValVector } func (*AggregateExpr) expr() {} diff --git a/promql/engine.go b/promql/engine.go index e4406a08d7..bab6d61616 100644 --- a/promql/engine.go +++ b/promql/engine.go @@ -14,12 +14,10 @@ package promql import ( - "encoding/json" "fmt" "math" "runtime" "sort" - "strconv" "time" "github.com/prometheus/common/model" @@ -31,114 +29,70 @@ import ( "github.com/prometheus/prometheus/util/stats" ) -// SampleStream is a stream of Values belonging to an attached COWMetric. -type SampleStream struct { - Metric model.COWMetric `json:"metric"` - Values []model.SamplePair `json:"values"` +// sampleStream is a stream of Values belonging to an attached COWMetric. +type sampleStream struct { + Metric metric.Metric + Values []model.SamplePair } -// Sample is a single sample belonging to a COWMetric. -type Sample struct { - Metric model.COWMetric `json:"metric"` - Value model.SampleValue `json:"value"` - Timestamp model.Time `json:"timestamp"` +// sample is a single sample belonging to a COWMetric. +type sample struct { + Metric metric.Metric + Value model.SampleValue + Timestamp model.Time } -// MarshalJSON implements json.Marshaler. -func (s *Sample) MarshalJSON() ([]byte, error) { - v := struct { - Metric model.COWMetric `json:"metric"` - Value model.SamplePair `json:"value"` - }{ - Metric: s.Metric, - Value: model.SamplePair{ - Timestamp: s.Timestamp, - Value: s.Value, - }, - } - - return json.Marshal(&v) -} - -// Scalar is a scalar value evaluated at the set timestamp. -type Scalar struct { - Value model.SampleValue `json:"value"` - Timestamp model.Time `json:"timestamp"` -} - -func (s *Scalar) String() string { - return fmt.Sprintf("scalar: %v @[%v]", s.Value, s.Timestamp) -} - -// MarshalJSON implements json.Marshaler. -func (s *Scalar) MarshalJSON() ([]byte, error) { - v := strconv.FormatFloat(float64(s.Value), 'f', -1, 64) - return json.Marshal([]interface{}{s.Timestamp, string(v)}) -} - -// String is a string value evaluated at the set timestamp. -type String struct { - Value string `json:"value"` - Timestamp model.Time `json:"timestamp"` -} - -// MarshalJSON implements json.Marshaler. -func (s *String) MarshalJSON() ([]byte, error) { - return json.Marshal([]interface{}{s.Timestamp, s.Value}) -} - -func (s *String) String() string { - return s.Value -} - -// Vector is basically only an alias for model.Samples, but the +// vector is basically only an alias for model.Samples, but the // contract is that in a Vector, all Samples have the same timestamp. -type Vector []*Sample +type vector []*sample -// Matrix is a slice of SampleStreams that implements sort.Interface and +func (vector) Type() model.ValueType { return model.ValVector } +func (vec vector) String() string { return vec.value().String() } + +func (vec vector) value() model.Vector { + val := make(model.Vector, len(vec)) + for i, s := range vec { + val[i] = &model.Sample{ + Metric: s.Metric.Copy().Metric, + Value: s.Value, + Timestamp: s.Timestamp, + } + } + return val +} + +// matrix is a slice of SampleStreams that implements sort.Interface and // has a String method. -type Matrix []*SampleStream +type matrix []*sampleStream -// Len implements sort.Interface. -func (matrix Matrix) Len() int { - return len(matrix) +func (matrix) Type() model.ValueType { return model.ValMatrix } +func (mat matrix) String() string { return mat.value().String() } + +func (mat matrix) value() model.Matrix { + val := make(model.Matrix, len(mat)) + for i, ss := range mat { + val[i] = &model.SampleStream{ + Metric: ss.Metric.Copy().Metric, + Values: ss.Values, + } + } + return val } -// Less implements sort.Interface. -func (matrix Matrix) Less(i, j int) bool { - return matrix[i].Metric.String() < matrix[j].Metric.String() -} - -// Swap implements sort.Interface. -func (matrix Matrix) Swap(i, j int) { - matrix[i], matrix[j] = matrix[j], matrix[i] -} - -// Value is a generic interface for values resulting from a query evaluation. -type Value interface { - Type() ExprType - String() string -} - -func (Matrix) Type() ExprType { return ExprMatrix } -func (Vector) Type() ExprType { return ExprVector } -func (*Scalar) Type() ExprType { return ExprScalar } -func (*String) Type() ExprType { return ExprString } - // Result holds the resulting value of an execution or an error // if any occurred. type Result struct { Err error - Value Value + Value model.Value } // Vector returns a vector if the result value is one. An error is returned if // the result was an error or the result value is not a vector. -func (r *Result) Vector() (Vector, error) { +func (r *Result) Vector() (model.Vector, error) { if r.Err != nil { return nil, r.Err } - v, ok := r.Value.(Vector) + v, ok := r.Value.(model.Vector) if !ok { return nil, fmt.Errorf("query result is not a vector") } @@ -147,11 +101,11 @@ func (r *Result) Vector() (Vector, error) { // Matrix returns a matrix. An error is returned if // the result was an error or the result value is not a matrix. -func (r *Result) Matrix() (Matrix, error) { +func (r *Result) Matrix() (model.Matrix, error) { if r.Err != nil { return nil, r.Err } - v, ok := r.Value.(Matrix) + v, ok := r.Value.(model.Matrix) if !ok { return nil, fmt.Errorf("query result is not a matrix") } @@ -160,11 +114,11 @@ func (r *Result) Matrix() (Matrix, error) { // Scalar returns a scalar value. An error is returned if // the result was an error or the result value is not a scalar. -func (r *Result) Scalar() (*Scalar, error) { +func (r *Result) Scalar() (*model.Scalar, error) { if r.Err != nil { return nil, r.Err } - v, ok := r.Value.(*Scalar) + v, ok := r.Value.(*model.Scalar) if !ok { return nil, fmt.Errorf("query result is not a scalar") } @@ -326,7 +280,7 @@ func (ng *Engine) NewRangeQuery(qs string, start, end model.Time, interval time. if err != nil { return nil, err } - if expr.Type() != ExprVector && expr.Type() != ExprScalar { + if expr.Type() != model.ValVector && expr.Type() != model.ValScalar { return nil, fmt.Errorf("invalid expression type %q for range query, must be scalar or vector", expr.Type()) } qry := ng.newQuery(expr, start, end, interval) @@ -372,7 +326,7 @@ func (ng *Engine) newTestQuery(f func(context.Context) error) Query { // // At this point per query only one EvalStmt is evaluated. Alert and record // statements are not handled by the Engine. -func (ng *Engine) exec(q *query) (Value, error) { +func (ng *Engine) exec(q *query) (model.Value, error) { ctx, cancel := context.WithTimeout(q.ng.baseCtx, ng.options.Timeout) q.cancel = cancel @@ -409,7 +363,7 @@ func (ng *Engine) exec(q *query) (Value, error) { } // execEvalStmt evaluates the expression of an evaluation statement for the given time range. -func (ng *Engine) execEvalStmt(ctx context.Context, query *query, s *EvalStmt) (Value, error) { +func (ng *Engine) execEvalStmt(ctx context.Context, query *query, s *EvalStmt) (model.Value, error) { prepareTimer := query.stats.GetTimer(stats.TotalQueryPreparationTime).Start() analyzeTimer := query.stats.GetTimer(stats.QueryAnalysisTime).Start() @@ -452,13 +406,22 @@ func (ng *Engine) execEvalStmt(ctx context.Context, query *query, s *EvalStmt) ( return nil, err } + // Turn matrix and vector types with protected metrics into + // model.* types. + switch v := val.(type) { + case vector: + val = v.value() + case matrix: + val = v.value() + } + evalTimer.Stop() return val, nil } numSteps := int(s.End.Sub(s.Start) / s.Interval) // Range evaluation. - sampleStreams := map[model.Fingerprint]*SampleStream{} + sampleStreams := map[model.Fingerprint]*sampleStream{} for ts := s.Start; !ts.After(s.End); ts = ts.Add(s.Interval) { if err := contextDone(ctx, "range evaluation"); err != nil { @@ -475,24 +438,24 @@ func (ng *Engine) execEvalStmt(ctx context.Context, query *query, s *EvalStmt) ( } switch v := val.(type) { - case *Scalar: + case *model.Scalar: // As the expression type does not change we can safely default to 0 // as the fingerprint for scalar expressions. ss := sampleStreams[0] if ss == nil { - ss = &SampleStream{Values: make([]model.SamplePair, 0, numSteps)} + ss = &sampleStream{Values: make([]model.SamplePair, 0, numSteps)} sampleStreams[0] = ss } ss.Values = append(ss.Values, model.SamplePair{ Value: v.Value, Timestamp: v.Timestamp, }) - case Vector: + case vector: for _, sample := range v { fp := sample.Metric.Metric.Fingerprint() ss := sampleStreams[fp] if ss == nil { - ss = &SampleStream{ + ss = &sampleStream{ Metric: sample.Metric, Values: make([]model.SamplePair, 0, numSteps), } @@ -514,9 +477,9 @@ func (ng *Engine) execEvalStmt(ctx context.Context, query *query, s *EvalStmt) ( } appendTimer := query.stats.GetTimer(stats.ResultAppendTime).Start() - matrix := Matrix{} - for _, sampleStream := range sampleStreams { - matrix = append(matrix, sampleStream) + mat := matrix{} + for _, ss := range sampleStreams { + mat = append(mat, ss) } appendTimer.Stop() @@ -524,11 +487,14 @@ func (ng *Engine) execEvalStmt(ctx context.Context, query *query, s *EvalStmt) ( return nil, err } + // Turn matrix type with protected metric into model.Matrix. + resMatrix := mat.value() + sortTimer := query.stats.GetTimer(stats.ResultSortTime).Start() - sort.Sort(matrix) + sort.Sort(resMatrix) sortTimer.Stop() - return matrix, nil + return resMatrix, nil } // An evaluator evaluates given expressions at a fixed timestamp. It is attached to an @@ -568,9 +534,9 @@ func (ev *evaluator) recover(errp *error) { } // evalScalar attempts to evaluate e to a scalar value and errors otherwise. -func (ev *evaluator) evalScalar(e Expr) *Scalar { +func (ev *evaluator) evalScalar(e Expr) *model.Scalar { val := ev.eval(e) - sv, ok := val.(*Scalar) + sv, ok := val.(*model.Scalar) if !ok { ev.errorf("expected scalar but got %s", val.Type()) } @@ -578,9 +544,9 @@ func (ev *evaluator) evalScalar(e Expr) *Scalar { } // evalVector attempts to evaluate e to a vector value and errors otherwise. -func (ev *evaluator) evalVector(e Expr) Vector { +func (ev *evaluator) evalVector(e Expr) vector { val := ev.eval(e) - vec, ok := val.(Vector) + vec, ok := val.(vector) if !ok { ev.errorf("expected vector but got %s", val.Type()) } @@ -600,9 +566,9 @@ func (ev *evaluator) evalFloat(e Expr) float64 { } // evalMatrix attempts to evaluate e into a matrix and errors otherwise. -func (ev *evaluator) evalMatrix(e Expr) Matrix { +func (ev *evaluator) evalMatrix(e Expr) matrix { val := ev.eval(e) - mat, ok := val.(Matrix) + mat, ok := val.(matrix) if !ok { ev.errorf("expected matrix but got %s", val.Type()) } @@ -610,7 +576,7 @@ func (ev *evaluator) evalMatrix(e Expr) Matrix { } // evalMatrixBounds attempts to evaluate e to matrix boundaries and errors otherwise. -func (ev *evaluator) evalMatrixBounds(e Expr) Matrix { +func (ev *evaluator) evalMatrixBounds(e Expr) matrix { ms, ok := e.(*MatrixSelector) if !ok { ev.errorf("matrix bounds can only be evaluated for matrix selectors, got %T", e) @@ -619,9 +585,9 @@ func (ev *evaluator) evalMatrixBounds(e Expr) Matrix { } // evalString attempts to evaluate e to a string value and errors otherwise. -func (ev *evaluator) evalString(e Expr) *String { +func (ev *evaluator) evalString(e Expr) *model.String { val := ev.eval(e) - sv, ok := val.(*String) + sv, ok := val.(*model.String) if !ok { ev.errorf("expected string but got %s", val.Type()) } @@ -629,7 +595,7 @@ func (ev *evaluator) evalString(e Expr) *String { } // evalOneOf evaluates e and errors unless the result is of one of the given types. -func (ev *evaluator) evalOneOf(e Expr, t1, t2 ExprType) Value { +func (ev *evaluator) evalOneOf(e Expr, t1, t2 model.ValueType) model.Value { val := ev.eval(e) if val.Type() != t1 && val.Type() != t2 { ev.errorf("expected %s or %s but got %s", t1, t2, val.Type()) @@ -637,15 +603,15 @@ func (ev *evaluator) evalOneOf(e Expr, t1, t2 ExprType) Value { return val } -func (ev *evaluator) Eval(expr Expr) (v Value, err error) { +func (ev *evaluator) Eval(expr Expr) (v model.Value, err error) { defer ev.recover(&err) return ev.eval(expr), nil } // eval evaluates the given expression as the given AST expression node requires. -func (ev *evaluator) eval(expr Expr) Value { +func (ev *evaluator) eval(expr Expr) model.Value { // This is the top-level evaluation method. - // Thus, we check for timeout/cancellation here. + // Thus, we check for timeout/cancelation here. if err := contextDone(ev.ctx, "expression evaluation"); err != nil { ev.error(err) } @@ -656,30 +622,30 @@ func (ev *evaluator) eval(expr Expr) Value { return ev.aggregation(e.Op, e.Grouping, e.KeepExtraLabels, vector) case *BinaryExpr: - lhs := ev.evalOneOf(e.LHS, ExprScalar, ExprVector) - rhs := ev.evalOneOf(e.RHS, ExprScalar, ExprVector) + lhs := ev.evalOneOf(e.LHS, model.ValScalar, model.ValVector) + rhs := ev.evalOneOf(e.RHS, model.ValScalar, model.ValVector) switch lt, rt := lhs.Type(), rhs.Type(); { - case lt == ExprScalar && rt == ExprScalar: - return &Scalar{ - Value: scalarBinop(e.Op, lhs.(*Scalar).Value, rhs.(*Scalar).Value), + case lt == model.ValScalar && rt == model.ValScalar: + return &model.Scalar{ + Value: scalarBinop(e.Op, lhs.(*model.Scalar).Value, rhs.(*model.Scalar).Value), Timestamp: ev.Timestamp, } - case lt == ExprVector && rt == ExprVector: + case lt == model.ValVector && rt == model.ValVector: switch e.Op { case itemLAND: - return ev.vectorAnd(lhs.(Vector), rhs.(Vector), e.VectorMatching) + return ev.vectorAnd(lhs.(vector), rhs.(vector), e.VectorMatching) case itemLOR: - return ev.vectorOr(lhs.(Vector), rhs.(Vector), e.VectorMatching) + return ev.vectorOr(lhs.(vector), rhs.(vector), e.VectorMatching) default: - return ev.vectorBinop(e.Op, lhs.(Vector), rhs.(Vector), e.VectorMatching) + return ev.vectorBinop(e.Op, lhs.(vector), rhs.(vector), e.VectorMatching) } - case lt == ExprVector && rt == ExprScalar: - return ev.vectorScalarBinop(e.Op, lhs.(Vector), rhs.(*Scalar), false) + case lt == model.ValVector && rt == model.ValScalar: + return ev.vectorScalarBinop(e.Op, lhs.(vector), rhs.(*model.Scalar), false) - case lt == ExprScalar && rt == ExprVector: - return ev.vectorScalarBinop(e.Op, rhs.(Vector), lhs.(*Scalar), true) + case lt == model.ValScalar && rt == model.ValVector: + return ev.vectorScalarBinop(e.Op, rhs.(vector), lhs.(*model.Scalar), true) } case *Call: @@ -689,22 +655,22 @@ func (ev *evaluator) eval(expr Expr) Value { return ev.matrixSelector(e) case *NumberLiteral: - return &Scalar{Value: e.Val, Timestamp: ev.Timestamp} + return &model.Scalar{Value: e.Val, Timestamp: ev.Timestamp} case *ParenExpr: return ev.eval(e.Expr) case *StringLiteral: - return &String{Value: e.Val, Timestamp: ev.Timestamp} + return &model.String{Value: e.Val, Timestamp: ev.Timestamp} case *UnaryExpr: - se := ev.evalOneOf(e.Expr, ExprScalar, ExprVector) + se := ev.evalOneOf(e.Expr, model.ValScalar, model.ValVector) // Only + and - are possible operators. if e.Op == itemSUB { switch v := se.(type) { - case *Scalar: + case *model.Scalar: v.Value = -v.Value - case Vector: + case vector: for i, sv := range v { v[i].Value = -sv.Value } @@ -719,13 +685,13 @@ func (ev *evaluator) eval(expr Expr) Value { } // vectorSelector evaluates a *VectorSelector expression. -func (ev *evaluator) vectorSelector(node *VectorSelector) Vector { - vec := Vector{} +func (ev *evaluator) vectorSelector(node *VectorSelector) vector { + vec := vector{} for fp, it := range node.iterators { sampleCandidates := it.ValueAtTime(ev.Timestamp.Add(-node.Offset)) samplePair := chooseClosestSample(sampleCandidates, ev.Timestamp.Add(-node.Offset)) if samplePair != nil { - vec = append(vec, &Sample{ + vec = append(vec, &sample{ Metric: node.metrics[fp], Value: samplePair.Value, Timestamp: ev.Timestamp, @@ -736,13 +702,13 @@ func (ev *evaluator) vectorSelector(node *VectorSelector) Vector { } // matrixSelector evaluates a *MatrixSelector expression. -func (ev *evaluator) matrixSelector(node *MatrixSelector) Matrix { +func (ev *evaluator) matrixSelector(node *MatrixSelector) matrix { interval := metric.Interval{ OldestInclusive: ev.Timestamp.Add(-node.Range - node.Offset), NewestInclusive: ev.Timestamp.Add(-node.Offset), } - sampleStreams := make([]*SampleStream, 0, len(node.iterators)) + sampleStreams := make([]*sampleStream, 0, len(node.iterators)) for fp, it := range node.iterators { samplePairs := it.RangeValues(interval) if len(samplePairs) == 0 { @@ -755,46 +721,46 @@ func (ev *evaluator) matrixSelector(node *MatrixSelector) Matrix { } } - sampleStream := &SampleStream{ + sampleStream := &sampleStream{ Metric: node.metrics[fp], Values: samplePairs, } sampleStreams = append(sampleStreams, sampleStream) } - return Matrix(sampleStreams) + return matrix(sampleStreams) } // matrixSelectorBounds evaluates the boundaries of a *MatrixSelector. -func (ev *evaluator) matrixSelectorBounds(node *MatrixSelector) Matrix { +func (ev *evaluator) matrixSelectorBounds(node *MatrixSelector) matrix { interval := metric.Interval{ OldestInclusive: ev.Timestamp.Add(-node.Range - node.Offset), NewestInclusive: ev.Timestamp.Add(-node.Offset), } - sampleStreams := make([]*SampleStream, 0, len(node.iterators)) + sampleStreams := make([]*sampleStream, 0, len(node.iterators)) for fp, it := range node.iterators { samplePairs := it.BoundaryValues(interval) if len(samplePairs) == 0 { continue } - sampleStream := &SampleStream{ + ss := &sampleStream{ Metric: node.metrics[fp], Values: samplePairs, } - sampleStreams = append(sampleStreams, sampleStream) + sampleStreams = append(sampleStreams, ss) } - return Matrix(sampleStreams) + return matrix(sampleStreams) } -func (ev *evaluator) vectorAnd(lhs, rhs Vector, matching *VectorMatching) Vector { +func (ev *evaluator) vectorAnd(lhs, rhs vector, matching *VectorMatching) vector { if matching.Card != CardManyToMany { panic("logical operations must always be many-to-many matching") } // If no matching labels are specified, match by all labels. sigf := signatureFunc(matching.On...) - var result Vector + var result vector // The set of signatures for the right-hand side vector. rightSigs := map[uint64]struct{}{} // Add all rhs samples to a map so we can easily find matches later. @@ -811,13 +777,13 @@ func (ev *evaluator) vectorAnd(lhs, rhs Vector, matching *VectorMatching) Vector return result } -func (ev *evaluator) vectorOr(lhs, rhs Vector, matching *VectorMatching) Vector { +func (ev *evaluator) vectorOr(lhs, rhs vector, matching *VectorMatching) vector { if matching.Card != CardManyToMany { panic("logical operations must always be many-to-many matching") } sigf := signatureFunc(matching.On...) - var result Vector + var result vector leftSigs := map[uint64]struct{}{} // Add everything from the left-hand-side vector. for _, ls := range lhs { @@ -834,12 +800,12 @@ func (ev *evaluator) vectorOr(lhs, rhs Vector, matching *VectorMatching) Vector } // vectorBinop evaluates a binary operation between two vector, excluding AND and OR. -func (ev *evaluator) vectorBinop(op itemType, lhs, rhs Vector, matching *VectorMatching) Vector { +func (ev *evaluator) vectorBinop(op itemType, lhs, rhs vector, matching *VectorMatching) vector { if matching.Card == CardManyToMany { panic("many-to-many only allowed for AND and OR") } var ( - result = Vector{} + result = vector{} sigf = signatureFunc(matching.On...) resultLabels = append(matching.On, matching.Include...) ) @@ -852,7 +818,7 @@ func (ev *evaluator) vectorBinop(op itemType, lhs, rhs Vector, matching *VectorM } // All samples from the rhs hashed by the matching label/values. - rightSigs := map[uint64]*Sample{} + rightSigs := map[uint64]*sample{} // Add all rhs samples to a map so we can easily find matches later. for _, rs := range rhs { @@ -911,7 +877,7 @@ func (ev *evaluator) vectorBinop(op itemType, lhs, rhs Vector, matching *VectorM insertedSigs[insertSig] = struct{}{} } - result = append(result, &Sample{ + result = append(result, &sample{ Metric: metric, Value: value, Timestamp: ev.Timestamp, @@ -922,21 +888,21 @@ func (ev *evaluator) vectorBinop(op itemType, lhs, rhs Vector, matching *VectorM // signatureFunc returns a function that calculates the signature for a metric // based on the provided labels. -func signatureFunc(labels ...model.LabelName) func(m model.COWMetric) uint64 { +func signatureFunc(labels ...model.LabelName) func(m metric.Metric) uint64 { if len(labels) == 0 { - return func(m model.COWMetric) uint64 { + return func(m metric.Metric) uint64 { m.Del(model.MetricNameLabel) return uint64(m.Metric.Fingerprint()) } } - return func(m model.COWMetric) uint64 { + return func(m metric.Metric) uint64 { return model.SignatureForLabels(m.Metric, labels...) } } // resultMetric returns the metric for the given sample(s) based on the vector // binary operation and the matching options. -func resultMetric(met model.COWMetric, op itemType, labels ...model.LabelName) model.COWMetric { +func resultMetric(met metric.Metric, op itemType, labels ...model.LabelName) metric.Metric { if len(labels) == 0 { if shouldDropMetricName(op) { met.Del(model.MetricNameLabel) @@ -951,12 +917,12 @@ func resultMetric(met model.COWMetric, op itemType, labels ...model.LabelName) m m[ln] = v } } - return model.COWMetric{Metric: m, Copied: false} + return metric.Metric{Metric: m, Copied: false} } // vectorScalarBinop evaluates a binary operation between a vector and a scalar. -func (ev *evaluator) vectorScalarBinop(op itemType, lhs Vector, rhs *Scalar, swap bool) Vector { - vector := make(Vector, 0, len(lhs)) +func (ev *evaluator) vectorScalarBinop(op itemType, lhs vector, rhs *model.Scalar, swap bool) vector { + vec := make(vector, 0, len(lhs)) for _, lhsSample := range lhs { lv, rv := lhsSample.Value, rhs.Value @@ -971,10 +937,10 @@ func (ev *evaluator) vectorScalarBinop(op itemType, lhs Vector, rhs *Scalar, swa if shouldDropMetricName(op) { lhsSample.Metric.Del(model.MetricNameLabel) } - vector = append(vector, lhsSample) + vec = append(vec, lhsSample) } } - return vector + return vec } // scalarBinop evaluates a binary operation between two scalars. @@ -1042,7 +1008,7 @@ func vectorElemBinop(op itemType, lhs, rhs model.SampleValue) (model.SampleValue } // labelIntersection returns the metric of common label/value pairs of two input metrics. -func labelIntersection(metric1, metric2 model.COWMetric) model.COWMetric { +func labelIntersection(metric1, metric2 metric.Metric) metric.Metric { for label, value := range metric1.Metric { if metric2.Metric[label] != value { metric1.Del(label) @@ -1052,29 +1018,29 @@ func labelIntersection(metric1, metric2 model.COWMetric) model.COWMetric { } type groupedAggregation struct { - labels model.COWMetric + labels metric.Metric value model.SampleValue valuesSquaredSum model.SampleValue groupCount int } // aggregation evaluates an aggregation operation on a vector. -func (ev *evaluator) aggregation(op itemType, grouping model.LabelNames, keepExtra bool, vector Vector) Vector { +func (ev *evaluator) aggregation(op itemType, grouping model.LabelNames, keepExtra bool, vec vector) vector { result := map[uint64]*groupedAggregation{} - for _, sample := range vector { + for _, sample := range vec { groupingKey := model.SignatureForLabels(sample.Metric.Metric, grouping...) groupedResult, ok := result[groupingKey] // Add a new group if it doesn't exist. if !ok { - var m model.COWMetric + var m metric.Metric if keepExtra { m = sample.Metric m.Del(model.MetricNameLabel) } else { - m = model.COWMetric{ + m = metric.Metric{ Metric: model.Metric{}, Copied: true, } @@ -1123,7 +1089,7 @@ func (ev *evaluator) aggregation(op itemType, grouping model.LabelNames, keepExt } // Construct the result vector from the aggregated groups. - resultVector := make(Vector, 0, len(result)) + resultVector := make(vector, 0, len(result)) for _, aggr := range result { switch op { @@ -1140,7 +1106,7 @@ func (ev *evaluator) aggregation(op itemType, grouping model.LabelNames, keepExt default: // For other aggregations, we already have the right value. } - sample := &Sample{ + sample := &sample{ Metric: aggr.labels, Value: aggr.value, Timestamp: ev.Timestamp, diff --git a/promql/functions.go b/promql/functions.go index a74538c994..cb6135e0e5 100644 --- a/promql/functions.go +++ b/promql/functions.go @@ -30,29 +30,29 @@ import ( // used by function nodes. type Function struct { Name string - ArgTypes []ExprType + ArgTypes []model.ValueType OptionalArgs int - ReturnType ExprType - Call func(ev *evaluator, args Expressions) Value + ReturnType model.ValueType + Call func(ev *evaluator, args Expressions) model.Value } // === time() model.SampleValue === -func funcTime(ev *evaluator, args Expressions) Value { - return &Scalar{ +func funcTime(ev *evaluator, args Expressions) model.Value { + return &model.Scalar{ Value: model.SampleValue(ev.Timestamp.Unix()), Timestamp: ev.Timestamp, } } -// === delta(matrix ExprMatrix, isCounter=0 ExprScalar) Vector === -func funcDelta(ev *evaluator, args Expressions) Value { +// === delta(matrix model.ValMatrix, isCounter=0 model.ValScalar) Vector === +func funcDelta(ev *evaluator, args Expressions) model.Value { isCounter := len(args) >= 2 && ev.evalInt(args[1]) > 0 - resultVector := Vector{} + resultVector := vector{} // If we treat these metrics as counters, we need to fetch all values // in the interval to find breaks in the timeseries' monotonicity. // I.e. if a counter resets, we want to ignore that reset. - var matrixValue Matrix + var matrixValue matrix if isCounter { matrixValue = ev.evalMatrix(args[0]) } else { @@ -65,8 +65,10 @@ func funcDelta(ev *evaluator, args Expressions) Value { continue } - counterCorrection := model.SampleValue(0) - lastValue := model.SampleValue(0) + var ( + counterCorrection model.SampleValue + lastValue model.SampleValue + ) for _, sample := range samples.Values { currentValue := sample.Value if isCounter && currentValue < lastValue { @@ -93,7 +95,7 @@ func funcDelta(ev *evaluator, args Expressions) Value { intervalCorrection := model.SampleValue(targetInterval) / model.SampleValue(sampledInterval) resultValue *= intervalCorrection - resultSample := &Sample{ + resultSample := &sample{ Metric: samples.Metric, Value: resultValue, Timestamp: ev.Timestamp, @@ -104,12 +106,12 @@ func funcDelta(ev *evaluator, args Expressions) Value { return resultVector } -// === rate(node ExprMatrix) Vector === -func funcRate(ev *evaluator, args Expressions) Value { +// === rate(node model.ValMatrix) Vector === +func funcRate(ev *evaluator, args Expressions) model.Value { args = append(args, &NumberLiteral{1}) - vector := funcDelta(ev, args).(Vector) + vector := funcDelta(ev, args).(vector) - // TODO: could be other type of ExprMatrix in the future (right now, only + // TODO: could be other type of model.ValMatrix in the future (right now, only // MatrixSelector exists). Find a better way of getting the duration of a // matrix, such as looking at the samples themselves. interval := args[0].(*MatrixSelector).Range @@ -119,38 +121,38 @@ func funcRate(ev *evaluator, args Expressions) Value { return vector } -// === increase(node ExprMatrix) Vector === -func funcIncrease(ev *evaluator, args Expressions) Value { +// === increase(node model.ValMatrix) Vector === +func funcIncrease(ev *evaluator, args Expressions) model.Value { args = append(args, &NumberLiteral{1}) - vector := funcDelta(ev, args).(Vector) - return vector + return funcDelta(ev, args).(vector) } -// === sort(node ExprVector) Vector === -func funcSort(ev *evaluator, args Expressions) Value { +// === sort(node model.ValVector) Vector === +func funcSort(ev *evaluator, args Expressions) model.Value { byValueSorter := vectorByValueHeap(ev.evalVector(args[0])) sort.Sort(byValueSorter) - return Vector(byValueSorter) + return vector(byValueSorter) } -// === sortDesc(node ExprVector) Vector === -func funcSortDesc(ev *evaluator, args Expressions) Value { +// === sortDesc(node model.ValVector) Vector === +func funcSortDesc(ev *evaluator, args Expressions) model.Value { byValueSorter := vectorByValueHeap(ev.evalVector(args[0])) sort.Sort(sort.Reverse(byValueSorter)) - return Vector(byValueSorter) + + return vector(byValueSorter) } -// === topk(k ExprScalar, node ExprVector) Vector === -func funcTopk(ev *evaluator, args Expressions) Value { +// === topk(k model.ValScalar, node model.ValVector) Vector === +func funcTopk(ev *evaluator, args Expressions) model.Value { k := ev.evalInt(args[0]) if k < 1 { - return Vector{} + return vector{} } - vector := ev.evalVector(args[1]) + vec := ev.evalVector(args[1]) topk := make(vectorByValueHeap, 0, k) - for _, el := range vector { + for _, el := range vec { if len(topk) < k || topk[0].Value < el.Value { if len(topk) == k { heap.Pop(&topk) @@ -159,21 +161,21 @@ func funcTopk(ev *evaluator, args Expressions) Value { } } sort.Sort(sort.Reverse(topk)) - return Vector(topk) + return vector(topk) } -// === bottomk(k ExprScalar, node ExprVector) Vector === -func funcBottomk(ev *evaluator, args Expressions) Value { +// === bottomk(k model.ValScalar, node model.ValVector) Vector === +func funcBottomk(ev *evaluator, args Expressions) model.Value { k := ev.evalInt(args[0]) if k < 1 { - return Vector{} + return vector{} } - vector := ev.evalVector(args[1]) + vec := ev.evalVector(args[1]) bottomk := make(vectorByValueHeap, 0, k) bkHeap := reverseHeap{Interface: &bottomk} - for _, el := range vector { + for _, el := range vec { if len(bottomk) < k || bottomk[0].Value > el.Value { if len(bottomk) == k { heap.Pop(&bkHeap) @@ -182,17 +184,17 @@ func funcBottomk(ev *evaluator, args Expressions) Value { } } sort.Sort(bottomk) - return Vector(bottomk) + return vector(bottomk) } -// === drop_common_labels(node ExprVector) Vector === -func funcDropCommonLabels(ev *evaluator, args Expressions) Value { - vector := ev.evalVector(args[0]) - if len(vector) < 1 { - return Vector{} +// === drop_common_labels(node model.ValVector) Vector === +func funcDropCommonLabels(ev *evaluator, args Expressions) model.Value { + vec := ev.evalVector(args[0]) + if len(vec) < 1 { + return vector{} } common := model.LabelSet{} - for k, v := range vector[0].Metric.Metric { + for k, v := range vec[0].Metric.Metric { // TODO(julius): Should we also drop common metric names? if k == model.MetricNameLabel { continue @@ -200,7 +202,7 @@ func funcDropCommonLabels(ev *evaluator, args Expressions) Value { common[k] = v } - for _, el := range vector[1:] { + for _, el := range vec[1:] { for k, v := range common { if el.Metric.Metric[k] != v { // Deletion of map entries while iterating over them is safe. @@ -212,18 +214,18 @@ func funcDropCommonLabels(ev *evaluator, args Expressions) Value { } } - for _, el := range vector { + for _, el := range vec { for k := range el.Metric.Metric { if _, ok := common[k]; ok { el.Metric.Del(k) } } } - return vector + return vec } -// === round(vector ExprVector, toNearest=1 Scalar) Vector === -func funcRound(ev *evaluator, args Expressions) Value { +// === round(vector model.ValVector, toNearest=1 Scalar) Vector === +func funcRound(ev *evaluator, args Expressions) model.Value { // round returns a number rounded to toNearest. // Ties are solved by rounding up. toNearest := float64(1) @@ -233,42 +235,42 @@ func funcRound(ev *evaluator, args Expressions) Value { // Invert as it seems to cause fewer floating point accuracy issues. toNearestInverse := 1.0 / toNearest - vector := ev.evalVector(args[0]) - for _, el := range vector { + vec := ev.evalVector(args[0]) + for _, el := range vec { el.Metric.Del(model.MetricNameLabel) el.Value = model.SampleValue(math.Floor(float64(el.Value)*toNearestInverse+0.5) / toNearestInverse) } - return vector + return vec } -// === scalar(node ExprVector) Scalar === -func funcScalar(ev *evaluator, args Expressions) Value { +// === scalar(node model.ValVector) Scalar === +func funcScalar(ev *evaluator, args Expressions) model.Value { v := ev.evalVector(args[0]) if len(v) != 1 { - return &Scalar{model.SampleValue(math.NaN()), ev.Timestamp} + return &model.Scalar{model.SampleValue(math.NaN()), ev.Timestamp} } - return &Scalar{model.SampleValue(v[0].Value), ev.Timestamp} + return &model.Scalar{model.SampleValue(v[0].Value), ev.Timestamp} } -// === count_scalar(vector ExprVector) model.SampleValue === -func funcCountScalar(ev *evaluator, args Expressions) Value { - return &Scalar{ +// === count_scalar(vector model.ValVector) model.SampleValue === +func funcCountScalar(ev *evaluator, args Expressions) model.Value { + return &model.Scalar{ Value: model.SampleValue(len(ev.evalVector(args[0]))), Timestamp: ev.Timestamp, } } -func aggrOverTime(ev *evaluator, args Expressions, aggrFn func([]model.SamplePair) model.SampleValue) Value { - matrix := ev.evalMatrix(args[0]) - resultVector := Vector{} +func aggrOverTime(ev *evaluator, args Expressions, aggrFn func([]model.SamplePair) model.SampleValue) model.Value { + mat := ev.evalMatrix(args[0]) + resultVector := vector{} - for _, el := range matrix { + for _, el := range mat { if len(el.Values) == 0 { continue } el.Metric.Del(model.MetricNameLabel) - resultVector = append(resultVector, &Sample{ + resultVector = append(resultVector, &sample{ Metric: el.Metric, Value: aggrFn(el.Values), Timestamp: ev.Timestamp, @@ -277,8 +279,8 @@ func aggrOverTime(ev *evaluator, args Expressions, aggrFn func([]model.SamplePai return resultVector } -// === avg_over_time(matrix ExprMatrix) Vector === -func funcAvgOverTime(ev *evaluator, args Expressions) Value { +// === avg_over_time(matrix model.ValMatrix) Vector === +func funcAvgOverTime(ev *evaluator, args Expressions) model.Value { return aggrOverTime(ev, args, func(values []model.SamplePair) model.SampleValue { var sum model.SampleValue for _, v := range values { @@ -288,15 +290,15 @@ func funcAvgOverTime(ev *evaluator, args Expressions) Value { }) } -// === count_over_time(matrix ExprMatrix) Vector === -func funcCountOverTime(ev *evaluator, args Expressions) Value { +// === count_over_time(matrix model.ValMatrix) Vector === +func funcCountOverTime(ev *evaluator, args Expressions) model.Value { return aggrOverTime(ev, args, func(values []model.SamplePair) model.SampleValue { return model.SampleValue(len(values)) }) } -// === floor(vector ExprVector) Vector === -func funcFloor(ev *evaluator, args Expressions) Value { +// === floor(vector model.ValVector) Vector === +func funcFloor(ev *evaluator, args Expressions) model.Value { vector := ev.evalVector(args[0]) for _, el := range vector { el.Metric.Del(model.MetricNameLabel) @@ -305,8 +307,8 @@ func funcFloor(ev *evaluator, args Expressions) Value { return vector } -// === max_over_time(matrix ExprMatrix) Vector === -func funcMaxOverTime(ev *evaluator, args Expressions) Value { +// === max_over_time(matrix model.ValMatrix) Vector === +func funcMaxOverTime(ev *evaluator, args Expressions) model.Value { return aggrOverTime(ev, args, func(values []model.SamplePair) model.SampleValue { max := math.Inf(-1) for _, v := range values { @@ -316,8 +318,8 @@ func funcMaxOverTime(ev *evaluator, args Expressions) Value { }) } -// === min_over_time(matrix ExprMatrix) Vector === -func funcMinOverTime(ev *evaluator, args Expressions) Value { +// === min_over_time(matrix model.ValMatrix) Vector === +func funcMinOverTime(ev *evaluator, args Expressions) model.Value { return aggrOverTime(ev, args, func(values []model.SamplePair) model.SampleValue { min := math.Inf(1) for _, v := range values { @@ -327,8 +329,8 @@ func funcMinOverTime(ev *evaluator, args Expressions) Value { }) } -// === sum_over_time(matrix ExprMatrix) Vector === -func funcSumOverTime(ev *evaluator, args Expressions) Value { +// === sum_over_time(matrix model.ValMatrix) Vector === +func funcSumOverTime(ev *evaluator, args Expressions) model.Value { return aggrOverTime(ev, args, func(values []model.SamplePair) model.SampleValue { var sum model.SampleValue for _, v := range values { @@ -338,8 +340,8 @@ func funcSumOverTime(ev *evaluator, args Expressions) Value { }) } -// === abs(vector ExprVector) Vector === -func funcAbs(ev *evaluator, args Expressions) Value { +// === abs(vector model.ValVector) Vector === +func funcAbs(ev *evaluator, args Expressions) model.Value { vector := ev.evalVector(args[0]) for _, el := range vector { el.Metric.Del(model.MetricNameLabel) @@ -348,10 +350,10 @@ func funcAbs(ev *evaluator, args Expressions) Value { return vector } -// === absent(vector ExprVector) Vector === -func funcAbsent(ev *evaluator, args Expressions) Value { +// === absent(vector model.ValVector) Vector === +func funcAbsent(ev *evaluator, args Expressions) model.Value { if len(ev.evalVector(args[0])) > 0 { - return Vector{} + return vector{} } m := model.Metric{} if vs, ok := args[0].(*VectorSelector); ok { @@ -361,9 +363,9 @@ func funcAbsent(ev *evaluator, args Expressions) Value { } } } - return Vector{ - &Sample{ - Metric: model.COWMetric{ + return vector{ + &sample{ + Metric: metric.Metric{ Metric: m, Copied: true, }, @@ -373,8 +375,8 @@ func funcAbsent(ev *evaluator, args Expressions) Value { } } -// === ceil(vector ExprVector) Vector === -func funcCeil(ev *evaluator, args Expressions) Value { +// === ceil(vector model.ValVector) Vector === +func funcCeil(ev *evaluator, args Expressions) model.Value { vector := ev.evalVector(args[0]) for _, el := range vector { el.Metric.Del(model.MetricNameLabel) @@ -383,8 +385,8 @@ func funcCeil(ev *evaluator, args Expressions) Value { return vector } -// === exp(vector ExprVector) Vector === -func funcExp(ev *evaluator, args Expressions) Value { +// === exp(vector model.ValVector) Vector === +func funcExp(ev *evaluator, args Expressions) model.Value { vector := ev.evalVector(args[0]) for _, el := range vector { el.Metric.Del(model.MetricNameLabel) @@ -394,7 +396,7 @@ func funcExp(ev *evaluator, args Expressions) Value { } // === sqrt(vector VectorNode) Vector === -func funcSqrt(ev *evaluator, args Expressions) Value { +func funcSqrt(ev *evaluator, args Expressions) model.Value { vector := ev.evalVector(args[0]) for _, el := range vector { el.Metric.Del(model.MetricNameLabel) @@ -403,8 +405,8 @@ func funcSqrt(ev *evaluator, args Expressions) Value { return vector } -// === ln(vector ExprVector) Vector === -func funcLn(ev *evaluator, args Expressions) Value { +// === ln(vector model.ValVector) Vector === +func funcLn(ev *evaluator, args Expressions) model.Value { vector := ev.evalVector(args[0]) for _, el := range vector { el.Metric.Del(model.MetricNameLabel) @@ -413,8 +415,8 @@ func funcLn(ev *evaluator, args Expressions) Value { return vector } -// === log2(vector ExprVector) Vector === -func funcLog2(ev *evaluator, args Expressions) Value { +// === log2(vector model.ValVector) Vector === +func funcLog2(ev *evaluator, args Expressions) model.Value { vector := ev.evalVector(args[0]) for _, el := range vector { el.Metric.Del(model.MetricNameLabel) @@ -423,8 +425,8 @@ func funcLog2(ev *evaluator, args Expressions) Value { return vector } -// === log10(vector ExprVector) Vector === -func funcLog10(ev *evaluator, args Expressions) Value { +// === log10(vector model.ValVector) Vector === +func funcLog10(ev *evaluator, args Expressions) model.Value { vector := ev.evalVector(args[0]) for _, el := range vector { el.Metric.Del(model.MetricNameLabel) @@ -433,12 +435,12 @@ func funcLog10(ev *evaluator, args Expressions) Value { return vector } -// === deriv(node ExprMatrix) Vector === -func funcDeriv(ev *evaluator, args Expressions) Value { - resultVector := Vector{} - matrix := ev.evalMatrix(args[0]) +// === deriv(node model.ValMatrix) Vector === +func funcDeriv(ev *evaluator, args Expressions) model.Value { + resultVector := vector{} + mat := ev.evalMatrix(args[0]) - for _, samples := range matrix { + for _, samples := range mat { // No sense in trying to compute a derivative without at least two points. // Drop this vector element. if len(samples.Values) < 2 { @@ -464,7 +466,7 @@ func funcDeriv(ev *evaluator, args Expressions) Value { resultValue := numerator / denominator - resultSample := &Sample{ + resultSample := &sample{ Metric: samples.Metric, Value: resultValue, Timestamp: ev.Timestamp, @@ -475,9 +477,9 @@ func funcDeriv(ev *evaluator, args Expressions) Value { return resultVector } -// === predict_linear(node ExprMatrix, k ExprScalar) Vector === -func funcPredictLinear(ev *evaluator, args Expressions) Value { - vector := funcDeriv(ev, args[0:1]).(Vector) +// === predict_linear(node model.ValMatrix, k model.ValScalar) Vector === +func funcPredictLinear(ev *evaluator, args Expressions) model.Value { + vec := funcDeriv(ev, args[0:1]).(vector) duration := model.SampleValue(model.SampleValue(ev.evalFloat(args[1]))) excludedLabels := map[model.LabelName]struct{}{ @@ -486,14 +488,14 @@ func funcPredictLinear(ev *evaluator, args Expressions) Value { // Calculate predicted delta over the duration. signatureToDelta := map[uint64]model.SampleValue{} - for _, el := range vector { + for _, el := range vec { signature := model.SignatureWithoutLabels(el.Metric.Metric, excludedLabels) signatureToDelta[signature] = el.Value * duration } // add predicted delta to last value. matrixBounds := ev.evalMatrixBounds(args[0]) - outVec := make(Vector, 0, len(signatureToDelta)) + outVec := make(vector, 0, len(signatureToDelta)) for _, samples := range matrixBounds { if len(samples.Values) < 2 { continue @@ -502,7 +504,7 @@ func funcPredictLinear(ev *evaluator, args Expressions) Value { delta, ok := signatureToDelta[signature] if ok { samples.Metric.Del(model.MetricNameLabel) - outVec = append(outVec, &Sample{ + outVec = append(outVec, &sample{ Metric: samples.Metric, Value: delta + samples.Values[1].Value, Timestamp: ev.Timestamp, @@ -512,12 +514,12 @@ func funcPredictLinear(ev *evaluator, args Expressions) Value { return outVec } -// === histogram_quantile(k ExprScalar, vector ExprVector) Vector === -func funcHistogramQuantile(ev *evaluator, args Expressions) Value { +// === histogram_quantile(k model.ValScalar, vector model.ValVector) Vector === +func funcHistogramQuantile(ev *evaluator, args Expressions) model.Value { q := model.SampleValue(ev.evalFloat(args[0])) inVec := ev.evalVector(args[1]) - outVec := Vector{} + outVec := vector{} signatureToMetricWithBuckets := map[uint64]*metricWithBuckets{} for _, el := range inVec { upperBound, err := strconv.ParseFloat( @@ -540,7 +542,7 @@ func funcHistogramQuantile(ev *evaluator, args Expressions) Value { } for _, mb := range signatureToMetricWithBuckets { - outVec = append(outVec, &Sample{ + outVec = append(outVec, &sample{ Metric: mb.metric, Value: model.SampleValue(quantile(q, mb.buckets)), Timestamp: ev.Timestamp, @@ -550,10 +552,10 @@ func funcHistogramQuantile(ev *evaluator, args Expressions) Value { return outVec } -// === resets(matrix ExprMatrix) Vector === -func funcResets(ev *evaluator, args Expressions) Value { +// === resets(matrix model.ValMatrix) Vector === +func funcResets(ev *evaluator, args Expressions) model.Value { in := ev.evalMatrix(args[0]) - out := make(Vector, 0, len(in)) + out := make(vector, 0, len(in)) for _, samples := range in { resets := 0 @@ -566,7 +568,7 @@ func funcResets(ev *evaluator, args Expressions) Value { prev = current } - rs := &Sample{ + rs := &sample{ Metric: samples.Metric, Value: model.SampleValue(resets), Timestamp: ev.Timestamp, @@ -577,10 +579,10 @@ func funcResets(ev *evaluator, args Expressions) Value { return out } -// === changes(matrix ExprMatrix) Vector === -func funcChanges(ev *evaluator, args Expressions) Value { +// === changes(matrix model.ValMatrix) Vector === +func funcChanges(ev *evaluator, args Expressions) model.Value { in := ev.evalMatrix(args[0]) - out := make(Vector, 0, len(in)) + out := make(vector, 0, len(in)) for _, samples := range in { changes := 0 @@ -593,7 +595,7 @@ func funcChanges(ev *evaluator, args Expressions) Value { prev = current } - rs := &Sample{ + rs := &sample{ Metric: samples.Metric, Value: model.SampleValue(changes), Timestamp: ev.Timestamp, @@ -604,8 +606,8 @@ func funcChanges(ev *evaluator, args Expressions) Value { return out } -// === label_replace(vector ExprVector, dst_label, replacement, src_labelname, regex ExprString) Vector === -func funcLabelReplace(ev *evaluator, args Expressions) Value { +// === label_replace(vector model.ValVector, dst_label, replacement, src_labelname, regex model.ValString) Vector === +func funcLabelReplace(ev *evaluator, args Expressions) model.Value { var ( vector = ev.evalVector(args[0]) dst = model.LabelName(ev.evalString(args[1]).Value) @@ -651,196 +653,196 @@ func funcLabelReplace(ev *evaluator, args Expressions) Value { var functions = map[string]*Function{ "abs": { Name: "abs", - ArgTypes: []ExprType{ExprVector}, - ReturnType: ExprVector, + ArgTypes: []model.ValueType{model.ValVector}, + ReturnType: model.ValVector, Call: funcAbs, }, "absent": { Name: "absent", - ArgTypes: []ExprType{ExprVector}, - ReturnType: ExprVector, + ArgTypes: []model.ValueType{model.ValVector}, + ReturnType: model.ValVector, Call: funcAbsent, }, "increase": { Name: "increase", - ArgTypes: []ExprType{ExprMatrix}, - ReturnType: ExprVector, + ArgTypes: []model.ValueType{model.ValMatrix}, + ReturnType: model.ValVector, Call: funcIncrease, }, "avg_over_time": { Name: "avg_over_time", - ArgTypes: []ExprType{ExprMatrix}, - ReturnType: ExprVector, + ArgTypes: []model.ValueType{model.ValMatrix}, + ReturnType: model.ValVector, Call: funcAvgOverTime, }, "bottomk": { Name: "bottomk", - ArgTypes: []ExprType{ExprScalar, ExprVector}, - ReturnType: ExprVector, + ArgTypes: []model.ValueType{model.ValScalar, model.ValVector}, + ReturnType: model.ValVector, Call: funcBottomk, }, "ceil": { Name: "ceil", - ArgTypes: []ExprType{ExprVector}, - ReturnType: ExprVector, + ArgTypes: []model.ValueType{model.ValVector}, + ReturnType: model.ValVector, Call: funcCeil, }, "changes": { Name: "changes", - ArgTypes: []ExprType{ExprMatrix}, - ReturnType: ExprVector, + ArgTypes: []model.ValueType{model.ValMatrix}, + ReturnType: model.ValVector, Call: funcChanges, }, "count_over_time": { Name: "count_over_time", - ArgTypes: []ExprType{ExprMatrix}, - ReturnType: ExprVector, + ArgTypes: []model.ValueType{model.ValMatrix}, + ReturnType: model.ValVector, Call: funcCountOverTime, }, "count_scalar": { Name: "count_scalar", - ArgTypes: []ExprType{ExprVector}, - ReturnType: ExprScalar, + ArgTypes: []model.ValueType{model.ValVector}, + ReturnType: model.ValScalar, Call: funcCountScalar, }, "delta": { Name: "delta", - ArgTypes: []ExprType{ExprMatrix, ExprScalar}, + ArgTypes: []model.ValueType{model.ValMatrix, model.ValScalar}, OptionalArgs: 1, // The 2nd argument is deprecated. - ReturnType: ExprVector, + ReturnType: model.ValVector, Call: funcDelta, }, "deriv": { Name: "deriv", - ArgTypes: []ExprType{ExprMatrix}, - ReturnType: ExprVector, + ArgTypes: []model.ValueType{model.ValMatrix}, + ReturnType: model.ValVector, Call: funcDeriv, }, "drop_common_labels": { Name: "drop_common_labels", - ArgTypes: []ExprType{ExprVector}, - ReturnType: ExprVector, + ArgTypes: []model.ValueType{model.ValVector}, + ReturnType: model.ValVector, Call: funcDropCommonLabels, }, "exp": { Name: "exp", - ArgTypes: []ExprType{ExprVector}, - ReturnType: ExprVector, + ArgTypes: []model.ValueType{model.ValVector}, + ReturnType: model.ValVector, Call: funcExp, }, "floor": { Name: "floor", - ArgTypes: []ExprType{ExprVector}, - ReturnType: ExprVector, + ArgTypes: []model.ValueType{model.ValVector}, + ReturnType: model.ValVector, Call: funcFloor, }, "histogram_quantile": { Name: "histogram_quantile", - ArgTypes: []ExprType{ExprScalar, ExprVector}, - ReturnType: ExprVector, + ArgTypes: []model.ValueType{model.ValScalar, model.ValVector}, + ReturnType: model.ValVector, Call: funcHistogramQuantile, }, "label_replace": { Name: "label_replace", - ArgTypes: []ExprType{ExprVector, ExprString, ExprString, ExprString, ExprString}, - ReturnType: ExprVector, + ArgTypes: []model.ValueType{model.ValVector, model.ValString, model.ValString, model.ValString, model.ValString}, + ReturnType: model.ValVector, Call: funcLabelReplace, }, "ln": { Name: "ln", - ArgTypes: []ExprType{ExprVector}, - ReturnType: ExprVector, + ArgTypes: []model.ValueType{model.ValVector}, + ReturnType: model.ValVector, Call: funcLn, }, "log10": { Name: "log10", - ArgTypes: []ExprType{ExprVector}, - ReturnType: ExprVector, + ArgTypes: []model.ValueType{model.ValVector}, + ReturnType: model.ValVector, Call: funcLog10, }, "log2": { Name: "log2", - ArgTypes: []ExprType{ExprVector}, - ReturnType: ExprVector, + ArgTypes: []model.ValueType{model.ValVector}, + ReturnType: model.ValVector, Call: funcLog2, }, "max_over_time": { Name: "max_over_time", - ArgTypes: []ExprType{ExprMatrix}, - ReturnType: ExprVector, + ArgTypes: []model.ValueType{model.ValMatrix}, + ReturnType: model.ValVector, Call: funcMaxOverTime, }, "min_over_time": { Name: "min_over_time", - ArgTypes: []ExprType{ExprMatrix}, - ReturnType: ExprVector, + ArgTypes: []model.ValueType{model.ValMatrix}, + ReturnType: model.ValVector, Call: funcMinOverTime, }, "predict_linear": { Name: "predict_linear", - ArgTypes: []ExprType{ExprMatrix, ExprScalar}, - ReturnType: ExprVector, + ArgTypes: []model.ValueType{model.ValMatrix, model.ValScalar}, + ReturnType: model.ValVector, Call: funcPredictLinear, }, "rate": { Name: "rate", - ArgTypes: []ExprType{ExprMatrix}, - ReturnType: ExprVector, + ArgTypes: []model.ValueType{model.ValMatrix}, + ReturnType: model.ValVector, Call: funcRate, }, "resets": { Name: "resets", - ArgTypes: []ExprType{ExprMatrix}, - ReturnType: ExprVector, + ArgTypes: []model.ValueType{model.ValMatrix}, + ReturnType: model.ValVector, Call: funcResets, }, "round": { Name: "round", - ArgTypes: []ExprType{ExprVector, ExprScalar}, + ArgTypes: []model.ValueType{model.ValVector, model.ValScalar}, OptionalArgs: 1, - ReturnType: ExprVector, + ReturnType: model.ValVector, Call: funcRound, }, "scalar": { Name: "scalar", - ArgTypes: []ExprType{ExprVector}, - ReturnType: ExprScalar, + ArgTypes: []model.ValueType{model.ValVector}, + ReturnType: model.ValScalar, Call: funcScalar, }, "sort": { Name: "sort", - ArgTypes: []ExprType{ExprVector}, - ReturnType: ExprVector, + ArgTypes: []model.ValueType{model.ValVector}, + ReturnType: model.ValVector, Call: funcSort, }, "sort_desc": { Name: "sort_desc", - ArgTypes: []ExprType{ExprVector}, - ReturnType: ExprVector, + ArgTypes: []model.ValueType{model.ValVector}, + ReturnType: model.ValVector, Call: funcSortDesc, }, "sqrt": { Name: "sqrt", - ArgTypes: []ExprType{ExprVector}, - ReturnType: ExprVector, + ArgTypes: []model.ValueType{model.ValVector}, + ReturnType: model.ValVector, Call: funcSqrt, }, "sum_over_time": { Name: "sum_over_time", - ArgTypes: []ExprType{ExprMatrix}, - ReturnType: ExprVector, + ArgTypes: []model.ValueType{model.ValMatrix}, + ReturnType: model.ValVector, Call: funcSumOverTime, }, "time": { Name: "time", - ArgTypes: []ExprType{}, - ReturnType: ExprScalar, + ArgTypes: []model.ValueType{}, + ReturnType: model.ValScalar, Call: funcTime, }, "topk": { Name: "topk", - ArgTypes: []ExprType{ExprScalar, ExprVector}, - ReturnType: ExprVector, + ArgTypes: []model.ValueType{model.ValScalar, model.ValVector}, + ReturnType: model.ValVector, Call: funcTopk, }, } @@ -851,7 +853,7 @@ func getFunction(name string) (*Function, bool) { return function, ok } -type vectorByValueHeap Vector +type vectorByValueHeap vector func (s vectorByValueHeap) Len() int { return len(s) @@ -869,7 +871,7 @@ func (s vectorByValueHeap) Swap(i, j int) { } func (s *vectorByValueHeap) Push(x interface{}) { - *s = append(*s, x.(*Sample)) + *s = append(*s, x.(*sample)) } func (s *vectorByValueHeap) Pop() interface{} { diff --git a/promql/parse.go b/promql/parse.go index dd3f1a3f8e..6442e3de1b 100644 --- a/promql/parse.go +++ b/promql/parse.go @@ -967,7 +967,7 @@ func (p *parser) vectorSelector(name string) *VectorSelector { // expectType checks the type of the node and raises an error if it // is not of the expected type. -func (p *parser) expectType(node Node, want ExprType, context string) { +func (p *parser) expectType(node Node, want model.ValueType, context string) { t := p.checkType(node) if t != want { p.errorf("expected type %s in %s, got %s", want, context, t) @@ -979,12 +979,12 @@ func (p *parser) expectType(node Node, want ExprType, context string) { // // Some of these checks are redundant as the the parsing stage does not allow // them, but the costs are small and might reveal errors when making changes. -func (p *parser) checkType(node Node) (typ ExprType) { +func (p *parser) checkType(node Node) (typ model.ValueType) { // For expressions the type is determined by their Type function. // Statements and lists do not have a type but are not invalid either. switch n := node.(type) { case Statements, Expressions, Statement: - typ = ExprNone + typ = model.ValNone case Expr: typ = n.Type() default: @@ -996,27 +996,27 @@ func (p *parser) checkType(node Node) (typ ExprType) { switch n := node.(type) { case Statements: for _, s := range n { - p.expectType(s, ExprNone, "statement list") + p.expectType(s, model.ValNone, "statement list") } case *AlertStmt: - p.expectType(n.Expr, ExprVector, "alert statement") + p.expectType(n.Expr, model.ValVector, "alert statement") case *EvalStmt: ty := p.checkType(n.Expr) - if ty == ExprNone { + if ty == model.ValNone { p.errorf("evaluation statement must have a valid expression type but got %s", ty) } case *RecordStmt: ty := p.checkType(n.Expr) - if ty != ExprVector && ty != ExprScalar { + if ty != model.ValVector && ty != model.ValScalar { p.errorf("record statement must have a valid expression of type vector or scalar but got %s", ty) } case Expressions: for _, e := range n { ty := p.checkType(e) - if ty == ExprNone { + if ty == model.ValNone { p.errorf("expression must have a valid expression type but got %s", ty) } } @@ -1024,7 +1024,7 @@ func (p *parser) checkType(node Node) (typ ExprType) { if !n.Op.isAggregator() { p.errorf("aggregation operator expected in aggregation expression but got %q", n.Op) } - p.expectType(n.Expr, ExprVector, "aggregation expression") + p.expectType(n.Expr, model.ValVector, "aggregation expression") case *BinaryExpr: lt := p.checkType(n.LHS) @@ -1033,11 +1033,11 @@ func (p *parser) checkType(node Node) (typ ExprType) { if !n.Op.isOperator() { p.errorf("only logical and arithmetic operators allowed in binary expression, got %q", n.Op) } - if (lt != ExprScalar && lt != ExprVector) || (rt != ExprScalar && rt != ExprVector) { + if (lt != model.ValScalar && lt != model.ValVector) || (rt != model.ValScalar && rt != model.ValVector) { p.errorf("binary expression must contain only scalar and vector types") } - if (lt != ExprVector || rt != ExprVector) && n.VectorMatching != nil { + if (lt != model.ValVector || rt != model.ValVector) && n.VectorMatching != nil { if len(n.VectorMatching.On) > 0 { p.errorf("vector matching only allowed between vectors") } @@ -1054,7 +1054,7 @@ func (p *parser) checkType(node Node) (typ ExprType) { } } - if (lt == ExprScalar || rt == ExprScalar) && (n.Op == itemLAND || n.Op == itemLOR) { + if (lt == model.ValScalar || rt == model.ValScalar) && (n.Op == itemLAND || n.Op == itemLOR) { p.errorf("AND and OR not allowed in binary scalar expression") } @@ -1077,7 +1077,7 @@ func (p *parser) checkType(node Node) (typ ExprType) { if n.Op != itemADD && n.Op != itemSUB { p.errorf("only + and - operators allowed for unary expressions") } - if t := p.checkType(n.Expr); t != ExprScalar && t != ExprVector { + if t := p.checkType(n.Expr); t != model.ValScalar && t != model.ValVector { p.errorf("unary expression only allowed on expressions of type scalar or vector, got %q", t) } diff --git a/promql/printer.go b/promql/printer.go index d61f0b563a..99025ccd95 100644 --- a/promql/printer.go +++ b/promql/printer.go @@ -24,47 +24,6 @@ import ( "github.com/prometheus/prometheus/util/strutil" ) -func (matrix Matrix) String() string { - metricStrings := make([]string, 0, len(matrix)) - for _, sampleStream := range matrix { - metricName, hasName := sampleStream.Metric.Metric[model.MetricNameLabel] - numLabels := len(sampleStream.Metric.Metric) - if hasName { - numLabels-- - } - labelStrings := make([]string, 0, numLabels) - for label, value := range sampleStream.Metric.Metric { - if label != model.MetricNameLabel { - labelStrings = append(labelStrings, fmt.Sprintf("%s=%q", label, value)) - } - } - sort.Strings(labelStrings) - valueStrings := make([]string, 0, len(sampleStream.Values)) - for _, value := range sampleStream.Values { - valueStrings = append(valueStrings, - fmt.Sprintf("\n%v @[%v]", value.Value, value.Timestamp)) - } - metricStrings = append(metricStrings, - fmt.Sprintf("%s{%s} => %s", - metricName, - strings.Join(labelStrings, ", "), - strings.Join(valueStrings, ", "))) - } - sort.Strings(metricStrings) - return strings.Join(metricStrings, "\n") -} - -func (vector Vector) String() string { - metricStrings := make([]string, 0, len(vector)) - for _, sample := range vector { - metricStrings = append(metricStrings, - fmt.Sprintf("%s => %v @[%v]", - sample.Metric, - sample.Value, sample.Timestamp)) - } - return strings.Join(metricStrings, "\n") -} - // Tree returns a string of the tree structure of the given node. func Tree(node Node) string { return tree(node, "") diff --git a/promql/quantile.go b/promql/quantile.go index 88585bcd85..ca7af50d5f 100644 --- a/promql/quantile.go +++ b/promql/quantile.go @@ -18,6 +18,8 @@ import ( "sort" "github.com/prometheus/common/model" + + "github.com/prometheus/prometheus/storage/metric" ) // Helpers to calculate quantiles. @@ -42,7 +44,7 @@ func (b buckets) Swap(i, j int) { b[i], b[j] = b[j], b[i] } func (b buckets) Less(i, j int) bool { return b[i].upperBound < b[j].upperBound } type metricWithBuckets struct { - metric model.COWMetric + metric metric.Metric buckets buckets } diff --git a/promql/test.go b/promql/test.go index fecc89caf0..d4adbce5f5 100644 --- a/promql/test.go +++ b/promql/test.go @@ -340,25 +340,25 @@ func (ev *evalCmd) expect(pos int, m model.Metric, vals ...sequenceValue) { } // compareResult compares the result value with the defined expectation. -func (ev *evalCmd) compareResult(result Value) error { +func (ev *evalCmd) compareResult(result model.Value) error { switch val := result.(type) { - case Matrix: + case model.Matrix: if ev.instant { return fmt.Errorf("received range result on instant evaluation") } seen := map[model.Fingerprint]bool{} for pos, v := range val { - fp := v.Metric.Metric.Fingerprint() + fp := v.Metric.Fingerprint() if _, ok := ev.metrics[fp]; !ok { - return fmt.Errorf("unexpected metric %s in result", v.Metric.Metric) + return fmt.Errorf("unexpected metric %s in result", v.Metric) } exp := ev.expected[fp] if ev.ordered && exp.pos != pos+1 { - return fmt.Errorf("expected metric %s with %v at position %d but was at %d", v.Metric.Metric, exp.vals, exp.pos, pos+1) + return fmt.Errorf("expected metric %s with %v at position %d but was at %d", v.Metric, exp.vals, exp.pos, pos+1) } for i, expVal := range exp.vals { if !almostEqual(float64(expVal.value), float64(v.Values[i].Value)) { - return fmt.Errorf("expected %v for %s but got %v", expVal, v.Metric.Metric, v.Values) + return fmt.Errorf("expected %v for %s but got %v", expVal, v.Metric, v.Values) } } seen[fp] = true @@ -369,22 +369,22 @@ func (ev *evalCmd) compareResult(result Value) error { } } - case Vector: + case model.Vector: if !ev.instant { fmt.Errorf("received instant result on range evaluation") } seen := map[model.Fingerprint]bool{} for pos, v := range val { - fp := v.Metric.Metric.Fingerprint() + fp := v.Metric.Fingerprint() if _, ok := ev.metrics[fp]; !ok { - return fmt.Errorf("unexpected metric %s in result", v.Metric.Metric) + return fmt.Errorf("unexpected metric %s in result", v.Metric) } exp := ev.expected[fp] if ev.ordered && exp.pos != pos+1 { - return fmt.Errorf("expected metric %s with %v at position %d but was at %d", v.Metric.Metric, exp.vals, exp.pos, pos+1) + return fmt.Errorf("expected metric %s with %v at position %d but was at %d", v.Metric, exp.vals, exp.pos, pos+1) } if !almostEqual(float64(exp.vals[0].value), float64(v.Value)) { - return fmt.Errorf("expected %v for %s but got %v", exp.vals[0].value, v.Metric.Metric, v.Value) + return fmt.Errorf("expected %v for %s but got %v", exp.vals[0].value, v.Metric, v.Value) } seen[fp] = true @@ -395,7 +395,7 @@ func (ev *evalCmd) compareResult(result Value) error { } } - case *Scalar: + case *model.Scalar: if !almostEqual(float64(ev.expected[0].vals[0].value), float64(val.Value)) { return fmt.Errorf("expected scalar %v but got %v", val.Value, ev.expected[0].vals[0].value) } diff --git a/rules/alerting.go b/rules/alerting.go index 7a72073f67..69b6d87e9b 100644 --- a/rules/alerting.go +++ b/rules/alerting.go @@ -77,8 +77,8 @@ type Alert struct { } // sample returns a Sample suitable for recording the alert. -func (a Alert) sample(timestamp model.Time, value model.SampleValue) *promql.Sample { - recordedMetric := model.Metric{} +func (a Alert) sample(timestamp model.Time, value model.SampleValue) *model.Sample { + recordedMetric := make(model.Metric, len(a.Labels)+3) for label, value := range a.Labels { recordedMetric[label] = value } @@ -87,11 +87,8 @@ func (a Alert) sample(timestamp model.Time, value model.SampleValue) *promql.Sam recordedMetric[alertNameLabel] = model.LabelValue(a.Name) recordedMetric[alertStateLabel] = model.LabelValue(a.State.String()) - return &promql.Sample{ - Metric: model.COWMetric{ - Metric: recordedMetric, - Copied: true, - }, + return &model.Sample{ + Metric: recordedMetric, Value: value, Timestamp: timestamp, } @@ -152,7 +149,7 @@ func (rule *AlertingRule) Name() string { // eval evaluates the rule expression and then creates pending alerts and fires // or removes previously pending alerts accordingly. -func (rule *AlertingRule) eval(timestamp model.Time, engine *promql.Engine) (promql.Vector, error) { +func (rule *AlertingRule) eval(timestamp model.Time, engine *promql.Engine) (model.Vector, error) { query, err := engine.NewInstantQuery(rule.vector.String(), timestamp) if err != nil { return nil, err @@ -169,11 +166,11 @@ func (rule *AlertingRule) eval(timestamp model.Time, engine *promql.Engine) (pro // or update the expression value for existing elements. resultFPs := map[model.Fingerprint]struct{}{} for _, sample := range exprResult { - fp := sample.Metric.Metric.Fingerprint() + fp := sample.Metric.Fingerprint() resultFPs[fp] = struct{}{} if alert, ok := rule.activeAlerts[fp]; !ok { - labels := model.LabelSet(sample.Metric.Metric.Clone()) + labels := model.LabelSet(sample.Metric.Clone()) labels = labels.Merge(rule.labels) if _, ok := labels[model.MetricNameLabel]; ok { delete(labels, model.MetricNameLabel) @@ -190,7 +187,7 @@ func (rule *AlertingRule) eval(timestamp model.Time, engine *promql.Engine) (pro } } - vector := promql.Vector{} + var vector model.Vector // Check if any pending alerts should be removed or fire now. Write out alert timeseries. for fp, activeAlert := range rule.activeAlerts { diff --git a/rules/manager.go b/rules/manager.go index 5cc91a485b..90bfd447a3 100644 --- a/rules/manager.go +++ b/rules/manager.go @@ -80,7 +80,7 @@ type Rule interface { // Name returns the name of the rule. Name() string // Eval evaluates the rule, including any associated recording or alerting actions. - eval(model.Time, *promql.Engine) (promql.Vector, error) + eval(model.Time, *promql.Engine) (model.Vector, error) // String returns a human-readable string representation of the rule. String() string // HTMLSnippet returns a human-readable string representation of the rule, @@ -273,11 +273,7 @@ func (m *Manager) runIteration() { } for _, s := range vector { - m.sampleAppender.Append(&model.Sample{ - Metric: s.Metric.Metric, - Value: s.Value, - Timestamp: s.Timestamp, - }) + m.sampleAppender.Append(s) } }(rule) } diff --git a/rules/recording.go b/rules/recording.go index 5ac4f10ec3..f3e3ecda5b 100644 --- a/rules/recording.go +++ b/rules/recording.go @@ -43,38 +43,44 @@ func NewRecordingRule(name string, vector promql.Expr, labels model.LabelSet) *R func (rule RecordingRule) Name() string { return rule.name } // eval evaluates the rule and then overrides the metric names and labels accordingly. -func (rule RecordingRule) eval(timestamp model.Time, engine *promql.Engine) (promql.Vector, error) { +func (rule RecordingRule) eval(timestamp model.Time, engine *promql.Engine) (model.Vector, error) { query, err := engine.NewInstantQuery(rule.vector.String(), timestamp) if err != nil { return nil, err } - result := query.Exec() - var vector promql.Vector + var ( + result = query.Exec() + vector model.Vector + ) switch result.Value.(type) { - case promql.Vector: + case model.Vector: vector, err = result.Vector() if err != nil { return nil, err } - case *promql.Scalar: + case *model.Scalar: scalar, err := result.Scalar() if err != nil { return nil, err } - vector = promql.Vector{&promql.Sample{Value: scalar.Value, Timestamp: scalar.Timestamp}} + vector = model.Vector{&model.Sample{ + Value: scalar.Value, + Timestamp: scalar.Timestamp, + }} default: return nil, fmt.Errorf("rule result is not a vector or scalar") } // Override the metric name and labels. for _, sample := range vector { - sample.Metric.Set(model.MetricNameLabel, model.LabelValue(rule.name)) + sample.Metric[model.MetricNameLabel] = model.LabelValue(rule.name) + for label, value := range rule.labels { if value == "" { - sample.Metric.Del(label) + delete(sample.Metric, label) } else { - sample.Metric.Set(label, value) + sample.Metric[label] = value } } } diff --git a/storage/metric/metric_test.go b/storage/metric/metric_test.go index 855bdcb2d7..f33a51e40a 100644 --- a/storage/metric/metric_test.go +++ b/storage/metric/metric_test.go @@ -27,21 +27,21 @@ func TestMetric(t *testing.T) { scenarios := []struct { fn func(*Metric) - out Metric + out model.Metric }{ { - fn: func(cm *etric) { + fn: func(cm *Metric) { cm.Del("to_delete") }, - out: Metric{ + out: model.Metric{ "to_change": "test2", }, }, { - fn: func(cm *COWMetric) { + fn: func(cm *Metric) { cm.Set("to_change", "changed") }, - out: Metric{ + out: model.Metric{ "to_delete": "test1", "to_change": "changed", }, diff --git a/template/template.go b/template/template.go index 8051b37cd4..19a2e9be2c 100644 --- a/template/template.go +++ b/template/template.go @@ -64,24 +64,21 @@ func query(q string, timestamp model.Time, queryEngine *promql.Engine) (queryRes if res.Err != nil { return nil, res.Err } - var vector promql.Vector + var vector model.Vector switch v := res.Value.(type) { - case promql.Matrix: + case model.Matrix: return nil, errors.New("matrix return values not supported") - case promql.Vector: + case model.Vector: vector = v - case *promql.Scalar: - vector = promql.Vector{&promql.Sample{ + case *model.Scalar: + vector = model.Vector{&model.Sample{ Value: v.Value, Timestamp: v.Timestamp, }} - case *promql.String: - vector = promql.Vector{&promql.Sample{ - Metric: model.COWMetric{ - Metric: model.Metric{"__value__": model.LabelValue(v.Value)}, - Copied: true, - }, + case *model.String: + vector = model.Vector{&model.Sample{ + Metric: model.Metric{"__value__": model.LabelValue(v.Value)}, Timestamp: v.Timestamp, }} default: @@ -96,7 +93,7 @@ func query(q string, timestamp model.Time, queryEngine *promql.Engine) (queryRes Value: float64(v.Value), Labels: make(map[string]string), } - for label, value := range v.Metric.Metric { + for label, value := range v.Metric { s.Labels[string(label)] = string(value) } result[n] = &s diff --git a/web/api/legacy/query.go b/web/api/legacy/query.go index 9136ad7673..72a919ed93 100644 --- a/web/api/legacy/query.go +++ b/web/api/legacy/query.go @@ -26,8 +26,6 @@ import ( "github.com/prometheus/common/model" "github.com/prometheus/log" - - "github.com/prometheus/prometheus/promql" ) // Enables cross-site script calls. @@ -89,15 +87,15 @@ func (api *API) Query(w http.ResponseWriter, r *http.Request) { } log.Debugf("Instant query: %s\nQuery stats:\n%s\n", expr, query.Stats()) - if vec, ok := res.Value.(promql.Vector); ok { + if vec, ok := res.Value.(model.Vector); ok { respondJSON(w, plainVec(vec)) return } - if sca, ok := res.Value.(*promql.Scalar); ok { + if sca, ok := res.Value.(*model.Scalar); ok { respondJSON(w, (*plainScalar)(sca)) return } - if str, ok := res.Value.(*promql.String); ok { + if str, ok := res.Value.(*model.String); ok { respondJSON(w, (*plainString)(str)) return } @@ -107,10 +105,10 @@ func (api *API) Query(w http.ResponseWriter, r *http.Request) { // plainVec is an indirection that hides the original MarshalJSON method // which does not fit the response format for the legacy API. -type plainVec promql.Vector +type plainVec model.Vector func (pv plainVec) MarshalJSON() ([]byte, error) { - type plainSmpl promql.Sample + type plainSmpl model.Sample v := make([]*plainSmpl, len(pv)) for i, sv := range pv { @@ -120,8 +118,8 @@ func (pv plainVec) MarshalJSON() ([]byte, error) { return json.Marshal(&v) } -func (pv plainVec) Type() promql.ExprType { - return promql.ExprVector +func (pv plainVec) Type() model.ValueType { + return model.ValVector } func (pv plainVec) String() string { @@ -130,15 +128,15 @@ func (pv plainVec) String() string { // plainScalar is an indirection that hides the original MarshalJSON method // which does not fit the response format for the legacy API. -type plainScalar promql.Scalar +type plainScalar model.Scalar func (ps plainScalar) MarshalJSON() ([]byte, error) { s := strconv.FormatFloat(float64(ps.Value), 'f', -1, 64) return json.Marshal(&s) } -func (plainScalar) Type() promql.ExprType { - return promql.ExprScalar +func (plainScalar) Type() model.ValueType { + return model.ValScalar } func (plainScalar) String() string { @@ -147,10 +145,10 @@ func (plainScalar) String() string { // plainString is an indirection that hides the original MarshalJSON method // which does not fit the response format for the legacy API. -type plainString promql.String +type plainString model.String -func (pv plainString) Type() promql.ExprType { - return promql.ExprString +func (pv plainString) Type() model.ValueType { + return model.ValString } func (pv plainString) String() string { @@ -257,7 +255,7 @@ func errorJSON(w io.Writer, err error) error { } // RespondJSON converts the given data value to JSON and writes it to w. -func respondJSON(w io.Writer, val promql.Value) error { +func respondJSON(w io.Writer, val model.Value) error { data := struct { Type string `json:"type"` Value interface{} `json:"value"` @@ -268,7 +266,7 @@ func respondJSON(w io.Writer, val promql.Value) error { Version: jsonFormatVersion, } // TODO(fabxc): Adding MarshalJSON to promql.Values might be a good idea. - if sc, ok := val.(*promql.Scalar); ok { + if sc, ok := val.(*model.Scalar); ok { data.Value = sc.Value } enc := json.NewEncoder(w) diff --git a/web/api/v1/api.go b/web/api/v1/api.go index 2d08e20bec..0809a8a9ec 100644 --- a/web/api/v1/api.go +++ b/web/api/v1/api.go @@ -15,6 +15,7 @@ import ( "github.com/prometheus/prometheus/promql" "github.com/prometheus/prometheus/storage/local" + "github.com/prometheus/prometheus/storage/metric" "github.com/prometheus/prometheus/util/route" "github.com/prometheus/prometheus/util/strutil" ) @@ -98,8 +99,8 @@ func (api *API) Register(r *route.Router) { } type queryData struct { - ResultType promql.ExprType `json:"resultType"` - Result promql.Value `json:"result"` + ResultType model.ValueType `json:"resultType"` + Result model.Value `json:"result"` } func (api *API) query(r *http.Request) (interface{}, *apiError) { @@ -187,7 +188,7 @@ func (api *API) series(r *http.Request) (interface{}, *apiError) { if len(r.Form["match[]"]) == 0 { return nil, &apiError{errorBadData, fmt.Errorf("no match[] parameter provided")} } - res := map[model.Fingerprint]model.COWMetric{} + res := map[model.Fingerprint]metric.Metric{} for _, lm := range r.Form["match[]"] { matchers, err := promql.ParseMetricSelector(lm) diff --git a/web/api/v1/api_test.go b/web/api/v1/api_test.go index f5fcac28ec..1b1c497e1a 100644 --- a/web/api/v1/api_test.go +++ b/web/api/v1/api_test.go @@ -55,8 +55,8 @@ func TestEndpoints(t *testing.T) { "time": []string{"123.3"}, }, response: &queryData{ - ResultType: promql.ExprScalar, - Result: &promql.Scalar{ + ResultType: model.ValScalar, + Result: &model.Scalar{ Value: 2, Timestamp: start.Add(123*time.Second + 300*time.Millisecond), }, @@ -69,8 +69,8 @@ func TestEndpoints(t *testing.T) { "time": []string{"1970-01-01T00:02:03Z"}, }, response: &queryData{ - ResultType: promql.ExprScalar, - Result: &promql.Scalar{ + ResultType: model.ValScalar, + Result: &model.Scalar{ Value: 0.333, Timestamp: start.Add(123 * time.Second), }, @@ -83,8 +83,8 @@ func TestEndpoints(t *testing.T) { "time": []string{"1970-01-01T01:02:03+01:00"}, }, response: &queryData{ - ResultType: promql.ExprScalar, - Result: &promql.Scalar{ + ResultType: model.ValScalar, + Result: &model.Scalar{ Value: 0.333, Timestamp: start.Add(123 * time.Second), }, @@ -99,14 +99,15 @@ func TestEndpoints(t *testing.T) { "step": []string{"1"}, }, response: &queryData{ - ResultType: promql.ExprMatrix, - Result: promql.Matrix{ - &promql.SampleStream{ + ResultType: model.ValMatrix, + Result: model.Matrix{ + &model.SampleStream{ Values: []model.SamplePair{ {Value: 0, Timestamp: start}, {Value: 1, Timestamp: start.Add(1 * time.Second)}, {Value: 2, Timestamp: start.Add(2 * time.Second)}, }, + Metric: model.Metric{}, }, }, }, @@ -312,7 +313,7 @@ func TestEndpoints(t *testing.T) { t.Fatalf("Expected error of type %q but got none", test.errType) } if !reflect.DeepEqual(resp, test.response) { - t.Fatalf("Response does not match, expected:\n%#v\ngot:\n%#v", test.response, resp) + t.Fatalf("Response does not match, expected:\n%+v\ngot:\n%+v", test.response, resp) } // Ensure that removed metrics are unindexed before the next request. suite.Storage().WaitForIndexing() diff --git a/web/federate.go b/web/federate.go index d32773c4c3..a4f2757b93 100644 --- a/web/federate.go +++ b/web/federate.go @@ -20,6 +20,7 @@ import ( "github.com/prometheus/prometheus/promql" "github.com/prometheus/prometheus/storage/local" + "github.com/prometheus/prometheus/storage/metric" dto "github.com/prometheus/client_model/go" "github.com/prometheus/common/expfmt" @@ -33,7 +34,7 @@ type Federation struct { func (fed *Federation) ServeHTTP(w http.ResponseWriter, req *http.Request) { req.ParseForm() - metrics := map[model.Fingerprint]model.COWMetric{} + metrics := map[model.Fingerprint]metric.Metric{} for _, s := range req.Form["match[]"] { matchers, err := promql.ParseMetricSelector(s)