Merge pull request #1025 from prometheus/model

Migrate promql to common/model
This commit is contained in:
Fabian Reinartz 2015-08-25 13:55:56 +02:00
commit a2d117c677
28 changed files with 928 additions and 673 deletions

4
Godeps/Godeps.json generated
View file

@ -60,11 +60,11 @@
}, },
{ {
"ImportPath": "github.com/prometheus/common/expfmt", "ImportPath": "github.com/prometheus/common/expfmt",
"Rev": "2502df85be1b9482ed669faa6b7cfe7f850eb08e" "Rev": "7cd9f43750daf997c60f33f46680780067410f28"
}, },
{ {
"ImportPath": "github.com/prometheus/common/model", "ImportPath": "github.com/prometheus/common/model",
"Rev": "2502df85be1b9482ed669faa6b7cfe7f850eb08e" "Rev": "7cd9f43750daf997c60f33f46680780067410f28"
}, },
{ {
"ImportPath": "github.com/prometheus/client_model/go", "ImportPath": "github.com/prometheus/client_model/go",

View file

@ -24,23 +24,23 @@ import (
const ( const (
// ExportedLabelPrefix is the prefix to prepend to the label names present in // 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. // 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 // MetricNameLabel is the label name indicating the metric name of a
// timeseries. // timeseries.
MetricNameLabel LabelName = "__name__" MetricNameLabel = "__name__"
// SchemeLabel is the name of the label that holds the scheme on which to // SchemeLabel is the name of the label that holds the scheme on which to
// scrape a target. // scrape a target.
SchemeLabel LabelName = "__scheme__" SchemeLabel = "__scheme__"
// AddressLabel is the name of the label that holds the address of // AddressLabel is the name of the label that holds the address of
// a scrape target. // a scrape target.
AddressLabel LabelName = "__address__" AddressLabel = "__address__"
// MetricsPathLabel is the name of the label that holds the path on which to // MetricsPathLabel is the name of the label that holds the path on which to
// scrape a target. // scrape a target.
MetricsPathLabel LabelName = "__metrics_path__" MetricsPathLabel = "__metrics_path__"
// ReservedLabelPrefix is a prefix which is not legal in user-supplied // ReservedLabelPrefix is a prefix which is not legal in user-supplied
// label names. // label names.
@ -63,10 +63,10 @@ const (
// JobLabel is the label name indicating the job from which a timeseries // JobLabel is the label name indicating the job from which a timeseries
// was scraped. // was scraped.
JobLabel LabelName = "job" JobLabel = "job"
// InstanceLabel is the label name used for the instance label. // 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 // BucketLabel is used for the label that defines the upper bound of a
// bucket of a histogram ("le" -> "less or equal"). // bucket of a histogram ("le" -> "less or equal").

View file

@ -14,7 +14,6 @@
package model package model
import ( import (
"encoding/json"
"fmt" "fmt"
"sort" "sort"
"strings" "strings"
@ -80,41 +79,3 @@ func (m Metric) Fingerprint() Fingerprint {
func (m Metric) FastFingerprint() Fingerprint { func (m Metric) FastFingerprint() Fingerprint {
return LabelSet(m).FastFingerprint() 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)
}

View file

@ -81,52 +81,3 @@ func BenchmarkMetric(b *testing.B) {
testMetric(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)
}
}
}

View file

@ -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) 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, {}} input := []map[string]string{nil, {}}
var ms runtime.MemStats var ms runtime.MemStats
@ -262,7 +262,7 @@ func TestEmptyLabelSignature(t *testing.T) {
runtime.ReadMemStats(&ms) runtime.ReadMemStats(&ms)
if got := ms.Alloc; alloc != got { 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")
} }
} }

View file

@ -112,9 +112,7 @@ var dotPrecision = int(math.Log10(float64(second)))
// String returns a string representation of the Time. // String returns a string representation of the Time.
func (t Time) String() string { func (t Time) String() string {
s := strconv.FormatInt(int64(t), 10) return strconv.FormatFloat(float64(t)/float64(second), 'f', -1, 64)
i := len(s) - dotPrecision
return s[:i] + "." + s[i:]
} }
// MarshalJSON implements the json.Marshaler interface. // MarshalJSON implements the json.Marshaler interface.

View file

@ -67,18 +67,13 @@ func (s SamplePair) MarshalJSON() ([]byte, error) {
if err != nil { if err != nil {
return nil, err return nil, err
} }
return json.Marshal([...]interface{}{t, v}) return []byte(fmt.Sprintf("[%s,%s]", t, v)), nil
} }
// UnmarshalJSON implements json.Unmarshaler. // UnmarshalJSON implements json.Unmarshaler.
func (s *SamplePair) UnmarshalJSON(b []byte) error { func (s *SamplePair) UnmarshalJSON(b []byte) error {
if len(b) < 2 || b[0] != '"' || b[len(b)-1] != '"' { v := [...]json.Unmarshaler{&s.Timestamp, &s.Value}
return fmt.Errorf("sample pair must be array") return json.Unmarshal(b, &v)
}
b = b[1 : len(b)-1]
return json.Unmarshal(b, [...]json.Unmarshaler{&s.Timestamp, &s.Value})
} }
// Equal returns true if this SamplePair and o have equal Values and equal // 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)) 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) return fmt.Sprintf("%s @[%s]", s.Value, s.Timestamp)
} }
// Sample is a sample pair associated with a metric. // Sample is a sample pair associated with a metric.
type Sample struct { type Sample struct {
Metric Metric Metric Metric `json:"metric"`
Value SampleValue Value SampleValue `json:"value"`
Timestamp Time Timestamp Time `json:"timestamp"`
} }
// Equal compares first the metrics, then the timestamp, then the value. // Equal compares first the metrics, then the timestamp, then the value.
@ -117,13 +112,53 @@ func (s *Sample) Equal(o *Sample) bool {
return true return true
} }
func (s *Sample) String() string { func (s Sample) String() string {
return fmt.Sprintf("%s => %s", s.Metric, SamplePair{ return fmt.Sprintf("%s => %s", s.Metric, SamplePair{
Timestamp: s.Timestamp, Timestamp: s.Timestamp,
Value: s.Value, 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. // Samples is a sortable Sample slice. It implements sort.Interface.
type Samples []*Sample type Samples []*Sample
@ -169,7 +204,7 @@ type SampleStream struct {
Values []SamplePair `json:"values"` Values []SamplePair `json:"values"`
} }
func (ss *SampleStream) String() string { func (ss SampleStream) String() string {
vals := make([]string, len(ss.Values)) vals := make([]string, len(ss.Values))
for i, v := range ss.Values { for i, v := range ss.Values {
vals[i] = v.String() vals[i] = v.String()
@ -247,10 +282,33 @@ type Scalar struct {
Timestamp Time `json:"timestamp"` Timestamp Time `json:"timestamp"`
} }
func (s *Scalar) String() string { func (s Scalar) String() string {
return fmt.Sprintf("scalar: %v @[%v]", s.Value, s.Timestamp) 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. // String is a string value evaluated at the set timestamp.
type String struct { type String struct {
Value string `json:"value"` Value string `json:"value"`
@ -261,6 +319,17 @@ func (s *String) String() string {
return s.Value 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 // Vector is basically only an alias for Samples, but the
// contract is that in a Vector, all Samples have the same timestamp. // contract is that in a Vector, all Samples have the same timestamp.
type Vector []*Sample type Vector []*Sample

View file

@ -14,10 +14,258 @@
package model package model
import ( import (
"encoding/json"
"math"
"reflect"
"sort" "sort"
"testing" "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) { func TestVectorSort(t *testing.T) {
input := Vector{ input := Vector{
&Sample{ &Sample{

View file

@ -14,7 +14,6 @@
package promql package promql
import ( import (
"encoding/json"
"fmt" "fmt"
"time" "time"
@ -88,45 +87,13 @@ func (*AlertStmt) stmt() {}
func (*EvalStmt) stmt() {} func (*EvalStmt) stmt() {}
func (*RecordStmt) 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 "<ExprNone>"
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. // Expr is a generic interface for all expression types.
type Expr interface { type Expr interface {
Node Node
// Type returns the type the expression evaluates to. It does not perform // Type returns the type the expression evaluates to. It does not perform
// in-depth checks as this is done at parsing-time. // 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 ensures that no other types accidentally implement the interface.
expr() expr()
} }
@ -167,7 +134,7 @@ type MatrixSelector struct {
// The series iterators are populated at query analysis time. // The series iterators are populated at query analysis time.
iterators map[model.Fingerprint]local.SeriesIterator iterators map[model.Fingerprint]local.SeriesIterator
metrics map[model.Fingerprint]model.COWMetric metrics map[model.Fingerprint]metric.Metric
} }
// NumberLiteral represents a number. // NumberLiteral represents a number.
@ -201,23 +168,23 @@ type VectorSelector struct {
// The series iterators are populated at query analysis time. // The series iterators are populated at query analysis time.
iterators map[model.Fingerprint]local.SeriesIterator 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 *AggregateExpr) Type() model.ValueType { return model.ValVector }
func (e *Call) Type() ExprType { return e.Func.ReturnType } func (e *Call) Type() model.ValueType { return e.Func.ReturnType }
func (e *MatrixSelector) Type() ExprType { return ExprMatrix } func (e *MatrixSelector) Type() model.ValueType { return model.ValMatrix }
func (e *NumberLiteral) Type() ExprType { return ExprScalar } func (e *NumberLiteral) Type() model.ValueType { return model.ValScalar }
func (e *ParenExpr) Type() ExprType { return e.Expr.Type() } func (e *ParenExpr) Type() model.ValueType { return e.Expr.Type() }
func (e *StringLiteral) Type() ExprType { return ExprString } func (e *StringLiteral) Type() model.ValueType { return model.ValString }
func (e *UnaryExpr) Type() ExprType { return e.Expr.Type() } func (e *UnaryExpr) Type() model.ValueType { return e.Expr.Type() }
func (e *VectorSelector) Type() ExprType { return ExprVector } func (e *VectorSelector) Type() model.ValueType { return model.ValVector }
func (e *BinaryExpr) Type() ExprType { func (e *BinaryExpr) Type() model.ValueType {
if e.LHS.Type() == ExprScalar && e.RHS.Type() == ExprScalar { if e.LHS.Type() == model.ValScalar && e.RHS.Type() == model.ValScalar {
return ExprScalar return model.ValScalar
} }
return ExprVector return model.ValVector
} }
func (*AggregateExpr) expr() {} func (*AggregateExpr) expr() {}

View file

@ -14,12 +14,10 @@
package promql package promql
import ( import (
"encoding/json"
"fmt" "fmt"
"math" "math"
"runtime" "runtime"
"sort" "sort"
"strconv"
"time" "time"
"github.com/prometheus/common/model" "github.com/prometheus/common/model"
@ -31,114 +29,70 @@ import (
"github.com/prometheus/prometheus/util/stats" "github.com/prometheus/prometheus/util/stats"
) )
// SampleStream is a stream of Values belonging to an attached COWMetric. // sampleStream is a stream of Values belonging to an attached COWMetric.
type SampleStream struct { type sampleStream struct {
Metric model.COWMetric `json:"metric"` Metric metric.Metric
Values []model.SamplePair `json:"values"` Values []model.SamplePair
} }
// Sample is a single sample belonging to a COWMetric. // sample is a single sample belonging to a COWMetric.
type Sample struct { type sample struct {
Metric model.COWMetric `json:"metric"` Metric metric.Metric
Value model.SampleValue `json:"value"` Value model.SampleValue
Timestamp model.Time `json:"timestamp"` Timestamp model.Time
} }
// MarshalJSON implements json.Marshaler. // vector is basically only an alias for model.Samples, but the
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
// contract is that in a Vector, all Samples have the same timestamp. // 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. // has a String method.
type Matrix []*SampleStream type matrix []*sampleStream
// Len implements sort.Interface. func (matrix) Type() model.ValueType { return model.ValMatrix }
func (matrix Matrix) Len() int { func (mat matrix) String() string { return mat.value().String() }
return len(matrix)
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 // Result holds the resulting value of an execution or an error
// if any occurred. // if any occurred.
type Result struct { type Result struct {
Err error Err error
Value Value Value model.Value
} }
// Vector returns a vector if the result value is one. An error is returned if // 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. // 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 { if r.Err != nil {
return nil, r.Err return nil, r.Err
} }
v, ok := r.Value.(Vector) v, ok := r.Value.(model.Vector)
if !ok { if !ok {
return nil, fmt.Errorf("query result is not a vector") 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 // Matrix returns a matrix. An error is returned if
// the result was an error or the result value is not a matrix. // 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 { if r.Err != nil {
return nil, r.Err return nil, r.Err
} }
v, ok := r.Value.(Matrix) v, ok := r.Value.(model.Matrix)
if !ok { if !ok {
return nil, fmt.Errorf("query result is not a matrix") 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 // Scalar returns a scalar value. An error is returned if
// the result was an error or the result value is not a scalar. // 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 { if r.Err != nil {
return nil, r.Err return nil, r.Err
} }
v, ok := r.Value.(*Scalar) v, ok := r.Value.(*model.Scalar)
if !ok { if !ok {
return nil, fmt.Errorf("query result is not a scalar") 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 { if err != nil {
return nil, err 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()) 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) 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 // At this point per query only one EvalStmt is evaluated. Alert and record
// statements are not handled by the Engine. // 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) ctx, cancel := context.WithTimeout(q.ng.baseCtx, ng.options.Timeout)
q.cancel = cancel 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. // 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() prepareTimer := query.stats.GetTimer(stats.TotalQueryPreparationTime).Start()
analyzeTimer := query.stats.GetTimer(stats.QueryAnalysisTime).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 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() evalTimer.Stop()
return val, nil return val, nil
} }
numSteps := int(s.End.Sub(s.Start) / s.Interval) numSteps := int(s.End.Sub(s.Start) / s.Interval)
// Range evaluation. // 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) { for ts := s.Start; !ts.After(s.End); ts = ts.Add(s.Interval) {
if err := contextDone(ctx, "range evaluation"); err != nil { 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) { switch v := val.(type) {
case *Scalar: case *model.Scalar:
// As the expression type does not change we can safely default to 0 // As the expression type does not change we can safely default to 0
// as the fingerprint for scalar expressions. // as the fingerprint for scalar expressions.
ss := sampleStreams[0] ss := sampleStreams[0]
if ss == nil { if ss == nil {
ss = &SampleStream{Values: make([]model.SamplePair, 0, numSteps)} ss = &sampleStream{Values: make([]model.SamplePair, 0, numSteps)}
sampleStreams[0] = ss sampleStreams[0] = ss
} }
ss.Values = append(ss.Values, model.SamplePair{ ss.Values = append(ss.Values, model.SamplePair{
Value: v.Value, Value: v.Value,
Timestamp: v.Timestamp, Timestamp: v.Timestamp,
}) })
case Vector: case vector:
for _, sample := range v { for _, sample := range v {
fp := sample.Metric.Metric.Fingerprint() fp := sample.Metric.Metric.Fingerprint()
ss := sampleStreams[fp] ss := sampleStreams[fp]
if ss == nil { if ss == nil {
ss = &SampleStream{ ss = &sampleStream{
Metric: sample.Metric, Metric: sample.Metric,
Values: make([]model.SamplePair, 0, numSteps), 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() appendTimer := query.stats.GetTimer(stats.ResultAppendTime).Start()
matrix := Matrix{} mat := matrix{}
for _, sampleStream := range sampleStreams { for _, ss := range sampleStreams {
matrix = append(matrix, sampleStream) mat = append(mat, ss)
} }
appendTimer.Stop() appendTimer.Stop()
@ -524,11 +487,14 @@ func (ng *Engine) execEvalStmt(ctx context.Context, query *query, s *EvalStmt) (
return nil, err return nil, err
} }
// Turn matrix type with protected metric into model.Matrix.
resMatrix := mat.value()
sortTimer := query.stats.GetTimer(stats.ResultSortTime).Start() sortTimer := query.stats.GetTimer(stats.ResultSortTime).Start()
sort.Sort(matrix) sort.Sort(resMatrix)
sortTimer.Stop() sortTimer.Stop()
return matrix, nil return resMatrix, nil
} }
// An evaluator evaluates given expressions at a fixed timestamp. It is attached to an // 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. // 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) val := ev.eval(e)
sv, ok := val.(*Scalar) sv, ok := val.(*model.Scalar)
if !ok { if !ok {
ev.errorf("expected scalar but got %s", val.Type()) 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. // 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) val := ev.eval(e)
vec, ok := val.(Vector) vec, ok := val.(vector)
if !ok { if !ok {
ev.errorf("expected vector but got %s", val.Type()) 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. // 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) val := ev.eval(e)
mat, ok := val.(Matrix) mat, ok := val.(matrix)
if !ok { if !ok {
ev.errorf("expected matrix but got %s", val.Type()) 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. // 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) ms, ok := e.(*MatrixSelector)
if !ok { if !ok {
ev.errorf("matrix bounds can only be evaluated for matrix selectors, got %T", e) 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. // 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) val := ev.eval(e)
sv, ok := val.(*String) sv, ok := val.(*model.String)
if !ok { if !ok {
ev.errorf("expected string but got %s", val.Type()) 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. // 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) val := ev.eval(e)
if val.Type() != t1 && val.Type() != t2 { if val.Type() != t1 && val.Type() != t2 {
ev.errorf("expected %s or %s but got %s", t1, t2, val.Type()) 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 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) defer ev.recover(&err)
return ev.eval(expr), nil return ev.eval(expr), nil
} }
// eval evaluates the given expression as the given AST expression node requires. // 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. // 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 { if err := contextDone(ev.ctx, "expression evaluation"); err != nil {
ev.error(err) ev.error(err)
} }
@ -656,30 +622,30 @@ func (ev *evaluator) eval(expr Expr) Value {
return ev.aggregation(e.Op, e.Grouping, e.KeepExtraLabels, vector) return ev.aggregation(e.Op, e.Grouping, e.KeepExtraLabels, vector)
case *BinaryExpr: case *BinaryExpr:
lhs := ev.evalOneOf(e.LHS, ExprScalar, ExprVector) lhs := ev.evalOneOf(e.LHS, model.ValScalar, model.ValVector)
rhs := ev.evalOneOf(e.RHS, ExprScalar, ExprVector) rhs := ev.evalOneOf(e.RHS, model.ValScalar, model.ValVector)
switch lt, rt := lhs.Type(), rhs.Type(); { switch lt, rt := lhs.Type(), rhs.Type(); {
case lt == ExprScalar && rt == ExprScalar: case lt == model.ValScalar && rt == model.ValScalar:
return &Scalar{ return &model.Scalar{
Value: scalarBinop(e.Op, lhs.(*Scalar).Value, rhs.(*Scalar).Value), Value: scalarBinop(e.Op, lhs.(*model.Scalar).Value, rhs.(*model.Scalar).Value),
Timestamp: ev.Timestamp, Timestamp: ev.Timestamp,
} }
case lt == ExprVector && rt == ExprVector: case lt == model.ValVector && rt == model.ValVector:
switch e.Op { switch e.Op {
case itemLAND: case itemLAND:
return ev.vectorAnd(lhs.(Vector), rhs.(Vector), e.VectorMatching) return ev.vectorAnd(lhs.(vector), rhs.(vector), e.VectorMatching)
case itemLOR: case itemLOR:
return ev.vectorOr(lhs.(Vector), rhs.(Vector), e.VectorMatching) return ev.vectorOr(lhs.(vector), rhs.(vector), e.VectorMatching)
default: 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: case lt == model.ValVector && rt == model.ValScalar:
return ev.vectorScalarBinop(e.Op, lhs.(Vector), rhs.(*Scalar), false) return ev.vectorScalarBinop(e.Op, lhs.(vector), rhs.(*model.Scalar), false)
case lt == ExprScalar && rt == ExprVector: case lt == model.ValScalar && rt == model.ValVector:
return ev.vectorScalarBinop(e.Op, rhs.(Vector), lhs.(*Scalar), true) return ev.vectorScalarBinop(e.Op, rhs.(vector), lhs.(*model.Scalar), true)
} }
case *Call: case *Call:
@ -689,22 +655,22 @@ func (ev *evaluator) eval(expr Expr) Value {
return ev.matrixSelector(e) return ev.matrixSelector(e)
case *NumberLiteral: case *NumberLiteral:
return &Scalar{Value: e.Val, Timestamp: ev.Timestamp} return &model.Scalar{Value: e.Val, Timestamp: ev.Timestamp}
case *ParenExpr: case *ParenExpr:
return ev.eval(e.Expr) return ev.eval(e.Expr)
case *StringLiteral: case *StringLiteral:
return &String{Value: e.Val, Timestamp: ev.Timestamp} return &model.String{Value: e.Val, Timestamp: ev.Timestamp}
case *UnaryExpr: case *UnaryExpr:
se := ev.evalOneOf(e.Expr, ExprScalar, ExprVector) se := ev.evalOneOf(e.Expr, model.ValScalar, model.ValVector)
// Only + and - are possible operators. // Only + and - are possible operators.
if e.Op == itemSUB { if e.Op == itemSUB {
switch v := se.(type) { switch v := se.(type) {
case *Scalar: case *model.Scalar:
v.Value = -v.Value v.Value = -v.Value
case Vector: case vector:
for i, sv := range v { for i, sv := range v {
v[i].Value = -sv.Value v[i].Value = -sv.Value
} }
@ -719,13 +685,13 @@ func (ev *evaluator) eval(expr Expr) Value {
} }
// vectorSelector evaluates a *VectorSelector expression. // vectorSelector evaluates a *VectorSelector expression.
func (ev *evaluator) vectorSelector(node *VectorSelector) Vector { func (ev *evaluator) vectorSelector(node *VectorSelector) vector {
vec := Vector{} vec := vector{}
for fp, it := range node.iterators { for fp, it := range node.iterators {
sampleCandidates := it.ValueAtTime(ev.Timestamp.Add(-node.Offset)) sampleCandidates := it.ValueAtTime(ev.Timestamp.Add(-node.Offset))
samplePair := chooseClosestSample(sampleCandidates, ev.Timestamp.Add(-node.Offset)) samplePair := chooseClosestSample(sampleCandidates, ev.Timestamp.Add(-node.Offset))
if samplePair != nil { if samplePair != nil {
vec = append(vec, &Sample{ vec = append(vec, &sample{
Metric: node.metrics[fp], Metric: node.metrics[fp],
Value: samplePair.Value, Value: samplePair.Value,
Timestamp: ev.Timestamp, Timestamp: ev.Timestamp,
@ -736,13 +702,13 @@ func (ev *evaluator) vectorSelector(node *VectorSelector) Vector {
} }
// matrixSelector evaluates a *MatrixSelector expression. // matrixSelector evaluates a *MatrixSelector expression.
func (ev *evaluator) matrixSelector(node *MatrixSelector) Matrix { func (ev *evaluator) matrixSelector(node *MatrixSelector) matrix {
interval := metric.Interval{ interval := metric.Interval{
OldestInclusive: ev.Timestamp.Add(-node.Range - node.Offset), OldestInclusive: ev.Timestamp.Add(-node.Range - node.Offset),
NewestInclusive: ev.Timestamp.Add(-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 { for fp, it := range node.iterators {
samplePairs := it.RangeValues(interval) samplePairs := it.RangeValues(interval)
if len(samplePairs) == 0 { if len(samplePairs) == 0 {
@ -755,46 +721,46 @@ func (ev *evaluator) matrixSelector(node *MatrixSelector) Matrix {
} }
} }
sampleStream := &SampleStream{ sampleStream := &sampleStream{
Metric: node.metrics[fp], Metric: node.metrics[fp],
Values: samplePairs, Values: samplePairs,
} }
sampleStreams = append(sampleStreams, sampleStream) sampleStreams = append(sampleStreams, sampleStream)
} }
return Matrix(sampleStreams) return matrix(sampleStreams)
} }
// matrixSelectorBounds evaluates the boundaries of a *MatrixSelector. // matrixSelectorBounds evaluates the boundaries of a *MatrixSelector.
func (ev *evaluator) matrixSelectorBounds(node *MatrixSelector) Matrix { func (ev *evaluator) matrixSelectorBounds(node *MatrixSelector) matrix {
interval := metric.Interval{ interval := metric.Interval{
OldestInclusive: ev.Timestamp.Add(-node.Range - node.Offset), OldestInclusive: ev.Timestamp.Add(-node.Range - node.Offset),
NewestInclusive: ev.Timestamp.Add(-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 { for fp, it := range node.iterators {
samplePairs := it.BoundaryValues(interval) samplePairs := it.BoundaryValues(interval)
if len(samplePairs) == 0 { if len(samplePairs) == 0 {
continue continue
} }
sampleStream := &SampleStream{ ss := &sampleStream{
Metric: node.metrics[fp], Metric: node.metrics[fp],
Values: samplePairs, 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 { if matching.Card != CardManyToMany {
panic("logical operations must always be many-to-many matching") panic("logical operations must always be many-to-many matching")
} }
// If no matching labels are specified, match by all labels. // If no matching labels are specified, match by all labels.
sigf := signatureFunc(matching.On...) sigf := signatureFunc(matching.On...)
var result Vector var result vector
// The set of signatures for the right-hand side vector. // The set of signatures for the right-hand side vector.
rightSigs := map[uint64]struct{}{} rightSigs := map[uint64]struct{}{}
// Add all rhs samples to a map so we can easily find matches later. // 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 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 { if matching.Card != CardManyToMany {
panic("logical operations must always be many-to-many matching") panic("logical operations must always be many-to-many matching")
} }
sigf := signatureFunc(matching.On...) sigf := signatureFunc(matching.On...)
var result Vector var result vector
leftSigs := map[uint64]struct{}{} leftSigs := map[uint64]struct{}{}
// Add everything from the left-hand-side vector. // Add everything from the left-hand-side vector.
for _, ls := range lhs { 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. // 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 { if matching.Card == CardManyToMany {
panic("many-to-many only allowed for AND and OR") panic("many-to-many only allowed for AND and OR")
} }
var ( var (
result = Vector{} result = vector{}
sigf = signatureFunc(matching.On...) sigf = signatureFunc(matching.On...)
resultLabels = append(matching.On, matching.Include...) 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. // 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. // Add all rhs samples to a map so we can easily find matches later.
for _, rs := range rhs { for _, rs := range rhs {
@ -911,7 +877,7 @@ func (ev *evaluator) vectorBinop(op itemType, lhs, rhs Vector, matching *VectorM
insertedSigs[insertSig] = struct{}{} insertedSigs[insertSig] = struct{}{}
} }
result = append(result, &Sample{ result = append(result, &sample{
Metric: metric, Metric: metric,
Value: value, Value: value,
Timestamp: ev.Timestamp, 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 // signatureFunc returns a function that calculates the signature for a metric
// based on the provided labels. // 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 { if len(labels) == 0 {
return func(m model.COWMetric) uint64 { return func(m metric.Metric) uint64 {
m.Del(model.MetricNameLabel) m.Del(model.MetricNameLabel)
return uint64(m.Metric.Fingerprint()) return uint64(m.Metric.Fingerprint())
} }
} }
return func(m model.COWMetric) uint64 { return func(m metric.Metric) uint64 {
return model.SignatureForLabels(m.Metric, labels...) return model.SignatureForLabels(m.Metric, labels...)
} }
} }
// resultMetric returns the metric for the given sample(s) based on the vector // resultMetric returns the metric for the given sample(s) based on the vector
// binary operation and the matching options. // 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 len(labels) == 0 {
if shouldDropMetricName(op) { if shouldDropMetricName(op) {
met.Del(model.MetricNameLabel) met.Del(model.MetricNameLabel)
@ -951,12 +917,12 @@ func resultMetric(met model.COWMetric, op itemType, labels ...model.LabelName) m
m[ln] = v 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. // vectorScalarBinop evaluates a binary operation between a vector and a scalar.
func (ev *evaluator) vectorScalarBinop(op itemType, lhs Vector, rhs *Scalar, swap bool) Vector { func (ev *evaluator) vectorScalarBinop(op itemType, lhs vector, rhs *model.Scalar, swap bool) vector {
vector := make(Vector, 0, len(lhs)) vec := make(vector, 0, len(lhs))
for _, lhsSample := range lhs { for _, lhsSample := range lhs {
lv, rv := lhsSample.Value, rhs.Value lv, rv := lhsSample.Value, rhs.Value
@ -971,10 +937,10 @@ func (ev *evaluator) vectorScalarBinop(op itemType, lhs Vector, rhs *Scalar, swa
if shouldDropMetricName(op) { if shouldDropMetricName(op) {
lhsSample.Metric.Del(model.MetricNameLabel) 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. // 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. // 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 { for label, value := range metric1.Metric {
if metric2.Metric[label] != value { if metric2.Metric[label] != value {
metric1.Del(label) metric1.Del(label)
@ -1052,29 +1018,29 @@ func labelIntersection(metric1, metric2 model.COWMetric) model.COWMetric {
} }
type groupedAggregation struct { type groupedAggregation struct {
labels model.COWMetric labels metric.Metric
value model.SampleValue value model.SampleValue
valuesSquaredSum model.SampleValue valuesSquaredSum model.SampleValue
groupCount int groupCount int
} }
// aggregation evaluates an aggregation operation on a vector. // 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{} result := map[uint64]*groupedAggregation{}
for _, sample := range vector { for _, sample := range vec {
groupingKey := model.SignatureForLabels(sample.Metric.Metric, grouping...) groupingKey := model.SignatureForLabels(sample.Metric.Metric, grouping...)
groupedResult, ok := result[groupingKey] groupedResult, ok := result[groupingKey]
// Add a new group if it doesn't exist. // Add a new group if it doesn't exist.
if !ok { if !ok {
var m model.COWMetric var m metric.Metric
if keepExtra { if keepExtra {
m = sample.Metric m = sample.Metric
m.Del(model.MetricNameLabel) m.Del(model.MetricNameLabel)
} else { } else {
m = model.COWMetric{ m = metric.Metric{
Metric: model.Metric{}, Metric: model.Metric{},
Copied: true, Copied: true,
} }
@ -1123,7 +1089,7 @@ func (ev *evaluator) aggregation(op itemType, grouping model.LabelNames, keepExt
} }
// Construct the result vector from the aggregated groups. // Construct the result vector from the aggregated groups.
resultVector := make(Vector, 0, len(result)) resultVector := make(vector, 0, len(result))
for _, aggr := range result { for _, aggr := range result {
switch op { switch op {
@ -1140,7 +1106,7 @@ func (ev *evaluator) aggregation(op itemType, grouping model.LabelNames, keepExt
default: default:
// For other aggregations, we already have the right value. // For other aggregations, we already have the right value.
} }
sample := &Sample{ sample := &sample{
Metric: aggr.labels, Metric: aggr.labels,
Value: aggr.value, Value: aggr.value,
Timestamp: ev.Timestamp, Timestamp: ev.Timestamp,

View file

@ -30,29 +30,29 @@ import (
// used by function nodes. // used by function nodes.
type Function struct { type Function struct {
Name string Name string
ArgTypes []ExprType ArgTypes []model.ValueType
OptionalArgs int OptionalArgs int
ReturnType ExprType ReturnType model.ValueType
Call func(ev *evaluator, args Expressions) Value Call func(ev *evaluator, args Expressions) model.Value
} }
// === time() model.SampleValue === // === time() model.SampleValue ===
func funcTime(ev *evaluator, args Expressions) Value { func funcTime(ev *evaluator, args Expressions) model.Value {
return &Scalar{ return &model.Scalar{
Value: model.SampleValue(ev.Timestamp.Unix()), Value: model.SampleValue(ev.Timestamp.Unix()),
Timestamp: ev.Timestamp, Timestamp: ev.Timestamp,
} }
} }
// === delta(matrix ExprMatrix, isCounter=0 ExprScalar) Vector === // === delta(matrix model.ValMatrix, isCounter=0 model.ValScalar) Vector ===
func funcDelta(ev *evaluator, args Expressions) Value { func funcDelta(ev *evaluator, args Expressions) model.Value {
isCounter := len(args) >= 2 && ev.evalInt(args[1]) > 0 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 // If we treat these metrics as counters, we need to fetch all values
// in the interval to find breaks in the timeseries' monotonicity. // in the interval to find breaks in the timeseries' monotonicity.
// I.e. if a counter resets, we want to ignore that reset. // I.e. if a counter resets, we want to ignore that reset.
var matrixValue Matrix var matrixValue matrix
if isCounter { if isCounter {
matrixValue = ev.evalMatrix(args[0]) matrixValue = ev.evalMatrix(args[0])
} else { } else {
@ -65,8 +65,10 @@ func funcDelta(ev *evaluator, args Expressions) Value {
continue continue
} }
counterCorrection := model.SampleValue(0) var (
lastValue := model.SampleValue(0) counterCorrection model.SampleValue
lastValue model.SampleValue
)
for _, sample := range samples.Values { for _, sample := range samples.Values {
currentValue := sample.Value currentValue := sample.Value
if isCounter && currentValue < lastValue { if isCounter && currentValue < lastValue {
@ -93,7 +95,7 @@ func funcDelta(ev *evaluator, args Expressions) Value {
intervalCorrection := model.SampleValue(targetInterval) / model.SampleValue(sampledInterval) intervalCorrection := model.SampleValue(targetInterval) / model.SampleValue(sampledInterval)
resultValue *= intervalCorrection resultValue *= intervalCorrection
resultSample := &Sample{ resultSample := &sample{
Metric: samples.Metric, Metric: samples.Metric,
Value: resultValue, Value: resultValue,
Timestamp: ev.Timestamp, Timestamp: ev.Timestamp,
@ -104,12 +106,12 @@ func funcDelta(ev *evaluator, args Expressions) Value {
return resultVector return resultVector
} }
// === rate(node ExprMatrix) Vector === // === rate(node model.ValMatrix) Vector ===
func funcRate(ev *evaluator, args Expressions) Value { func funcRate(ev *evaluator, args Expressions) model.Value {
args = append(args, &NumberLiteral{1}) 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 // MatrixSelector exists). Find a better way of getting the duration of a
// matrix, such as looking at the samples themselves. // matrix, such as looking at the samples themselves.
interval := args[0].(*MatrixSelector).Range interval := args[0].(*MatrixSelector).Range
@ -119,38 +121,38 @@ func funcRate(ev *evaluator, args Expressions) Value {
return vector return vector
} }
// === increase(node ExprMatrix) Vector === // === increase(node model.ValMatrix) Vector ===
func funcIncrease(ev *evaluator, args Expressions) Value { func funcIncrease(ev *evaluator, args Expressions) model.Value {
args = append(args, &NumberLiteral{1}) args = append(args, &NumberLiteral{1})
vector := funcDelta(ev, args).(Vector) return funcDelta(ev, args).(vector)
return vector
} }
// === sort(node ExprVector) Vector === // === sort(node model.ValVector) Vector ===
func funcSort(ev *evaluator, args Expressions) Value { func funcSort(ev *evaluator, args Expressions) model.Value {
byValueSorter := vectorByValueHeap(ev.evalVector(args[0])) byValueSorter := vectorByValueHeap(ev.evalVector(args[0]))
sort.Sort(byValueSorter) sort.Sort(byValueSorter)
return Vector(byValueSorter) return vector(byValueSorter)
} }
// === sortDesc(node ExprVector) Vector === // === sortDesc(node model.ValVector) Vector ===
func funcSortDesc(ev *evaluator, args Expressions) Value { func funcSortDesc(ev *evaluator, args Expressions) model.Value {
byValueSorter := vectorByValueHeap(ev.evalVector(args[0])) byValueSorter := vectorByValueHeap(ev.evalVector(args[0]))
sort.Sort(sort.Reverse(byValueSorter)) sort.Sort(sort.Reverse(byValueSorter))
return Vector(byValueSorter)
return vector(byValueSorter)
} }
// === topk(k ExprScalar, node ExprVector) Vector === // === topk(k model.ValScalar, node model.ValVector) Vector ===
func funcTopk(ev *evaluator, args Expressions) Value { func funcTopk(ev *evaluator, args Expressions) model.Value {
k := ev.evalInt(args[0]) k := ev.evalInt(args[0])
if k < 1 { if k < 1 {
return Vector{} return vector{}
} }
vector := ev.evalVector(args[1]) vec := ev.evalVector(args[1])
topk := make(vectorByValueHeap, 0, k) 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 || topk[0].Value < el.Value {
if len(topk) == k { if len(topk) == k {
heap.Pop(&topk) heap.Pop(&topk)
@ -159,21 +161,21 @@ func funcTopk(ev *evaluator, args Expressions) Value {
} }
} }
sort.Sort(sort.Reverse(topk)) sort.Sort(sort.Reverse(topk))
return Vector(topk) return vector(topk)
} }
// === bottomk(k ExprScalar, node ExprVector) Vector === // === bottomk(k model.ValScalar, node model.ValVector) Vector ===
func funcBottomk(ev *evaluator, args Expressions) Value { func funcBottomk(ev *evaluator, args Expressions) model.Value {
k := ev.evalInt(args[0]) k := ev.evalInt(args[0])
if k < 1 { if k < 1 {
return Vector{} return vector{}
} }
vector := ev.evalVector(args[1]) vec := ev.evalVector(args[1])
bottomk := make(vectorByValueHeap, 0, k) bottomk := make(vectorByValueHeap, 0, k)
bkHeap := reverseHeap{Interface: &bottomk} 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 || bottomk[0].Value > el.Value {
if len(bottomk) == k { if len(bottomk) == k {
heap.Pop(&bkHeap) heap.Pop(&bkHeap)
@ -182,17 +184,17 @@ func funcBottomk(ev *evaluator, args Expressions) Value {
} }
} }
sort.Sort(bottomk) sort.Sort(bottomk)
return Vector(bottomk) return vector(bottomk)
} }
// === drop_common_labels(node ExprVector) Vector === // === drop_common_labels(node model.ValVector) Vector ===
func funcDropCommonLabels(ev *evaluator, args Expressions) Value { func funcDropCommonLabels(ev *evaluator, args Expressions) model.Value {
vector := ev.evalVector(args[0]) vec := ev.evalVector(args[0])
if len(vector) < 1 { if len(vec) < 1 {
return Vector{} return vector{}
} }
common := model.LabelSet{} 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? // TODO(julius): Should we also drop common metric names?
if k == model.MetricNameLabel { if k == model.MetricNameLabel {
continue continue
@ -200,7 +202,7 @@ func funcDropCommonLabels(ev *evaluator, args Expressions) Value {
common[k] = v common[k] = v
} }
for _, el := range vector[1:] { for _, el := range vec[1:] {
for k, v := range common { for k, v := range common {
if el.Metric.Metric[k] != v { if el.Metric.Metric[k] != v {
// Deletion of map entries while iterating over them is safe. // 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 { for k := range el.Metric.Metric {
if _, ok := common[k]; ok { if _, ok := common[k]; ok {
el.Metric.Del(k) el.Metric.Del(k)
} }
} }
} }
return vector return vec
} }
// === round(vector ExprVector, toNearest=1 Scalar) Vector === // === round(vector model.ValVector, toNearest=1 Scalar) Vector ===
func funcRound(ev *evaluator, args Expressions) Value { func funcRound(ev *evaluator, args Expressions) model.Value {
// round returns a number rounded to toNearest. // round returns a number rounded to toNearest.
// Ties are solved by rounding up. // Ties are solved by rounding up.
toNearest := float64(1) 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. // Invert as it seems to cause fewer floating point accuracy issues.
toNearestInverse := 1.0 / toNearest toNearestInverse := 1.0 / toNearest
vector := ev.evalVector(args[0]) vec := ev.evalVector(args[0])
for _, el := range vector { for _, el := range vec {
el.Metric.Del(model.MetricNameLabel) el.Metric.Del(model.MetricNameLabel)
el.Value = model.SampleValue(math.Floor(float64(el.Value)*toNearestInverse+0.5) / toNearestInverse) el.Value = model.SampleValue(math.Floor(float64(el.Value)*toNearestInverse+0.5) / toNearestInverse)
} }
return vector return vec
} }
// === scalar(node ExprVector) Scalar === // === scalar(node model.ValVector) Scalar ===
func funcScalar(ev *evaluator, args Expressions) Value { func funcScalar(ev *evaluator, args Expressions) model.Value {
v := ev.evalVector(args[0]) v := ev.evalVector(args[0])
if len(v) != 1 { 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 === // === count_scalar(vector model.ValVector) model.SampleValue ===
func funcCountScalar(ev *evaluator, args Expressions) Value { func funcCountScalar(ev *evaluator, args Expressions) model.Value {
return &Scalar{ return &model.Scalar{
Value: model.SampleValue(len(ev.evalVector(args[0]))), Value: model.SampleValue(len(ev.evalVector(args[0]))),
Timestamp: ev.Timestamp, Timestamp: ev.Timestamp,
} }
} }
func aggrOverTime(ev *evaluator, args Expressions, aggrFn func([]model.SamplePair) model.SampleValue) Value { func aggrOverTime(ev *evaluator, args Expressions, aggrFn func([]model.SamplePair) model.SampleValue) model.Value {
matrix := ev.evalMatrix(args[0]) mat := ev.evalMatrix(args[0])
resultVector := Vector{} resultVector := vector{}
for _, el := range matrix { for _, el := range mat {
if len(el.Values) == 0 { if len(el.Values) == 0 {
continue continue
} }
el.Metric.Del(model.MetricNameLabel) el.Metric.Del(model.MetricNameLabel)
resultVector = append(resultVector, &Sample{ resultVector = append(resultVector, &sample{
Metric: el.Metric, Metric: el.Metric,
Value: aggrFn(el.Values), Value: aggrFn(el.Values),
Timestamp: ev.Timestamp, Timestamp: ev.Timestamp,
@ -277,8 +279,8 @@ func aggrOverTime(ev *evaluator, args Expressions, aggrFn func([]model.SamplePai
return resultVector return resultVector
} }
// === avg_over_time(matrix ExprMatrix) Vector === // === avg_over_time(matrix model.ValMatrix) Vector ===
func funcAvgOverTime(ev *evaluator, args Expressions) Value { func funcAvgOverTime(ev *evaluator, args Expressions) model.Value {
return aggrOverTime(ev, args, func(values []model.SamplePair) model.SampleValue { return aggrOverTime(ev, args, func(values []model.SamplePair) model.SampleValue {
var sum model.SampleValue var sum model.SampleValue
for _, v := range values { for _, v := range values {
@ -288,15 +290,15 @@ func funcAvgOverTime(ev *evaluator, args Expressions) Value {
}) })
} }
// === count_over_time(matrix ExprMatrix) Vector === // === count_over_time(matrix model.ValMatrix) Vector ===
func funcCountOverTime(ev *evaluator, args Expressions) Value { func funcCountOverTime(ev *evaluator, args Expressions) model.Value {
return aggrOverTime(ev, args, func(values []model.SamplePair) model.SampleValue { return aggrOverTime(ev, args, func(values []model.SamplePair) model.SampleValue {
return model.SampleValue(len(values)) return model.SampleValue(len(values))
}) })
} }
// === floor(vector ExprVector) Vector === // === floor(vector model.ValVector) Vector ===
func funcFloor(ev *evaluator, args Expressions) Value { func funcFloor(ev *evaluator, args Expressions) model.Value {
vector := ev.evalVector(args[0]) vector := ev.evalVector(args[0])
for _, el := range vector { for _, el := range vector {
el.Metric.Del(model.MetricNameLabel) el.Metric.Del(model.MetricNameLabel)
@ -305,8 +307,8 @@ func funcFloor(ev *evaluator, args Expressions) Value {
return vector return vector
} }
// === max_over_time(matrix ExprMatrix) Vector === // === max_over_time(matrix model.ValMatrix) Vector ===
func funcMaxOverTime(ev *evaluator, args Expressions) Value { func funcMaxOverTime(ev *evaluator, args Expressions) model.Value {
return aggrOverTime(ev, args, func(values []model.SamplePair) model.SampleValue { return aggrOverTime(ev, args, func(values []model.SamplePair) model.SampleValue {
max := math.Inf(-1) max := math.Inf(-1)
for _, v := range values { for _, v := range values {
@ -316,8 +318,8 @@ func funcMaxOverTime(ev *evaluator, args Expressions) Value {
}) })
} }
// === min_over_time(matrix ExprMatrix) Vector === // === min_over_time(matrix model.ValMatrix) Vector ===
func funcMinOverTime(ev *evaluator, args Expressions) Value { func funcMinOverTime(ev *evaluator, args Expressions) model.Value {
return aggrOverTime(ev, args, func(values []model.SamplePair) model.SampleValue { return aggrOverTime(ev, args, func(values []model.SamplePair) model.SampleValue {
min := math.Inf(1) min := math.Inf(1)
for _, v := range values { for _, v := range values {
@ -327,8 +329,8 @@ func funcMinOverTime(ev *evaluator, args Expressions) Value {
}) })
} }
// === sum_over_time(matrix ExprMatrix) Vector === // === sum_over_time(matrix model.ValMatrix) Vector ===
func funcSumOverTime(ev *evaluator, args Expressions) Value { func funcSumOverTime(ev *evaluator, args Expressions) model.Value {
return aggrOverTime(ev, args, func(values []model.SamplePair) model.SampleValue { return aggrOverTime(ev, args, func(values []model.SamplePair) model.SampleValue {
var sum model.SampleValue var sum model.SampleValue
for _, v := range values { for _, v := range values {
@ -338,8 +340,8 @@ func funcSumOverTime(ev *evaluator, args Expressions) Value {
}) })
} }
// === abs(vector ExprVector) Vector === // === abs(vector model.ValVector) Vector ===
func funcAbs(ev *evaluator, args Expressions) Value { func funcAbs(ev *evaluator, args Expressions) model.Value {
vector := ev.evalVector(args[0]) vector := ev.evalVector(args[0])
for _, el := range vector { for _, el := range vector {
el.Metric.Del(model.MetricNameLabel) el.Metric.Del(model.MetricNameLabel)
@ -348,10 +350,10 @@ func funcAbs(ev *evaluator, args Expressions) Value {
return vector return vector
} }
// === absent(vector ExprVector) Vector === // === absent(vector model.ValVector) Vector ===
func funcAbsent(ev *evaluator, args Expressions) Value { func funcAbsent(ev *evaluator, args Expressions) model.Value {
if len(ev.evalVector(args[0])) > 0 { if len(ev.evalVector(args[0])) > 0 {
return Vector{} return vector{}
} }
m := model.Metric{} m := model.Metric{}
if vs, ok := args[0].(*VectorSelector); ok { if vs, ok := args[0].(*VectorSelector); ok {
@ -361,9 +363,9 @@ func funcAbsent(ev *evaluator, args Expressions) Value {
} }
} }
} }
return Vector{ return vector{
&Sample{ &sample{
Metric: model.COWMetric{ Metric: metric.Metric{
Metric: m, Metric: m,
Copied: true, Copied: true,
}, },
@ -373,8 +375,8 @@ func funcAbsent(ev *evaluator, args Expressions) Value {
} }
} }
// === ceil(vector ExprVector) Vector === // === ceil(vector model.ValVector) Vector ===
func funcCeil(ev *evaluator, args Expressions) Value { func funcCeil(ev *evaluator, args Expressions) model.Value {
vector := ev.evalVector(args[0]) vector := ev.evalVector(args[0])
for _, el := range vector { for _, el := range vector {
el.Metric.Del(model.MetricNameLabel) el.Metric.Del(model.MetricNameLabel)
@ -383,8 +385,8 @@ func funcCeil(ev *evaluator, args Expressions) Value {
return vector return vector
} }
// === exp(vector ExprVector) Vector === // === exp(vector model.ValVector) Vector ===
func funcExp(ev *evaluator, args Expressions) Value { func funcExp(ev *evaluator, args Expressions) model.Value {
vector := ev.evalVector(args[0]) vector := ev.evalVector(args[0])
for _, el := range vector { for _, el := range vector {
el.Metric.Del(model.MetricNameLabel) el.Metric.Del(model.MetricNameLabel)
@ -394,7 +396,7 @@ func funcExp(ev *evaluator, args Expressions) Value {
} }
// === sqrt(vector VectorNode) Vector === // === sqrt(vector VectorNode) Vector ===
func funcSqrt(ev *evaluator, args Expressions) Value { func funcSqrt(ev *evaluator, args Expressions) model.Value {
vector := ev.evalVector(args[0]) vector := ev.evalVector(args[0])
for _, el := range vector { for _, el := range vector {
el.Metric.Del(model.MetricNameLabel) el.Metric.Del(model.MetricNameLabel)
@ -403,8 +405,8 @@ func funcSqrt(ev *evaluator, args Expressions) Value {
return vector return vector
} }
// === ln(vector ExprVector) Vector === // === ln(vector model.ValVector) Vector ===
func funcLn(ev *evaluator, args Expressions) Value { func funcLn(ev *evaluator, args Expressions) model.Value {
vector := ev.evalVector(args[0]) vector := ev.evalVector(args[0])
for _, el := range vector { for _, el := range vector {
el.Metric.Del(model.MetricNameLabel) el.Metric.Del(model.MetricNameLabel)
@ -413,8 +415,8 @@ func funcLn(ev *evaluator, args Expressions) Value {
return vector return vector
} }
// === log2(vector ExprVector) Vector === // === log2(vector model.ValVector) Vector ===
func funcLog2(ev *evaluator, args Expressions) Value { func funcLog2(ev *evaluator, args Expressions) model.Value {
vector := ev.evalVector(args[0]) vector := ev.evalVector(args[0])
for _, el := range vector { for _, el := range vector {
el.Metric.Del(model.MetricNameLabel) el.Metric.Del(model.MetricNameLabel)
@ -423,8 +425,8 @@ func funcLog2(ev *evaluator, args Expressions) Value {
return vector return vector
} }
// === log10(vector ExprVector) Vector === // === log10(vector model.ValVector) Vector ===
func funcLog10(ev *evaluator, args Expressions) Value { func funcLog10(ev *evaluator, args Expressions) model.Value {
vector := ev.evalVector(args[0]) vector := ev.evalVector(args[0])
for _, el := range vector { for _, el := range vector {
el.Metric.Del(model.MetricNameLabel) el.Metric.Del(model.MetricNameLabel)
@ -433,12 +435,12 @@ func funcLog10(ev *evaluator, args Expressions) Value {
return vector return vector
} }
// === deriv(node ExprMatrix) Vector === // === deriv(node model.ValMatrix) Vector ===
func funcDeriv(ev *evaluator, args Expressions) Value { func funcDeriv(ev *evaluator, args Expressions) model.Value {
resultVector := Vector{} resultVector := vector{}
matrix := ev.evalMatrix(args[0]) 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. // No sense in trying to compute a derivative without at least two points.
// Drop this vector element. // Drop this vector element.
if len(samples.Values) < 2 { if len(samples.Values) < 2 {
@ -464,7 +466,7 @@ func funcDeriv(ev *evaluator, args Expressions) Value {
resultValue := numerator / denominator resultValue := numerator / denominator
resultSample := &Sample{ resultSample := &sample{
Metric: samples.Metric, Metric: samples.Metric,
Value: resultValue, Value: resultValue,
Timestamp: ev.Timestamp, Timestamp: ev.Timestamp,
@ -475,9 +477,9 @@ func funcDeriv(ev *evaluator, args Expressions) Value {
return resultVector return resultVector
} }
// === predict_linear(node ExprMatrix, k ExprScalar) Vector === // === predict_linear(node model.ValMatrix, k model.ValScalar) Vector ===
func funcPredictLinear(ev *evaluator, args Expressions) Value { func funcPredictLinear(ev *evaluator, args Expressions) model.Value {
vector := funcDeriv(ev, args[0:1]).(Vector) vec := funcDeriv(ev, args[0:1]).(vector)
duration := model.SampleValue(model.SampleValue(ev.evalFloat(args[1]))) duration := model.SampleValue(model.SampleValue(ev.evalFloat(args[1])))
excludedLabels := map[model.LabelName]struct{}{ excludedLabels := map[model.LabelName]struct{}{
@ -486,14 +488,14 @@ func funcPredictLinear(ev *evaluator, args Expressions) Value {
// Calculate predicted delta over the duration. // Calculate predicted delta over the duration.
signatureToDelta := map[uint64]model.SampleValue{} signatureToDelta := map[uint64]model.SampleValue{}
for _, el := range vector { for _, el := range vec {
signature := model.SignatureWithoutLabels(el.Metric.Metric, excludedLabels) signature := model.SignatureWithoutLabels(el.Metric.Metric, excludedLabels)
signatureToDelta[signature] = el.Value * duration signatureToDelta[signature] = el.Value * duration
} }
// add predicted delta to last value. // add predicted delta to last value.
matrixBounds := ev.evalMatrixBounds(args[0]) matrixBounds := ev.evalMatrixBounds(args[0])
outVec := make(Vector, 0, len(signatureToDelta)) outVec := make(vector, 0, len(signatureToDelta))
for _, samples := range matrixBounds { for _, samples := range matrixBounds {
if len(samples.Values) < 2 { if len(samples.Values) < 2 {
continue continue
@ -502,7 +504,7 @@ func funcPredictLinear(ev *evaluator, args Expressions) Value {
delta, ok := signatureToDelta[signature] delta, ok := signatureToDelta[signature]
if ok { if ok {
samples.Metric.Del(model.MetricNameLabel) samples.Metric.Del(model.MetricNameLabel)
outVec = append(outVec, &Sample{ outVec = append(outVec, &sample{
Metric: samples.Metric, Metric: samples.Metric,
Value: delta + samples.Values[1].Value, Value: delta + samples.Values[1].Value,
Timestamp: ev.Timestamp, Timestamp: ev.Timestamp,
@ -512,12 +514,12 @@ func funcPredictLinear(ev *evaluator, args Expressions) Value {
return outVec return outVec
} }
// === histogram_quantile(k ExprScalar, vector ExprVector) Vector === // === histogram_quantile(k model.ValScalar, vector model.ValVector) Vector ===
func funcHistogramQuantile(ev *evaluator, args Expressions) Value { func funcHistogramQuantile(ev *evaluator, args Expressions) model.Value {
q := model.SampleValue(ev.evalFloat(args[0])) q := model.SampleValue(ev.evalFloat(args[0]))
inVec := ev.evalVector(args[1]) inVec := ev.evalVector(args[1])
outVec := Vector{} outVec := vector{}
signatureToMetricWithBuckets := map[uint64]*metricWithBuckets{} signatureToMetricWithBuckets := map[uint64]*metricWithBuckets{}
for _, el := range inVec { for _, el := range inVec {
upperBound, err := strconv.ParseFloat( upperBound, err := strconv.ParseFloat(
@ -540,7 +542,7 @@ func funcHistogramQuantile(ev *evaluator, args Expressions) Value {
} }
for _, mb := range signatureToMetricWithBuckets { for _, mb := range signatureToMetricWithBuckets {
outVec = append(outVec, &Sample{ outVec = append(outVec, &sample{
Metric: mb.metric, Metric: mb.metric,
Value: model.SampleValue(quantile(q, mb.buckets)), Value: model.SampleValue(quantile(q, mb.buckets)),
Timestamp: ev.Timestamp, Timestamp: ev.Timestamp,
@ -550,10 +552,10 @@ func funcHistogramQuantile(ev *evaluator, args Expressions) Value {
return outVec return outVec
} }
// === resets(matrix ExprMatrix) Vector === // === resets(matrix model.ValMatrix) Vector ===
func funcResets(ev *evaluator, args Expressions) Value { func funcResets(ev *evaluator, args Expressions) model.Value {
in := ev.evalMatrix(args[0]) in := ev.evalMatrix(args[0])
out := make(Vector, 0, len(in)) out := make(vector, 0, len(in))
for _, samples := range in { for _, samples := range in {
resets := 0 resets := 0
@ -566,7 +568,7 @@ func funcResets(ev *evaluator, args Expressions) Value {
prev = current prev = current
} }
rs := &Sample{ rs := &sample{
Metric: samples.Metric, Metric: samples.Metric,
Value: model.SampleValue(resets), Value: model.SampleValue(resets),
Timestamp: ev.Timestamp, Timestamp: ev.Timestamp,
@ -577,10 +579,10 @@ func funcResets(ev *evaluator, args Expressions) Value {
return out return out
} }
// === changes(matrix ExprMatrix) Vector === // === changes(matrix model.ValMatrix) Vector ===
func funcChanges(ev *evaluator, args Expressions) Value { func funcChanges(ev *evaluator, args Expressions) model.Value {
in := ev.evalMatrix(args[0]) in := ev.evalMatrix(args[0])
out := make(Vector, 0, len(in)) out := make(vector, 0, len(in))
for _, samples := range in { for _, samples := range in {
changes := 0 changes := 0
@ -593,7 +595,7 @@ func funcChanges(ev *evaluator, args Expressions) Value {
prev = current prev = current
} }
rs := &Sample{ rs := &sample{
Metric: samples.Metric, Metric: samples.Metric,
Value: model.SampleValue(changes), Value: model.SampleValue(changes),
Timestamp: ev.Timestamp, Timestamp: ev.Timestamp,
@ -604,8 +606,8 @@ func funcChanges(ev *evaluator, args Expressions) Value {
return out return out
} }
// === label_replace(vector ExprVector, dst_label, replacement, src_labelname, regex ExprString) Vector === // === label_replace(vector model.ValVector, dst_label, replacement, src_labelname, regex model.ValString) Vector ===
func funcLabelReplace(ev *evaluator, args Expressions) Value { func funcLabelReplace(ev *evaluator, args Expressions) model.Value {
var ( var (
vector = ev.evalVector(args[0]) vector = ev.evalVector(args[0])
dst = model.LabelName(ev.evalString(args[1]).Value) dst = model.LabelName(ev.evalString(args[1]).Value)
@ -651,196 +653,196 @@ func funcLabelReplace(ev *evaluator, args Expressions) Value {
var functions = map[string]*Function{ var functions = map[string]*Function{
"abs": { "abs": {
Name: "abs", Name: "abs",
ArgTypes: []ExprType{ExprVector}, ArgTypes: []model.ValueType{model.ValVector},
ReturnType: ExprVector, ReturnType: model.ValVector,
Call: funcAbs, Call: funcAbs,
}, },
"absent": { "absent": {
Name: "absent", Name: "absent",
ArgTypes: []ExprType{ExprVector}, ArgTypes: []model.ValueType{model.ValVector},
ReturnType: ExprVector, ReturnType: model.ValVector,
Call: funcAbsent, Call: funcAbsent,
}, },
"increase": { "increase": {
Name: "increase", Name: "increase",
ArgTypes: []ExprType{ExprMatrix}, ArgTypes: []model.ValueType{model.ValMatrix},
ReturnType: ExprVector, ReturnType: model.ValVector,
Call: funcIncrease, Call: funcIncrease,
}, },
"avg_over_time": { "avg_over_time": {
Name: "avg_over_time", Name: "avg_over_time",
ArgTypes: []ExprType{ExprMatrix}, ArgTypes: []model.ValueType{model.ValMatrix},
ReturnType: ExprVector, ReturnType: model.ValVector,
Call: funcAvgOverTime, Call: funcAvgOverTime,
}, },
"bottomk": { "bottomk": {
Name: "bottomk", Name: "bottomk",
ArgTypes: []ExprType{ExprScalar, ExprVector}, ArgTypes: []model.ValueType{model.ValScalar, model.ValVector},
ReturnType: ExprVector, ReturnType: model.ValVector,
Call: funcBottomk, Call: funcBottomk,
}, },
"ceil": { "ceil": {
Name: "ceil", Name: "ceil",
ArgTypes: []ExprType{ExprVector}, ArgTypes: []model.ValueType{model.ValVector},
ReturnType: ExprVector, ReturnType: model.ValVector,
Call: funcCeil, Call: funcCeil,
}, },
"changes": { "changes": {
Name: "changes", Name: "changes",
ArgTypes: []ExprType{ExprMatrix}, ArgTypes: []model.ValueType{model.ValMatrix},
ReturnType: ExprVector, ReturnType: model.ValVector,
Call: funcChanges, Call: funcChanges,
}, },
"count_over_time": { "count_over_time": {
Name: "count_over_time", Name: "count_over_time",
ArgTypes: []ExprType{ExprMatrix}, ArgTypes: []model.ValueType{model.ValMatrix},
ReturnType: ExprVector, ReturnType: model.ValVector,
Call: funcCountOverTime, Call: funcCountOverTime,
}, },
"count_scalar": { "count_scalar": {
Name: "count_scalar", Name: "count_scalar",
ArgTypes: []ExprType{ExprVector}, ArgTypes: []model.ValueType{model.ValVector},
ReturnType: ExprScalar, ReturnType: model.ValScalar,
Call: funcCountScalar, Call: funcCountScalar,
}, },
"delta": { "delta": {
Name: "delta", Name: "delta",
ArgTypes: []ExprType{ExprMatrix, ExprScalar}, ArgTypes: []model.ValueType{model.ValMatrix, model.ValScalar},
OptionalArgs: 1, // The 2nd argument is deprecated. OptionalArgs: 1, // The 2nd argument is deprecated.
ReturnType: ExprVector, ReturnType: model.ValVector,
Call: funcDelta, Call: funcDelta,
}, },
"deriv": { "deriv": {
Name: "deriv", Name: "deriv",
ArgTypes: []ExprType{ExprMatrix}, ArgTypes: []model.ValueType{model.ValMatrix},
ReturnType: ExprVector, ReturnType: model.ValVector,
Call: funcDeriv, Call: funcDeriv,
}, },
"drop_common_labels": { "drop_common_labels": {
Name: "drop_common_labels", Name: "drop_common_labels",
ArgTypes: []ExprType{ExprVector}, ArgTypes: []model.ValueType{model.ValVector},
ReturnType: ExprVector, ReturnType: model.ValVector,
Call: funcDropCommonLabels, Call: funcDropCommonLabels,
}, },
"exp": { "exp": {
Name: "exp", Name: "exp",
ArgTypes: []ExprType{ExprVector}, ArgTypes: []model.ValueType{model.ValVector},
ReturnType: ExprVector, ReturnType: model.ValVector,
Call: funcExp, Call: funcExp,
}, },
"floor": { "floor": {
Name: "floor", Name: "floor",
ArgTypes: []ExprType{ExprVector}, ArgTypes: []model.ValueType{model.ValVector},
ReturnType: ExprVector, ReturnType: model.ValVector,
Call: funcFloor, Call: funcFloor,
}, },
"histogram_quantile": { "histogram_quantile": {
Name: "histogram_quantile", Name: "histogram_quantile",
ArgTypes: []ExprType{ExprScalar, ExprVector}, ArgTypes: []model.ValueType{model.ValScalar, model.ValVector},
ReturnType: ExprVector, ReturnType: model.ValVector,
Call: funcHistogramQuantile, Call: funcHistogramQuantile,
}, },
"label_replace": { "label_replace": {
Name: "label_replace", Name: "label_replace",
ArgTypes: []ExprType{ExprVector, ExprString, ExprString, ExprString, ExprString}, ArgTypes: []model.ValueType{model.ValVector, model.ValString, model.ValString, model.ValString, model.ValString},
ReturnType: ExprVector, ReturnType: model.ValVector,
Call: funcLabelReplace, Call: funcLabelReplace,
}, },
"ln": { "ln": {
Name: "ln", Name: "ln",
ArgTypes: []ExprType{ExprVector}, ArgTypes: []model.ValueType{model.ValVector},
ReturnType: ExprVector, ReturnType: model.ValVector,
Call: funcLn, Call: funcLn,
}, },
"log10": { "log10": {
Name: "log10", Name: "log10",
ArgTypes: []ExprType{ExprVector}, ArgTypes: []model.ValueType{model.ValVector},
ReturnType: ExprVector, ReturnType: model.ValVector,
Call: funcLog10, Call: funcLog10,
}, },
"log2": { "log2": {
Name: "log2", Name: "log2",
ArgTypes: []ExprType{ExprVector}, ArgTypes: []model.ValueType{model.ValVector},
ReturnType: ExprVector, ReturnType: model.ValVector,
Call: funcLog2, Call: funcLog2,
}, },
"max_over_time": { "max_over_time": {
Name: "max_over_time", Name: "max_over_time",
ArgTypes: []ExprType{ExprMatrix}, ArgTypes: []model.ValueType{model.ValMatrix},
ReturnType: ExprVector, ReturnType: model.ValVector,
Call: funcMaxOverTime, Call: funcMaxOverTime,
}, },
"min_over_time": { "min_over_time": {
Name: "min_over_time", Name: "min_over_time",
ArgTypes: []ExprType{ExprMatrix}, ArgTypes: []model.ValueType{model.ValMatrix},
ReturnType: ExprVector, ReturnType: model.ValVector,
Call: funcMinOverTime, Call: funcMinOverTime,
}, },
"predict_linear": { "predict_linear": {
Name: "predict_linear", Name: "predict_linear",
ArgTypes: []ExprType{ExprMatrix, ExprScalar}, ArgTypes: []model.ValueType{model.ValMatrix, model.ValScalar},
ReturnType: ExprVector, ReturnType: model.ValVector,
Call: funcPredictLinear, Call: funcPredictLinear,
}, },
"rate": { "rate": {
Name: "rate", Name: "rate",
ArgTypes: []ExprType{ExprMatrix}, ArgTypes: []model.ValueType{model.ValMatrix},
ReturnType: ExprVector, ReturnType: model.ValVector,
Call: funcRate, Call: funcRate,
}, },
"resets": { "resets": {
Name: "resets", Name: "resets",
ArgTypes: []ExprType{ExprMatrix}, ArgTypes: []model.ValueType{model.ValMatrix},
ReturnType: ExprVector, ReturnType: model.ValVector,
Call: funcResets, Call: funcResets,
}, },
"round": { "round": {
Name: "round", Name: "round",
ArgTypes: []ExprType{ExprVector, ExprScalar}, ArgTypes: []model.ValueType{model.ValVector, model.ValScalar},
OptionalArgs: 1, OptionalArgs: 1,
ReturnType: ExprVector, ReturnType: model.ValVector,
Call: funcRound, Call: funcRound,
}, },
"scalar": { "scalar": {
Name: "scalar", Name: "scalar",
ArgTypes: []ExprType{ExprVector}, ArgTypes: []model.ValueType{model.ValVector},
ReturnType: ExprScalar, ReturnType: model.ValScalar,
Call: funcScalar, Call: funcScalar,
}, },
"sort": { "sort": {
Name: "sort", Name: "sort",
ArgTypes: []ExprType{ExprVector}, ArgTypes: []model.ValueType{model.ValVector},
ReturnType: ExprVector, ReturnType: model.ValVector,
Call: funcSort, Call: funcSort,
}, },
"sort_desc": { "sort_desc": {
Name: "sort_desc", Name: "sort_desc",
ArgTypes: []ExprType{ExprVector}, ArgTypes: []model.ValueType{model.ValVector},
ReturnType: ExprVector, ReturnType: model.ValVector,
Call: funcSortDesc, Call: funcSortDesc,
}, },
"sqrt": { "sqrt": {
Name: "sqrt", Name: "sqrt",
ArgTypes: []ExprType{ExprVector}, ArgTypes: []model.ValueType{model.ValVector},
ReturnType: ExprVector, ReturnType: model.ValVector,
Call: funcSqrt, Call: funcSqrt,
}, },
"sum_over_time": { "sum_over_time": {
Name: "sum_over_time", Name: "sum_over_time",
ArgTypes: []ExprType{ExprMatrix}, ArgTypes: []model.ValueType{model.ValMatrix},
ReturnType: ExprVector, ReturnType: model.ValVector,
Call: funcSumOverTime, Call: funcSumOverTime,
}, },
"time": { "time": {
Name: "time", Name: "time",
ArgTypes: []ExprType{}, ArgTypes: []model.ValueType{},
ReturnType: ExprScalar, ReturnType: model.ValScalar,
Call: funcTime, Call: funcTime,
}, },
"topk": { "topk": {
Name: "topk", Name: "topk",
ArgTypes: []ExprType{ExprScalar, ExprVector}, ArgTypes: []model.ValueType{model.ValScalar, model.ValVector},
ReturnType: ExprVector, ReturnType: model.ValVector,
Call: funcTopk, Call: funcTopk,
}, },
} }
@ -851,7 +853,7 @@ func getFunction(name string) (*Function, bool) {
return function, ok return function, ok
} }
type vectorByValueHeap Vector type vectorByValueHeap vector
func (s vectorByValueHeap) Len() int { func (s vectorByValueHeap) Len() int {
return len(s) return len(s)
@ -869,7 +871,7 @@ func (s vectorByValueHeap) Swap(i, j int) {
} }
func (s *vectorByValueHeap) Push(x interface{}) { func (s *vectorByValueHeap) Push(x interface{}) {
*s = append(*s, x.(*Sample)) *s = append(*s, x.(*sample))
} }
func (s *vectorByValueHeap) Pop() interface{} { func (s *vectorByValueHeap) Pop() interface{} {

View file

@ -967,7 +967,7 @@ func (p *parser) vectorSelector(name string) *VectorSelector {
// expectType checks the type of the node and raises an error if it // expectType checks the type of the node and raises an error if it
// is not of the expected type. // 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) t := p.checkType(node)
if t != want { if t != want {
p.errorf("expected type %s in %s, got %s", want, context, t) 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 // 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. // 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. // For expressions the type is determined by their Type function.
// Statements and lists do not have a type but are not invalid either. // Statements and lists do not have a type but are not invalid either.
switch n := node.(type) { switch n := node.(type) {
case Statements, Expressions, Statement: case Statements, Expressions, Statement:
typ = ExprNone typ = model.ValNone
case Expr: case Expr:
typ = n.Type() typ = n.Type()
default: default:
@ -996,27 +996,27 @@ func (p *parser) checkType(node Node) (typ ExprType) {
switch n := node.(type) { switch n := node.(type) {
case Statements: case Statements:
for _, s := range n { for _, s := range n {
p.expectType(s, ExprNone, "statement list") p.expectType(s, model.ValNone, "statement list")
} }
case *AlertStmt: case *AlertStmt:
p.expectType(n.Expr, ExprVector, "alert statement") p.expectType(n.Expr, model.ValVector, "alert statement")
case *EvalStmt: case *EvalStmt:
ty := p.checkType(n.Expr) 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) p.errorf("evaluation statement must have a valid expression type but got %s", ty)
} }
case *RecordStmt: case *RecordStmt:
ty := p.checkType(n.Expr) 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) p.errorf("record statement must have a valid expression of type vector or scalar but got %s", ty)
} }
case Expressions: case Expressions:
for _, e := range n { for _, e := range n {
ty := p.checkType(e) ty := p.checkType(e)
if ty == ExprNone { if ty == model.ValNone {
p.errorf("expression must have a valid expression type but got %s", ty) 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() { if !n.Op.isAggregator() {
p.errorf("aggregation operator expected in aggregation expression but got %q", n.Op) 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: case *BinaryExpr:
lt := p.checkType(n.LHS) lt := p.checkType(n.LHS)
@ -1033,11 +1033,11 @@ func (p *parser) checkType(node Node) (typ ExprType) {
if !n.Op.isOperator() { if !n.Op.isOperator() {
p.errorf("only logical and arithmetic operators allowed in binary expression, got %q", n.Op) 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") 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 { if len(n.VectorMatching.On) > 0 {
p.errorf("vector matching only allowed between vectors") 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") 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 { if n.Op != itemADD && n.Op != itemSUB {
p.errorf("only + and - operators allowed for unary expressions") 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) p.errorf("unary expression only allowed on expressions of type scalar or vector, got %q", t)
} }

View file

@ -24,47 +24,6 @@ import (
"github.com/prometheus/prometheus/util/strutil" "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. // Tree returns a string of the tree structure of the given node.
func Tree(node Node) string { func Tree(node Node) string {
return tree(node, "") return tree(node, "")

View file

@ -18,6 +18,8 @@ import (
"sort" "sort"
"github.com/prometheus/common/model" "github.com/prometheus/common/model"
"github.com/prometheus/prometheus/storage/metric"
) )
// Helpers to calculate quantiles. // 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 } func (b buckets) Less(i, j int) bool { return b[i].upperBound < b[j].upperBound }
type metricWithBuckets struct { type metricWithBuckets struct {
metric model.COWMetric metric metric.Metric
buckets buckets buckets buckets
} }

View file

@ -340,25 +340,25 @@ func (ev *evalCmd) expect(pos int, m model.Metric, vals ...sequenceValue) {
} }
// compareResult compares the result value with the defined expectation. // 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) { switch val := result.(type) {
case Matrix: case model.Matrix:
if ev.instant { if ev.instant {
return fmt.Errorf("received range result on instant evaluation") return fmt.Errorf("received range result on instant evaluation")
} }
seen := map[model.Fingerprint]bool{} seen := map[model.Fingerprint]bool{}
for pos, v := range val { for pos, v := range val {
fp := v.Metric.Metric.Fingerprint() fp := v.Metric.Fingerprint()
if _, ok := ev.metrics[fp]; !ok { 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] exp := ev.expected[fp]
if ev.ordered && exp.pos != pos+1 { 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 { for i, expVal := range exp.vals {
if !almostEqual(float64(expVal.value), float64(v.Values[i].Value)) { 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 seen[fp] = true
@ -369,22 +369,22 @@ func (ev *evalCmd) compareResult(result Value) error {
} }
} }
case Vector: case model.Vector:
if !ev.instant { if !ev.instant {
fmt.Errorf("received instant result on range evaluation") fmt.Errorf("received instant result on range evaluation")
} }
seen := map[model.Fingerprint]bool{} seen := map[model.Fingerprint]bool{}
for pos, v := range val { for pos, v := range val {
fp := v.Metric.Metric.Fingerprint() fp := v.Metric.Fingerprint()
if _, ok := ev.metrics[fp]; !ok { 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] exp := ev.expected[fp]
if ev.ordered && exp.pos != pos+1 { 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)) { 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 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)) { 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) return fmt.Errorf("expected scalar %v but got %v", val.Value, ev.expected[0].vals[0].value)
} }

View file

@ -77,8 +77,8 @@ type Alert struct {
} }
// sample returns a Sample suitable for recording the alert. // sample returns a Sample suitable for recording the alert.
func (a Alert) sample(timestamp model.Time, value model.SampleValue) *promql.Sample { func (a Alert) sample(timestamp model.Time, value model.SampleValue) *model.Sample {
recordedMetric := model.Metric{} recordedMetric := make(model.Metric, len(a.Labels)+3)
for label, value := range a.Labels { for label, value := range a.Labels {
recordedMetric[label] = value 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[alertNameLabel] = model.LabelValue(a.Name)
recordedMetric[alertStateLabel] = model.LabelValue(a.State.String()) recordedMetric[alertStateLabel] = model.LabelValue(a.State.String())
return &promql.Sample{ return &model.Sample{
Metric: model.COWMetric{ Metric: recordedMetric,
Metric: recordedMetric,
Copied: true,
},
Value: value, Value: value,
Timestamp: timestamp, Timestamp: timestamp,
} }
@ -152,7 +149,7 @@ func (rule *AlertingRule) Name() string {
// eval evaluates the rule expression and then creates pending alerts and fires // eval evaluates the rule expression and then creates pending alerts and fires
// or removes previously pending alerts accordingly. // 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) query, err := engine.NewInstantQuery(rule.vector.String(), timestamp)
if err != nil { if err != nil {
return nil, err 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. // or update the expression value for existing elements.
resultFPs := map[model.Fingerprint]struct{}{} resultFPs := map[model.Fingerprint]struct{}{}
for _, sample := range exprResult { for _, sample := range exprResult {
fp := sample.Metric.Metric.Fingerprint() fp := sample.Metric.Fingerprint()
resultFPs[fp] = struct{}{} resultFPs[fp] = struct{}{}
if alert, ok := rule.activeAlerts[fp]; !ok { 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) labels = labels.Merge(rule.labels)
if _, ok := labels[model.MetricNameLabel]; ok { if _, ok := labels[model.MetricNameLabel]; ok {
delete(labels, model.MetricNameLabel) 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. // Check if any pending alerts should be removed or fire now. Write out alert timeseries.
for fp, activeAlert := range rule.activeAlerts { for fp, activeAlert := range rule.activeAlerts {

View file

@ -80,7 +80,7 @@ type Rule interface {
// Name returns the name of the rule. // Name returns the name of the rule.
Name() string Name() string
// Eval evaluates the rule, including any associated recording or alerting actions. // 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 returns a human-readable string representation of the rule.
String() string String() string
// HTMLSnippet returns a human-readable string representation of the rule, // HTMLSnippet returns a human-readable string representation of the rule,
@ -273,11 +273,7 @@ func (m *Manager) runIteration() {
} }
for _, s := range vector { for _, s := range vector {
m.sampleAppender.Append(&model.Sample{ m.sampleAppender.Append(s)
Metric: s.Metric.Metric,
Value: s.Value,
Timestamp: s.Timestamp,
})
} }
}(rule) }(rule)
} }

View file

@ -43,38 +43,44 @@ func NewRecordingRule(name string, vector promql.Expr, labels model.LabelSet) *R
func (rule RecordingRule) Name() string { return rule.name } func (rule RecordingRule) Name() string { return rule.name }
// eval evaluates the rule and then overrides the metric names and labels accordingly. // 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) query, err := engine.NewInstantQuery(rule.vector.String(), timestamp)
if err != nil { if err != nil {
return nil, err return nil, err
} }
result := query.Exec() var (
var vector promql.Vector result = query.Exec()
vector model.Vector
)
switch result.Value.(type) { switch result.Value.(type) {
case promql.Vector: case model.Vector:
vector, err = result.Vector() vector, err = result.Vector()
if err != nil { if err != nil {
return nil, err return nil, err
} }
case *promql.Scalar: case *model.Scalar:
scalar, err := result.Scalar() scalar, err := result.Scalar()
if err != nil { if err != nil {
return nil, err 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: default:
return nil, fmt.Errorf("rule result is not a vector or scalar") return nil, fmt.Errorf("rule result is not a vector or scalar")
} }
// Override the metric name and labels. // Override the metric name and labels.
for _, sample := range vector { 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 { for label, value := range rule.labels {
if value == "" { if value == "" {
sample.Metric.Del(label) delete(sample.Metric, label)
} else { } else {
sample.Metric.Set(label, value) sample.Metric[label] = value
} }
} }
} }

View file

@ -40,7 +40,7 @@ type Storage interface {
// MetricsForLabelMatchers returns the metrics from storage that satisfy the given // MetricsForLabelMatchers returns the metrics from storage that satisfy the given
// label matchers. At least one label matcher must be specified that does not // label matchers. At least one label matcher must be specified that does not
// match the empty string. // 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 // LastSamplePairForFingerprint returns the last sample pair for the
// provided fingerprint. If the respective time series does not exist or // provided fingerprint. If the respective time series does not exist or
// has an evicted head chunk, nil is returned. // 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. // Get all of the label values that are associated with a given label name.
LabelValuesForLabelName(model.LabelName) model.LabelValues LabelValuesForLabelName(model.LabelName) model.LabelValues
// Get the metric associated with the provided fingerprint. // 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. // Construct an iterator for a given fingerprint.
// The iterator will never return samples older than retention time, // The iterator will never return samples older than retention time,
// relative to the time NewIterator was called. // relative to the time NewIterator was called.

View file

@ -410,7 +410,7 @@ func (s *memorySeriesStorage) fingerprintsForLabelPairs(pairs ...model.LabelPair
} }
// MetricsForLabelMatchers implements Storage. // 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 ( var (
equals []model.LabelPair equals []model.LabelPair
filters []*metric.LabelMatcher filters []*metric.LabelMatcher
@ -462,7 +462,7 @@ func (s *memorySeriesStorage) MetricsForLabelMatchers(matchers ...*metric.LabelM
filters = remaining filters = remaining
} }
result := make(map[model.Fingerprint]model.COWMetric, len(resFPs)) result := make(map[model.Fingerprint]metric.Metric, len(resFPs))
for fp := range resFPs { for fp := range resFPs {
result[fp] = s.MetricForFingerprint(fp) result[fp] = s.MetricForFingerprint(fp)
} }
@ -486,7 +486,7 @@ func (s *memorySeriesStorage) LabelValuesForLabelName(labelName model.LabelName)
} }
// MetricForFingerprint implements Storage. // 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) s.fpLocker.Lock(fp)
defer s.fpLocker.Unlock(fp) defer s.fpLocker.Unlock(fp)
@ -494,16 +494,18 @@ func (s *memorySeriesStorage) MetricForFingerprint(fp model.Fingerprint) model.C
if ok { if ok {
// Wrap the returned metric in a copy-on-write (COW) metric here because // Wrap the returned metric in a copy-on-write (COW) metric here because
// the caller might mutate it. // the caller might mutate it.
return model.COWMetric{ return metric.Metric{
Metric: series.metric, Metric: series.metric,
} }
} }
metric, err := s.persistence.archivedMetric(fp) met, err := s.persistence.archivedMetric(fp)
if err != nil { if err != nil {
log.Errorf("Error retrieving archived metric for fingerprint %v: %v", fp, err) log.Errorf("Error retrieving archived metric for fingerprint %v: %v", fp, err)
} }
return model.COWMetric{
Metric: metric, return metric.Metric{
Metric: met,
Copied: false,
} }
} }

View file

@ -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) { func BenchmarkLabelMatching(b *testing.B) {
s, closer := NewTestStorage(b, 1) s, closer := NewTestStorage(b, 1)
@ -359,7 +359,7 @@ func BenchmarkLabelMatching(b *testing.B) {
b.ResetTimer() b.ResetTimer()
for i := 0; i < b.N; i++ { for i := 0; i < b.N; i++ {
benchLabelMatchingRes = map[model.Fingerprint]model.COWMetric{} benchLabelMatchingRes = map[model.Fingerprint]metric.Metric{}
for _, mt := range matcherTests { for _, mt := range matcherTests {
benchLabelMatchingRes = s.MetricsForLabelMatchers(mt...) benchLabelMatchingRes = s.MetricsForLabelMatchers(mt...)
} }

63
storage/metric/metric.go Normal file
View file

@ -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()
}

View file

@ -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 model.Metric
}{
{
fn: func(cm *Metric) {
cm.Del("to_delete")
},
out: model.Metric{
"to_change": "test2",
},
},
{
fn: func(cm *Metric) {
cm.Set("to_change", "changed")
},
out: model.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)
}
}
}

View file

@ -64,24 +64,21 @@ func query(q string, timestamp model.Time, queryEngine *promql.Engine) (queryRes
if res.Err != nil { if res.Err != nil {
return nil, res.Err return nil, res.Err
} }
var vector promql.Vector var vector model.Vector
switch v := res.Value.(type) { switch v := res.Value.(type) {
case promql.Matrix: case model.Matrix:
return nil, errors.New("matrix return values not supported") return nil, errors.New("matrix return values not supported")
case promql.Vector: case model.Vector:
vector = v vector = v
case *promql.Scalar: case *model.Scalar:
vector = promql.Vector{&promql.Sample{ vector = model.Vector{&model.Sample{
Value: v.Value, Value: v.Value,
Timestamp: v.Timestamp, Timestamp: v.Timestamp,
}} }}
case *promql.String: case *model.String:
vector = promql.Vector{&promql.Sample{ vector = model.Vector{&model.Sample{
Metric: model.COWMetric{ Metric: model.Metric{"__value__": model.LabelValue(v.Value)},
Metric: model.Metric{"__value__": model.LabelValue(v.Value)},
Copied: true,
},
Timestamp: v.Timestamp, Timestamp: v.Timestamp,
}} }}
default: default:
@ -96,7 +93,7 @@ func query(q string, timestamp model.Time, queryEngine *promql.Engine) (queryRes
Value: float64(v.Value), Value: float64(v.Value),
Labels: make(map[string]string), Labels: make(map[string]string),
} }
for label, value := range v.Metric.Metric { for label, value := range v.Metric {
s.Labels[string(label)] = string(value) s.Labels[string(label)] = string(value)
} }
result[n] = &s result[n] = &s

View file

@ -26,8 +26,6 @@ import (
"github.com/prometheus/common/model" "github.com/prometheus/common/model"
"github.com/prometheus/log" "github.com/prometheus/log"
"github.com/prometheus/prometheus/promql"
) )
// Enables cross-site script calls. // 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()) 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)) respondJSON(w, plainVec(vec))
return return
} }
if sca, ok := res.Value.(*promql.Scalar); ok { if sca, ok := res.Value.(*model.Scalar); ok {
respondJSON(w, (*plainScalar)(sca)) respondJSON(w, (*plainScalar)(sca))
return return
} }
if str, ok := res.Value.(*promql.String); ok { if str, ok := res.Value.(*model.String); ok {
respondJSON(w, (*plainString)(str)) respondJSON(w, (*plainString)(str))
return 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 // plainVec is an indirection that hides the original MarshalJSON method
// which does not fit the response format for the legacy API. // 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) { func (pv plainVec) MarshalJSON() ([]byte, error) {
type plainSmpl promql.Sample type plainSmpl model.Sample
v := make([]*plainSmpl, len(pv)) v := make([]*plainSmpl, len(pv))
for i, sv := range pv { for i, sv := range pv {
@ -120,8 +118,8 @@ func (pv plainVec) MarshalJSON() ([]byte, error) {
return json.Marshal(&v) return json.Marshal(&v)
} }
func (pv plainVec) Type() promql.ExprType { func (pv plainVec) Type() model.ValueType {
return promql.ExprVector return model.ValVector
} }
func (pv plainVec) String() string { func (pv plainVec) String() string {
@ -130,15 +128,15 @@ func (pv plainVec) String() string {
// plainScalar is an indirection that hides the original MarshalJSON method // plainScalar is an indirection that hides the original MarshalJSON method
// which does not fit the response format for the legacy API. // 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) { func (ps plainScalar) MarshalJSON() ([]byte, error) {
s := strconv.FormatFloat(float64(ps.Value), 'f', -1, 64) s := strconv.FormatFloat(float64(ps.Value), 'f', -1, 64)
return json.Marshal(&s) return json.Marshal(&s)
} }
func (plainScalar) Type() promql.ExprType { func (plainScalar) Type() model.ValueType {
return promql.ExprScalar return model.ValScalar
} }
func (plainScalar) String() string { func (plainScalar) String() string {
@ -147,10 +145,10 @@ func (plainScalar) String() string {
// plainString is an indirection that hides the original MarshalJSON method // plainString is an indirection that hides the original MarshalJSON method
// which does not fit the response format for the legacy API. // 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 { func (pv plainString) Type() model.ValueType {
return promql.ExprString return model.ValString
} }
func (pv plainString) String() string { 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. // 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 { data := struct {
Type string `json:"type"` Type string `json:"type"`
Value interface{} `json:"value"` Value interface{} `json:"value"`
@ -268,7 +266,7 @@ func respondJSON(w io.Writer, val promql.Value) error {
Version: jsonFormatVersion, Version: jsonFormatVersion,
} }
// TODO(fabxc): Adding MarshalJSON to promql.Values might be a good idea. // 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 data.Value = sc.Value
} }
enc := json.NewEncoder(w) enc := json.NewEncoder(w)

View file

@ -15,6 +15,7 @@ import (
"github.com/prometheus/prometheus/promql" "github.com/prometheus/prometheus/promql"
"github.com/prometheus/prometheus/storage/local" "github.com/prometheus/prometheus/storage/local"
"github.com/prometheus/prometheus/storage/metric"
"github.com/prometheus/prometheus/util/route" "github.com/prometheus/prometheus/util/route"
"github.com/prometheus/prometheus/util/strutil" "github.com/prometheus/prometheus/util/strutil"
) )
@ -98,8 +99,8 @@ func (api *API) Register(r *route.Router) {
} }
type queryData struct { type queryData struct {
ResultType promql.ExprType `json:"resultType"` ResultType model.ValueType `json:"resultType"`
Result promql.Value `json:"result"` Result model.Value `json:"result"`
} }
func (api *API) query(r *http.Request) (interface{}, *apiError) { 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 { if len(r.Form["match[]"]) == 0 {
return nil, &apiError{errorBadData, fmt.Errorf("no match[] parameter provided")} 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[]"] { for _, lm := range r.Form["match[]"] {
matchers, err := promql.ParseMetricSelector(lm) matchers, err := promql.ParseMetricSelector(lm)

View file

@ -55,8 +55,8 @@ func TestEndpoints(t *testing.T) {
"time": []string{"123.3"}, "time": []string{"123.3"},
}, },
response: &queryData{ response: &queryData{
ResultType: promql.ExprScalar, ResultType: model.ValScalar,
Result: &promql.Scalar{ Result: &model.Scalar{
Value: 2, Value: 2,
Timestamp: start.Add(123*time.Second + 300*time.Millisecond), 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"}, "time": []string{"1970-01-01T00:02:03Z"},
}, },
response: &queryData{ response: &queryData{
ResultType: promql.ExprScalar, ResultType: model.ValScalar,
Result: &promql.Scalar{ Result: &model.Scalar{
Value: 0.333, Value: 0.333,
Timestamp: start.Add(123 * time.Second), Timestamp: start.Add(123 * time.Second),
}, },
@ -83,8 +83,8 @@ func TestEndpoints(t *testing.T) {
"time": []string{"1970-01-01T01:02:03+01:00"}, "time": []string{"1970-01-01T01:02:03+01:00"},
}, },
response: &queryData{ response: &queryData{
ResultType: promql.ExprScalar, ResultType: model.ValScalar,
Result: &promql.Scalar{ Result: &model.Scalar{
Value: 0.333, Value: 0.333,
Timestamp: start.Add(123 * time.Second), Timestamp: start.Add(123 * time.Second),
}, },
@ -99,14 +99,15 @@ func TestEndpoints(t *testing.T) {
"step": []string{"1"}, "step": []string{"1"},
}, },
response: &queryData{ response: &queryData{
ResultType: promql.ExprMatrix, ResultType: model.ValMatrix,
Result: promql.Matrix{ Result: model.Matrix{
&promql.SampleStream{ &model.SampleStream{
Values: []model.SamplePair{ Values: []model.SamplePair{
{Value: 0, Timestamp: start}, {Value: 0, Timestamp: start},
{Value: 1, Timestamp: start.Add(1 * time.Second)}, {Value: 1, Timestamp: start.Add(1 * time.Second)},
{Value: 2, Timestamp: start.Add(2 * 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) t.Fatalf("Expected error of type %q but got none", test.errType)
} }
if !reflect.DeepEqual(resp, test.response) { 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. // Ensure that removed metrics are unindexed before the next request.
suite.Storage().WaitForIndexing() suite.Storage().WaitForIndexing()

View file

@ -20,6 +20,7 @@ import (
"github.com/prometheus/prometheus/promql" "github.com/prometheus/prometheus/promql"
"github.com/prometheus/prometheus/storage/local" "github.com/prometheus/prometheus/storage/local"
"github.com/prometheus/prometheus/storage/metric"
dto "github.com/prometheus/client_model/go" dto "github.com/prometheus/client_model/go"
"github.com/prometheus/common/expfmt" "github.com/prometheus/common/expfmt"
@ -33,7 +34,7 @@ type Federation struct {
func (fed *Federation) ServeHTTP(w http.ResponseWriter, req *http.Request) { func (fed *Federation) ServeHTTP(w http.ResponseWriter, req *http.Request) {
req.ParseForm() req.ParseForm()
metrics := map[model.Fingerprint]model.COWMetric{} metrics := map[model.Fingerprint]metric.Metric{}
for _, s := range req.Form["match[]"] { for _, s := range req.Form["match[]"] {
matchers, err := promql.ParseMetricSelector(s) matchers, err := promql.ParseMetricSelector(s)