diff --git a/cmd/prometheus/main.go b/cmd/prometheus/main.go index bacecba6a..31be3abca 100644 --- a/cmd/prometheus/main.go +++ b/cmd/prometheus/main.go @@ -24,6 +24,7 @@ import ( "syscall" "time" + "github.com/fabxc/tsdb" "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/common/log" "github.com/prometheus/common/model" @@ -82,6 +83,10 @@ func Main() int { reloadables []Reloadable ) + _, err := tsdb.Open(cfg.localStoragePath, nil, nil) + if err != nil { + log.Errorf("Opening storage failed: %s", err) + } var localStorage local.Storage reloadableRemoteStorage := remote.New() diff --git a/promql/ast.go b/promql/ast.go index b3ccd2570..97009e59d 100644 --- a/promql/ast.go +++ b/promql/ast.go @@ -15,12 +15,11 @@ package promql import ( "fmt" + "regexp" "time" - "github.com/prometheus/common/model" - - "github.com/prometheus/prometheus/storage/local" - "github.com/prometheus/prometheus/storage/metric" + "github.com/fabxc/tsdb" + "github.com/fabxc/tsdb/labels" ) // Node is a generic interface for all nodes in an AST. @@ -58,8 +57,8 @@ type AlertStmt struct { Name string Expr Expr Duration time.Duration - Labels model.LabelSet - Annotations model.LabelSet + Labels labels.Labels + Annotations labels.Labels } // EvalStmt holds an expression and information on the range it should @@ -69,7 +68,7 @@ type EvalStmt struct { // The time boundaries for the evaluation. If Start equals End an instant // is evaluated. - Start, End model.Time + Start, End time.Time // Time between two evaluated instants for the range [Start:End]. Interval time.Duration } @@ -78,7 +77,7 @@ type EvalStmt struct { type RecordStmt struct { Name string Expr Expr - Labels model.LabelSet + Labels labels.Labels } func (*AlertStmt) stmt() {} @@ -91,7 +90,7 @@ type Expr interface { // Type returns the type the expression evaluates to. It does not perform // in-depth checks as this is done at parsing-time. - Type() model.ValueType + Type() ValueType // expr ensures that no other types accidentally implement the interface. expr() } @@ -101,12 +100,12 @@ type Expressions []Expr // AggregateExpr represents an aggregation operation on a vector. type AggregateExpr struct { - Op itemType // The used aggregation operation. - Expr Expr // The vector expression over which is aggregated. - Param Expr // Parameter used by some aggregators. - Grouping model.LabelNames // The labels by which to group the vector. - Without bool // Whether to drop the given labels rather than keep them. - KeepCommonLabels bool // Whether to keep common labels among result elements. + Op itemType // The used aggregation operation. + Expr Expr // The vector expression over which is aggregated. + Param Expr // Parameter used by some aggregators. + Grouping []string // The labels by which to group the vector. + Without bool // Whether to drop the given labels rather than keep them. + KeepCommonLabels bool // Whether to keep common labels among result elements. } // BinaryExpr represents a binary expression between two child expressions. @@ -133,15 +132,16 @@ type MatrixSelector struct { Name string Range time.Duration Offset time.Duration - LabelMatchers metric.LabelMatchers + LabelMatchers []*LabelMatcher // The series iterators are populated at query preparation time. - iterators []local.SeriesIterator + series []tsdb.Series + iterators []*tsdb.BufferedSeriesIterator } // NumberLiteral represents a number. type NumberLiteral struct { - Val model.SampleValue + Val float64 } // ParenExpr wraps an expression so it cannot be disassembled as a consequence @@ -166,25 +166,26 @@ type UnaryExpr struct { type VectorSelector struct { Name string Offset time.Duration - LabelMatchers metric.LabelMatchers + LabelMatchers []*LabelMatcher // The series iterators are populated at query preparation time. - iterators []local.SeriesIterator + series []tsdb.Series + iterators []*tsdb.BufferedSeriesIterator } -func (e *AggregateExpr) Type() model.ValueType { return model.ValVector } -func (e *Call) Type() model.ValueType { return e.Func.ReturnType } -func (e *MatrixSelector) Type() model.ValueType { return model.ValMatrix } -func (e *NumberLiteral) Type() model.ValueType { return model.ValScalar } -func (e *ParenExpr) Type() model.ValueType { return e.Expr.Type() } -func (e *StringLiteral) Type() model.ValueType { return model.ValString } -func (e *UnaryExpr) Type() model.ValueType { return e.Expr.Type() } -func (e *VectorSelector) Type() model.ValueType { return model.ValVector } -func (e *BinaryExpr) Type() model.ValueType { - if e.LHS.Type() == model.ValScalar && e.RHS.Type() == model.ValScalar { - return model.ValScalar +func (e *AggregateExpr) Type() ValueType { return ValueTypeVector } +func (e *Call) Type() ValueType { return e.Func.ReturnType } +func (e *MatrixSelector) Type() ValueType { return ValueTypeMatrix } +func (e *NumberLiteral) Type() ValueType { return ValueTypeScalar } +func (e *ParenExpr) Type() ValueType { return e.Expr.Type() } +func (e *StringLiteral) Type() ValueType { return ValueTypeString } +func (e *UnaryExpr) Type() ValueType { return e.Expr.Type() } +func (e *VectorSelector) Type() ValueType { return ValueTypeVector } +func (e *BinaryExpr) Type() ValueType { + if e.LHS.Type() == ValueTypeScalar && e.RHS.Type() == ValueTypeScalar { + return ValueTypeScalar } - return model.ValVector + return ValueTypeScalar } func (*AggregateExpr) expr() {} @@ -229,13 +230,13 @@ type VectorMatching struct { Card VectorMatchCardinality // MatchingLabels contains the labels which define equality of a pair of // elements from the vectors. - MatchingLabels model.LabelNames + MatchingLabels []string // On includes the given label names from matching, // rather than excluding them. On bool // Include contains additional labels that should be included in // the result from the side with the lower cardinality. - Include model.LabelNames + Include []string } // Visitor allows visiting a Node and its child nodes. The Visit method is @@ -315,3 +316,83 @@ func (f inspector) Visit(node Node) Visitor { func Inspect(node Node, f func(Node) bool) { Walk(inspector(f), node) } + +// MatchType is an enum for label matching types. +type MatchType int + +// Possible MatchTypes. +const ( + MatchEqual MatchType = iota + MatchNotEqual + MatchRegexp + MatchNotRegexp +) + +func (m MatchType) String() string { + typeToStr := map[MatchType]string{ + MatchEqual: "=", + MatchNotEqual: "!=", + MatchRegexp: "=~", + MatchNotRegexp: "!~", + } + if str, ok := typeToStr[m]; ok { + return str + } + panic("unknown match type") +} + +// LabelMatcher models the matching of a label. +type LabelMatcher struct { + Type MatchType + Name string + Value string + + re *regexp.Regexp +} + +// NewLabelMatcher returns a LabelMatcher object ready to use. +func NewLabelMatcher(t MatchType, n, v string) (*LabelMatcher, error) { + m := &LabelMatcher{ + Type: t, + Name: n, + Value: v, + } + if t == MatchRegexp || t == MatchNotRegexp { + m.Value = "^(?:" + v + ")$" + re, err := regexp.Compile(m.Value) + if err != nil { + return nil, err + } + m.re = re + } + return m, nil +} + +func (m *LabelMatcher) String() string { + return fmt.Sprintf("%s%s%q", m.Name, m.Type, m.Value) +} + +func (m *LabelMatcher) matcher() labels.Matcher { + switch m.Type { + case MatchEqual: + return labels.NewEqualMatcher(m.Name, m.Value) + + case MatchNotEqual: + return labels.Not(labels.NewEqualMatcher(m.Name, m.Value)) + + case MatchRegexp: + res, err := labels.NewRegexpMatcher(m.Name, m.Value) + if err != nil { + panic(err) + } + return res + + case MatchNotRegexp: + res, err := labels.NewRegexpMatcher(m.Name, m.Value) + if err != nil { + panic(err) + } + return labels.Not(res) + } + panic("promql.LabelMatcher.matcher: invalid matcher type") +} diff --git a/promql/engine.go b/promql/engine.go index a343074df..6af4c7339 100644 --- a/promql/engine.go +++ b/promql/engine.go @@ -19,93 +19,143 @@ import ( "math" "runtime" "sort" + "strings" "time" + "github.com/fabxc/tsdb" + "github.com/fabxc/tsdb/labels" "github.com/prometheus/common/log" - "github.com/prometheus/common/model" "golang.org/x/net/context" - "github.com/prometheus/prometheus/storage/local" - "github.com/prometheus/prometheus/storage/metric" "github.com/prometheus/prometheus/util/stats" ) const ( // The largest SampleValue that can be converted to an int64 without overflow. - maxInt64 model.SampleValue = 9223372036854774784 + maxInt64 = 9223372036854774784 // The smallest SampleValue that can be converted to an int64 without underflow. - minInt64 model.SampleValue = -9223372036854775808 + minInt64 = -9223372036854775808 + + MetricNameLabel = "__name__" ) // convertibleToInt64 returns true if v does not over-/underflow an int64. -func convertibleToInt64(v model.SampleValue) bool { +func convertibleToInt64(v float64) bool { return v <= maxInt64 && v >= minInt64 } +// Value is a generic interface for values resulting from a query evaluation. +type Value interface { + Type() ValueType + String() string +} + +func (matrix) Type() ValueType { return ValueTypeMatrix } +func (vector) Type() ValueType { return ValueTypeVector } +func (scalar) Type() ValueType { return ValueTypeScalar } +func (stringVal) Type() ValueType { return ValueTypeString } + +// ValueType describes a type of a value. +type ValueType string + +// The valid value types. +const ( + ValueTypeNone = "none" + ValueTypeVector = "vector" + ValueTypeScalar = "scalar" + ValueTypeMatrix = "matrix" + ValueTypeString = "string" +) + +type stringVal struct { + s string + t int64 +} + +func (s stringVal) String() string { + return s.s +} + +type scalar struct { + t int64 + v float64 +} + +func (s scalar) String() string { + return "" +} + // sampleStream is a stream of Values belonging to an attached COWMetric. type sampleStream struct { - Metric metric.Metric - Values []model.SamplePair + Metric labels.Labels + Values []samplePair +} + +func (s sampleStream) String() string { + return "" +} + +type samplePair struct { + t int64 + v float64 +} + +func (s samplePair) String() string { + return "" } // sample is a single sample belonging to a COWMetric. type sample struct { - Metric metric.Metric - Value model.SampleValue - Timestamp model.Time + Metric labels.Labels + Value float64 + Timestamp int64 +} + +func (s sample) String() string { + return "" } // vector is basically only an alias for model.Samples, but the // contract is that in a Vector, all Samples have the same timestamp. -type vector []*sample +type vector []sample -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)) +func (vec vector) String() string { + entries := make([]string, len(vec)) for i, s := range vec { - val[i] = &model.Sample{ - Metric: s.Metric.Copy().Metric, - Value: s.Value, - Timestamp: s.Timestamp, - } + entries[i] = s.String() } - return val + return strings.Join(entries, "\n") } // matrix is a slice of SampleStreams that implements sort.Interface and // has a String method. -type matrix []*sampleStream +type matrix []sampleStream -func (matrix) Type() model.ValueType { return model.ValMatrix } -func (mat matrix) String() string { return mat.value().String() } +func (m matrix) String() string { + // TODO(fabxc): sort, or can we rely on order from the querier? + strs := make([]string, len(m)) -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, - } + for i, ss := range m { + strs[i] = ss.String() } - return val + + return strings.Join(strs, "\n") } // Result holds the resulting value of an execution or an error // if any occurred. type Result struct { Err error - Value model.Value + Value Value } // Vector returns a vector if the result value is one. An error is returned if // the result was an error or the result value is not a vector. -func (r *Result) Vector() (model.Vector, error) { +func (r *Result) Vector() (vector, error) { if r.Err != nil { return nil, r.Err } - v, ok := r.Value.(model.Vector) + v, ok := r.Value.(vector) if !ok { return nil, fmt.Errorf("query result is not a vector") } @@ -114,11 +164,11 @@ func (r *Result) Vector() (model.Vector, error) { // Matrix returns a matrix. An error is returned if // the result was an error or the result value is not a matrix. -func (r *Result) Matrix() (model.Matrix, error) { +func (r *Result) Matrix() (matrix, error) { if r.Err != nil { return nil, r.Err } - v, ok := r.Value.(model.Matrix) + v, ok := r.Value.(matrix) if !ok { return nil, fmt.Errorf("query result is not a range vector") } @@ -127,13 +177,13 @@ func (r *Result) Matrix() (model.Matrix, error) { // Scalar returns a scalar value. An error is returned if // the result was an error or the result value is not a scalar. -func (r *Result) Scalar() (*model.Scalar, error) { +func (r *Result) Scalar() (scalar, error) { if r.Err != nil { - return nil, r.Err + return scalar{}, r.Err } - v, ok := r.Value.(*model.Scalar) + v, ok := r.Value.(scalar) if !ok { - return nil, fmt.Errorf("query result is not a scalar") + return scalar{}, fmt.Errorf("query result is not a scalar") } return v, nil } @@ -239,7 +289,7 @@ type Engine struct { // Queryable allows opening a storage querier. type Queryable interface { - Querier() (local.Querier, error) + Querier(mint, maxt int64) (tsdb.Querier, error) } // NewEngine returns a new engine. @@ -267,7 +317,7 @@ var DefaultEngineOptions = &EngineOptions{ } // NewInstantQuery returns an evaluation query for the given expression at the given time. -func (ng *Engine) NewInstantQuery(qs string, ts model.Time) (Query, error) { +func (ng *Engine) NewInstantQuery(qs string, ts time.Time) (Query, error) { expr, err := ParseExpr(qs) if err != nil { return nil, err @@ -280,12 +330,12 @@ func (ng *Engine) NewInstantQuery(qs string, ts model.Time) (Query, error) { // NewRangeQuery returns an evaluation query for the given time range and with // the resolution set by the interval. -func (ng *Engine) NewRangeQuery(qs string, start, end model.Time, interval time.Duration) (Query, error) { +func (ng *Engine) NewRangeQuery(qs string, start, end time.Time, interval time.Duration) (Query, error) { expr, err := ParseExpr(qs) if err != nil { return nil, err } - if expr.Type() != model.ValVector && expr.Type() != model.ValScalar { + if expr.Type() != ValueTypeVector && expr.Type() != ValueTypeScalar { return nil, fmt.Errorf("invalid expression type %q for range query, must be scalar or instant vector", documentedType(expr.Type())) } qry := ng.newQuery(expr, start, end, interval) @@ -294,7 +344,7 @@ func (ng *Engine) NewRangeQuery(qs string, start, end model.Time, interval time. return qry, nil } -func (ng *Engine) newQuery(expr Expr, start, end model.Time, interval time.Duration) *query { +func (ng *Engine) newQuery(expr Expr, start, end time.Time, interval time.Duration) *query { es := &EvalStmt{ Expr: expr, Start: start, @@ -330,7 +380,7 @@ func (ng *Engine) newTestQuery(f func(context.Context) error) Query { // // At this point per query only one EvalStmt is evaluated. Alert and record // statements are not handled by the Engine. -func (ng *Engine) exec(ctx context.Context, q *query) (model.Value, error) { +func (ng *Engine) exec(ctx context.Context, q *query) (Value, error) { ctx, cancel := context.WithTimeout(ctx, ng.options.Timeout) q.cancel = cancel @@ -366,27 +416,30 @@ func (ng *Engine) exec(ctx context.Context, q *query) (model.Value, error) { panic(fmt.Errorf("promql.Engine.exec: unhandled statement of type %T", q.Statement())) } +func timeMilliseconds(t time.Time) int64 { + return t.UnixNano() / int64(time.Millisecond/time.Nanosecond) +} + +func durationMilliseconds(d time.Duration) int64 { + return int64(d / (time.Millisecond / time.Nanosecond)) +} + // execEvalStmt evaluates the expression of an evaluation statement for the given time range. -func (ng *Engine) execEvalStmt(ctx context.Context, query *query, s *EvalStmt) (model.Value, error) { - querier, err := ng.queryable.Querier() +func (ng *Engine) execEvalStmt(ctx context.Context, query *query, s *EvalStmt) (Value, error) { + + prepareTimer := query.stats.GetTimer(stats.QueryPreparationTime).Start() + querier, err := ng.populateIterators(ctx, s) + prepareTimer.Stop() if err != nil { return nil, err } defer querier.Close() - prepareTimer := query.stats.GetTimer(stats.QueryPreparationTime).Start() - err = ng.populateIterators(ctx, querier, s) - prepareTimer.Stop() - if err != nil { - return nil, err - } - defer ng.closeIterators(s) - evalTimer := query.stats.GetTimer(stats.InnerEvalTime).Start() // Instant evaluation. if s.Start == s.End && s.Interval == 0 { evaluator := &evaluator{ - Timestamp: s.Start, + Timestamp: timeMilliseconds(s.Start), ctx: ctx, } val, err := evaluator.Eval(s.Expr) @@ -394,22 +447,13 @@ func (ng *Engine) execEvalStmt(ctx context.Context, query *query, s *EvalStmt) ( return nil, err } - // Turn matrix and vector types with protected metrics into - // model.* types. - switch v := val.(type) { - case vector: - val = v.value() - case matrix: - val = v.value() - } - evalTimer.Stop() return val, nil } numSteps := int(s.End.Sub(s.Start) / s.Interval) // Range evaluation. - sampleStreams := map[model.Fingerprint]*sampleStream{} + sampleStreams := map[uint64]sampleStream{} for ts := s.Start; !ts.After(s.End); ts = ts.Add(s.Interval) { if err := contextDone(ctx, "range evaluation"); err != nil { @@ -417,7 +461,7 @@ func (ng *Engine) execEvalStmt(ctx context.Context, query *query, s *EvalStmt) ( } evaluator := &evaluator{ - Timestamp: ts, + Timestamp: timeMilliseconds(ts), ctx: ctx, } val, err := evaluator.Eval(s.Expr) @@ -426,32 +470,32 @@ func (ng *Engine) execEvalStmt(ctx context.Context, query *query, s *EvalStmt) ( } switch v := val.(type) { - case *model.Scalar: + case scalar: // As the expression type does not change we can safely default to 0 // as the fingerprint for scalar expressions. - ss := sampleStreams[0] - if ss == nil { - ss = &sampleStream{Values: make([]model.SamplePair, 0, numSteps)} + ss, ok := sampleStreams[0] + if !ok { + ss = sampleStream{Values: make([]samplePair, 0, numSteps)} sampleStreams[0] = ss } - ss.Values = append(ss.Values, model.SamplePair{ - Value: v.Value, - Timestamp: v.Timestamp, + ss.Values = append(ss.Values, samplePair{ + v: v.v, + t: v.t, }) case vector: for _, sample := range v { - fp := sample.Metric.Metric.Fingerprint() - ss := sampleStreams[fp] - if ss == nil { - ss = &sampleStream{ + h := sample.Metric.Hash() + ss, ok := sampleStreams[h] + if !ok { + ss = sampleStream{ Metric: sample.Metric, - Values: make([]model.SamplePair, 0, numSteps), + Values: make([]samplePair, 0, numSteps), } - sampleStreams[fp] = ss + sampleStreams[h] = ss } - ss.Values = append(ss.Values, model.SamplePair{ - Value: sample.Value, - Timestamp: sample.Timestamp, + ss.Values = append(ss.Values, samplePair{ + v: sample.Value, + t: sample.Timestamp, }) } default: @@ -476,68 +520,81 @@ func (ng *Engine) execEvalStmt(ctx context.Context, query *query, s *EvalStmt) ( } // Turn matrix type with protected metric into model.Matrix. - resMatrix := mat.value() + resMatrix := mat - sortTimer := query.stats.GetTimer(stats.ResultSortTime).Start() - sort.Sort(resMatrix) - sortTimer.Stop() + // TODO(fabxc): order ensured by storage? + // sortTimer := query.stats.GetTimer(stats.ResultSortTime).Start() + // sort.Sort(resMatrix) + // sortTimer.Stop() return resMatrix, nil } -func (ng *Engine) populateIterators(ctx context.Context, querier local.Querier, s *EvalStmt) error { - var queryErr error +func (ng *Engine) populateIterators(ctx context.Context, s *EvalStmt) (tsdb.Querier, error) { + var maxOffset time.Duration + Inspect(s.Expr, func(node Node) bool { switch n := node.(type) { case *VectorSelector: - if s.Start.Equal(s.End) { - n.iterators, queryErr = querier.QueryInstant( - ctx, - s.Start.Add(-n.Offset), - StalenessDelta, - n.LabelMatchers..., - ) - } else { - n.iterators, queryErr = querier.QueryRange( - ctx, - s.Start.Add(-n.Offset-StalenessDelta), - s.End.Add(-n.Offset), - n.LabelMatchers..., - ) - } - if queryErr != nil { - return false + if n.Offset > maxOffset { + maxOffset = n.Offset + StalenessDelta } case *MatrixSelector: - n.iterators, queryErr = querier.QueryRange( - ctx, - s.Start.Add(-n.Offset-n.Range), - s.End.Add(-n.Offset), - n.LabelMatchers..., - ) - if queryErr != nil { - return false + if n.Offset > maxOffset { + maxOffset = n.Offset + n.Range } } return true }) - return queryErr + + mint := s.Start.Add(-maxOffset) + + querier, err := ng.queryable.Querier(timeMilliseconds(mint), timeMilliseconds(s.End)) + if err != nil { + return nil, err + } + + Inspect(s.Expr, func(node Node) bool { + switch n := node.(type) { + case *VectorSelector: + sel := make(labels.Selector, 0, len(n.LabelMatchers)) + for _, m := range n.LabelMatchers { + sel = append(sel, m.matcher()) + } + + n.series, err = expandSeriesSet(querier.Select(sel...)) + if err != nil { + return false + } + for _, s := range n.series { + it := tsdb.NewBuffer(s.Iterator(), durationMilliseconds(StalenessDelta)) + n.iterators = append(n.iterators, it) + } + case *MatrixSelector: + sel := make(labels.Selector, 0, len(n.LabelMatchers)) + for _, m := range n.LabelMatchers { + sel = append(sel, m.matcher()) + } + + n.series, err = expandSeriesSet(querier.Select(sel...)) + if err != nil { + return false + } + for _, s := range n.series { + it := tsdb.NewBuffer(s.Iterator(), durationMilliseconds(n.Range)) + n.iterators = append(n.iterators, it) + } + } + return true + }) + return querier, err } -func (ng *Engine) closeIterators(s *EvalStmt) { - Inspect(s.Expr, func(node Node) bool { - switch n := node.(type) { - case *VectorSelector: - for _, it := range n.iterators { - it.Close() - } - case *MatrixSelector: - for _, it := range n.iterators { - it.Close() - } - } - return true - }) +func expandSeriesSet(it tsdb.SeriesSet) (res []tsdb.Series, err error) { + for it.Next() { + res = append(res, it.Series()) + } + return res, it.Err() } // An evaluator evaluates given expressions at a fixed timestamp. It is attached to an @@ -546,7 +603,7 @@ func (ng *Engine) closeIterators(s *EvalStmt) { type evaluator struct { ctx context.Context - Timestamp model.Time + Timestamp int64 // time in milliseconds } // fatalf causes a panic with the input formatted into an error. @@ -577,9 +634,9 @@ func (ev *evaluator) recover(errp *error) { } // evalScalar attempts to evaluate e to a scalar value and errors otherwise. -func (ev *evaluator) evalScalar(e Expr) *model.Scalar { +func (ev *evaluator) evalScalar(e Expr) scalar { val := ev.eval(e) - sv, ok := val.(*model.Scalar) + sv, ok := val.(scalar) if !ok { ev.errorf("expected scalar but got %s", documentedType(val.Type())) } @@ -599,16 +656,16 @@ func (ev *evaluator) evalVector(e Expr) vector { // evalInt attempts to evaluate e into an integer and errors otherwise. func (ev *evaluator) evalInt(e Expr) int64 { sc := ev.evalScalar(e) - if !convertibleToInt64(sc.Value) { - ev.errorf("scalar value %v overflows int64", sc.Value) + if !convertibleToInt64(sc.v) { + ev.errorf("scalar value %v overflows int64", sc.v) } - return int64(sc.Value) + return int64(sc.v) } // evalFloat attempts to evaluate e into a float and errors otherwise. func (ev *evaluator) evalFloat(e Expr) float64 { sc := ev.evalScalar(e) - return float64(sc.Value) + return float64(sc.v) } // evalMatrix attempts to evaluate e into a matrix and errors otherwise. @@ -624,9 +681,9 @@ func (ev *evaluator) evalMatrix(e Expr) matrix { } // evalString attempts to evaluate e to a string value and errors otherwise. -func (ev *evaluator) evalString(e Expr) *model.String { +func (ev *evaluator) evalString(e Expr) stringVal { val := ev.eval(e) - sv, ok := val.(*model.String) + sv, ok := val.(stringVal) if !ok { ev.errorf("expected string but got %s", documentedType(val.Type())) } @@ -634,7 +691,7 @@ func (ev *evaluator) evalString(e Expr) *model.String { } // evalOneOf evaluates e and errors unless the result is of one of the given types. -func (ev *evaluator) evalOneOf(e Expr, t1, t2 model.ValueType) model.Value { +func (ev *evaluator) evalOneOf(e Expr, t1, t2 ValueType) Value { val := ev.eval(e) if val.Type() != t1 && val.Type() != t2 { ev.errorf("expected %s or %s but got %s", documentedType(t1), documentedType(t2), documentedType(val.Type())) @@ -642,13 +699,13 @@ func (ev *evaluator) evalOneOf(e Expr, t1, t2 model.ValueType) model.Value { return val } -func (ev *evaluator) Eval(expr Expr) (v model.Value, err error) { +func (ev *evaluator) Eval(expr Expr) (v Value, err error) { defer ev.recover(&err) return ev.eval(expr), nil } // eval evaluates the given expression as the given AST expression node requires. -func (ev *evaluator) eval(expr Expr) model.Value { +func (ev *evaluator) eval(expr Expr) Value { // This is the top-level evaluation method. // Thus, we check for timeout/cancelation here. if err := contextDone(ev.ctx, "expression evaluation"); err != nil { @@ -661,17 +718,17 @@ func (ev *evaluator) eval(expr Expr) model.Value { return ev.aggregation(e.Op, e.Grouping, e.Without, e.KeepCommonLabels, e.Param, vector) case *BinaryExpr: - lhs := ev.evalOneOf(e.LHS, model.ValScalar, model.ValVector) - rhs := ev.evalOneOf(e.RHS, model.ValScalar, model.ValVector) + lhs := ev.evalOneOf(e.LHS, ValueTypeScalar, ValueTypeVector) + rhs := ev.evalOneOf(e.RHS, ValueTypeScalar, ValueTypeVector) switch lt, rt := lhs.Type(), rhs.Type(); { - case lt == model.ValScalar && rt == model.ValScalar: - return &model.Scalar{ - Value: scalarBinop(e.Op, lhs.(*model.Scalar).Value, rhs.(*model.Scalar).Value), - Timestamp: ev.Timestamp, + case lt == ValueTypeScalar && rt == ValueTypeScalar: + return scalar{ + v: scalarBinop(e.Op, lhs.(scalar).v, rhs.(scalar).v), + t: ev.Timestamp, } - case lt == model.ValVector && rt == model.ValVector: + case lt == ValueTypeVector && rt == ValueTypeVector: switch e.Op { case itemLAND: return ev.vectorAnd(lhs.(vector), rhs.(vector), e.VectorMatching) @@ -682,11 +739,11 @@ func (ev *evaluator) eval(expr Expr) model.Value { default: return ev.vectorBinop(e.Op, lhs.(vector), rhs.(vector), e.VectorMatching, e.ReturnBool) } - case lt == model.ValVector && rt == model.ValScalar: - return ev.vectorScalarBinop(e.Op, lhs.(vector), rhs.(*model.Scalar), false, e.ReturnBool) + case lt == ValueTypeVector && rt == ValueTypeScalar: + return ev.vectorScalarBinop(e.Op, lhs.(vector), rhs.(scalar), false, e.ReturnBool) - case lt == model.ValScalar && rt == model.ValVector: - return ev.vectorScalarBinop(e.Op, rhs.(vector), lhs.(*model.Scalar), true, e.ReturnBool) + case lt == ValueTypeScalar && rt == ValueTypeVector: + return ev.vectorScalarBinop(e.Op, rhs.(vector), lhs.(scalar), true, e.ReturnBool) } case *Call: @@ -696,21 +753,21 @@ func (ev *evaluator) eval(expr Expr) model.Value { return ev.matrixSelector(e) case *NumberLiteral: - return &model.Scalar{Value: e.Val, Timestamp: ev.Timestamp} + return scalar{v: e.Val, t: ev.Timestamp} case *ParenExpr: return ev.eval(e.Expr) case *StringLiteral: - return &model.String{Value: e.Val, Timestamp: ev.Timestamp} + return stringVal{s: e.Val, t: ev.Timestamp} case *UnaryExpr: - se := ev.evalOneOf(e.Expr, model.ValScalar, model.ValVector) + se := ev.evalOneOf(e.Expr, ValueTypeScalar, ValueTypeVector) // Only + and - are possible operators. if e.Op == itemSUB { switch v := se.(type) { - case *model.Scalar: - v.Value = -v.Value + case scalar: + v.v = -v.v case vector: for i, sv := range v { v[i].Value = -sv.Value @@ -727,17 +784,32 @@ func (ev *evaluator) eval(expr Expr) model.Value { // vectorSelector evaluates a *VectorSelector expression. func (ev *evaluator) vectorSelector(node *VectorSelector) vector { - vec := vector{} - for _, it := range node.iterators { - refTime := ev.Timestamp.Add(-node.Offset) - samplePair := it.ValueAtOrBeforeTime(refTime) - if samplePair.Timestamp.Before(refTime.Add(-StalenessDelta)) { - continue // Sample outside of staleness policy window. + var ( + ok bool + vec = make(vector, 0, len(node.series)) + refTime = ev.Timestamp - durationMilliseconds(node.Offset) + ) + + for i, it := range node.iterators { + if !it.Seek(refTime) { + if it.Err() != nil { + ev.error(it.Err()) + } + continue } - vec = append(vec, &sample{ - Metric: it.Metric(), - Value: samplePair.Value, - Timestamp: ev.Timestamp, + t, v := it.Values() + + if t > refTime { + t, v, ok = it.PeekBack() + if !ok || t < refTime-durationMilliseconds(StalenessDelta) { + continue + } + } + + vec = append(vec, sample{ + Metric: node.series[i].Labels(), + Value: v, + Timestamp: int64(ev.Timestamp), }) } return vec @@ -745,31 +817,43 @@ func (ev *evaluator) vectorSelector(node *VectorSelector) vector { // matrixSelector evaluates a *MatrixSelector expression. func (ev *evaluator) matrixSelector(node *MatrixSelector) matrix { - interval := metric.Interval{ - OldestInclusive: ev.Timestamp.Add(-node.Range - node.Offset), - NewestInclusive: ev.Timestamp.Add(-node.Offset), - } + var ( + offset = durationMilliseconds(node.Offset) + maxt = ev.Timestamp - offset + mint = maxt - durationMilliseconds(node.Range) + matrix = make(matrix, 0, len(node.series)) + ) - sampleStreams := make([]*sampleStream, 0, len(node.iterators)) - for _, it := range node.iterators { - samplePairs := it.RangeValues(interval) - if len(samplePairs) == 0 { + for i, it := range node.iterators { + ss := sampleStream{ + Metric: node.series[i].Labels(), + Values: make([]samplePair, 0, 16), + } + + if !it.Seek(maxt) { + if it.Err() != nil { + ev.error(it.Err()) + } continue } - if node.Offset != 0 { - for _, sp := range samplePairs { - sp.Timestamp = sp.Timestamp.Add(node.Offset) + buf := it.Buffer() + for buf.Next() { + t, v := buf.Values() + // Values in the buffer are guaranteed to be smaller than maxt. + if t >= mint { + ss.Values = append(ss.Values, samplePair{t: t + offset, v: v}) } } - - sampleStream := &sampleStream{ - Metric: it.Metric(), - Values: samplePairs, + // The seeked sample might also be in the range. + t, v := it.Values() + if t == maxt { + ss.Values = append(ss.Values, samplePair{t: t + offset, v: v}) } - sampleStreams = append(sampleStreams, sampleStream) + + matrix = append(matrix, ss) } - return matrix(sampleStreams) + return matrix } func (ev *evaluator) vectorAnd(lhs, rhs vector, matching *VectorMatching) vector { @@ -855,7 +939,7 @@ func (ev *evaluator) vectorBinop(op itemType, lhs, rhs vector, matching *VectorM } // All samples from the rhs hashed by the matching label/values. - rightSigs := map[uint64]*sample{} + rightSigs := map[uint64]sample{} // Add all rhs samples to a map so we can easily find matches later. for _, rs := range rhs { @@ -910,7 +994,8 @@ func (ev *evaluator) vectorBinop(op itemType, lhs, rhs vector, matching *VectorM // In many-to-one matching the grouping labels have to ensure a unique metric // for the result vector. Check whether those labels have already been added for // the same matching labels. - insertSig := uint64(metric.Metric.Fingerprint()) + insertSig := metric.Hash() + if !exists { insertedSigs = map[uint64]struct{}{} matchedSigs[sig] = insertedSigs @@ -920,7 +1005,7 @@ func (ev *evaluator) vectorBinop(op itemType, lhs, rhs vector, matching *VectorM insertedSigs[insertSig] = struct{}{} } - result = append(result, &sample{ + result = append(result, sample{ Metric: metric, Value: value, Timestamp: ev.Timestamp, @@ -929,77 +1014,114 @@ func (ev *evaluator) vectorBinop(op itemType, lhs, rhs vector, matching *VectorM return result } -// signatureFunc returns a function that calculates the signature for a metric -// ignoring the provided labels. If on, then the given labels are only used instead. -func signatureFunc(on bool, labels ...model.LabelName) func(m metric.Metric) uint64 { - if !on { - return func(m metric.Metric) uint64 { - tmp := m.Metric.Clone() - for _, l := range labels { - delete(tmp, l) +func hashWithoutLabels(lset labels.Labels, names ...string) uint64 { + cm := make(labels.Labels, 0, len(lset)-len(names)-1) + +Outer: + for _, l := range lset { + for _, n := range names { + if n == l.Name { + continue Outer + } + } + if l.Name == MetricNameLabel { + continue + } + cm = append(cm, l) + } + + return cm.Hash() +} + +func hashForLabels(lset labels.Labels, names ...string) uint64 { + cm := make(labels.Labels, 0, len(names)) + + for _, l := range lset { + for _, n := range names { + if l.Name == n { + cm = append(cm, l) + break } - delete(tmp, model.MetricNameLabel) - return uint64(tmp.Fingerprint()) } } - return func(m metric.Metric) uint64 { - return model.SignatureForLabels(m.Metric, labels...) + return cm.Hash() +} + +// signatureFunc returns a function that calculates the signature for a metric +// ignoring the provided labels. If on, then the given labels are only used instead. +func signatureFunc(on bool, names ...string) func(labels.Labels) uint64 { + // TODO(fabxc): ensure names are sorted and then use that and sortedness + // of labels by names to speed up the operations below. + // Alternatively, inline the hashing and don't build new label sets. + if on { + return func(lset labels.Labels) uint64 { return hashForLabels(lset, names...) } } + return func(lset labels.Labels) uint64 { return hashWithoutLabels(lset, names...) } } // resultMetric returns the metric for the given sample(s) based on the vector // binary operation and the matching options. -func resultMetric(lhs, rhs metric.Metric, op itemType, matching *VectorMatching) metric.Metric { +func resultMetric(lhs, rhs labels.Labels, op itemType, matching *VectorMatching) labels.Labels { + // del and add hold modifications to the LHS input metric. + // Deletions are applied first. + del := make([]string, 0, 16) + add := make([]labels.Label, 0, 16) + if shouldDropMetricName(op) { - lhs.Del(model.MetricNameLabel) + del = append(del, MetricNameLabel) } - if !matching.On { - if matching.Card == CardOneToOne { - for _, l := range matching.MatchingLabels { - lhs.Del(l) - } - } - for _, ln := range matching.Include { - // Included labels from the `group_x` modifier are taken from the "one"-side. - value := rhs.Metric[ln] - if value != "" { - lhs.Set(ln, rhs.Metric[ln]) - } else { - lhs.Del(ln) - } - } - return lhs - } - // As we definitely write, creating a new metric is the easiest solution. - m := model.Metric{} + if matching.Card == CardOneToOne { - for _, ln := range matching.MatchingLabels { - if v, ok := lhs.Metric[ln]; ok { - m[ln] = v + if matching.On { + Outer: + for _, l := range lhs { + for _, n := range matching.MatchingLabels { + if l.Name == n { + continue Outer + } + } + del = append(del, l.Name) } - } - } else { - for k, v := range lhs.Metric { - m[k] = v + } else { + del = append(del, matching.MatchingLabels...) } } for _, ln := range matching.Include { - // Included labels from the `group_x` modifier are taken from the "one"-side . - if v, ok := rhs.Metric[ln]; ok { - m[ln] = v - } else { - delete(m, ln) + // We always want to delete the include label on the LHS + // before adding an included one or not. + del = append(del, ln) + // Included labels from the `group_x` modifier are taken from the "one"-side. + if v := rhs.Get(ln); v != "" { + add = append(add, labels.Label{Name: ln, Value: v}) } } - return metric.Metric{Metric: m, Copied: false} + + return modifiedLabels(lhs, del, add) +} + +func modifiedLabels(lhs labels.Labels, del []string, add []labels.Label) labels.Labels { + res := make(labels.Labels, 0, len(lhs)+len(add)-len(del)) +Outer: + for _, l := range lhs { + for _, n := range del { + if l.Name == n { + continue Outer + } + } + res = append(res, l) + } + res = append(res, add...) + sort.Sort(res) + + return res } // vectorScalarBinop evaluates a binary operation between a vector and a scalar. -func (ev *evaluator) vectorScalarBinop(op itemType, lhs vector, rhs *model.Scalar, swap, returnBool bool) vector { +func (ev *evaluator) vectorScalarBinop(op itemType, lhs vector, rhs scalar, swap, returnBool bool) vector { vec := make(vector, 0, len(lhs)) for _, lhsSample := range lhs { - lv, rv := lhsSample.Value, rhs.Value + lv, rv := lhsSample.Value, rhs.v // lhs always contains the vector. If the original position was different // swap for calculating the value. if swap { @@ -1016,17 +1138,31 @@ func (ev *evaluator) vectorScalarBinop(op itemType, lhs vector, rhs *model.Scala } if keep { lhsSample.Value = value - if shouldDropMetricName(op) { - lhsSample.Metric.Del(model.MetricNameLabel) - } + lhsSample.Metric = copyLabels(lhsSample.Metric, shouldDropMetricName(op)) + vec = append(vec, lhsSample) } } return vec } +func copyLabels(metric labels.Labels, withName bool) labels.Labels { + if withName { + cm := make(labels.Labels, len(metric)) + copy(cm, metric) + return cm + } + cm := make(labels.Labels, 0, len(metric)-1) + for _, l := range metric { + if l.Name != MetricNameLabel { + cm = append(cm, l) + } + } + return cm +} + // scalarBinop evaluates a binary operation between two scalars. -func scalarBinop(op itemType, lhs, rhs model.SampleValue) model.SampleValue { +func scalarBinop(op itemType, lhs, rhs float64) float64 { switch op { case itemADD: return lhs + rhs @@ -1037,9 +1173,9 @@ func scalarBinop(op itemType, lhs, rhs model.SampleValue) model.SampleValue { case itemDIV: return lhs / rhs case itemPOW: - return model.SampleValue(math.Pow(float64(lhs), float64(rhs))) + return math.Pow(float64(lhs), float64(rhs)) case itemMOD: - return model.SampleValue(math.Mod(float64(lhs), float64(rhs))) + return math.Mod(float64(lhs), float64(rhs)) case itemEQL: return btos(lhs == rhs) case itemNEQ: @@ -1057,7 +1193,7 @@ func scalarBinop(op itemType, lhs, rhs model.SampleValue) model.SampleValue { } // vectorElemBinop evaluates a binary operation between two vector elements. -func vectorElemBinop(op itemType, lhs, rhs model.SampleValue) (model.SampleValue, bool) { +func vectorElemBinop(op itemType, lhs, rhs float64) (float64, bool) { switch op { case itemADD: return lhs + rhs, true @@ -1068,9 +1204,9 @@ func vectorElemBinop(op itemType, lhs, rhs model.SampleValue) (model.SampleValue case itemDIV: return lhs / rhs, true case itemPOW: - return model.SampleValue(math.Pow(float64(lhs), float64(rhs))), true + return math.Pow(float64(lhs), float64(rhs)), true case itemMOD: - return model.SampleValue(math.Mod(float64(lhs), float64(rhs))), true + return math.Mod(float64(lhs), float64(rhs)), true case itemEQL: return lhs, lhs == rhs case itemNEQ: @@ -1087,27 +1223,32 @@ func vectorElemBinop(op itemType, lhs, rhs model.SampleValue) (model.SampleValue panic(fmt.Errorf("operator %q not allowed for operations between vectors", op)) } -// labelIntersection returns the metric of common label/value pairs of two input metrics. -func labelIntersection(metric1, metric2 metric.Metric) metric.Metric { - for label, value := range metric1.Metric { - if metric2.Metric[label] != value { - metric1.Del(label) +// intersection returns the metric of common label/value pairs of two input metrics. +func intersection(ls1, ls2 labels.Labels) labels.Labels { + res := make(labels.Labels, 0, 5) + + for _, l1 := range ls1 { + for _, l2 := range ls2 { + if l1.Name == l2.Name && l1.Value == l2.Value { + res = append(res, l1) + continue + } } } - return metric1 + return res } type groupedAggregation struct { - labels metric.Metric - value model.SampleValue - valuesSquaredSum model.SampleValue + labels labels.Labels + value float64 + valuesSquaredSum float64 groupCount int heap vectorByValueHeap reverseHeap vectorByReverseValueHeap } // aggregation evaluates an aggregation operation on a vector. -func (ev *evaluator) aggregation(op itemType, grouping model.LabelNames, without bool, keepCommon bool, param Expr, vec vector) vector { +func (ev *evaluator) aggregation(op itemType, grouping []string, without bool, keepCommon bool, param Expr, vec vector) vector { result := map[uint64]*groupedAggregation{} var k int64 @@ -1121,54 +1262,47 @@ func (ev *evaluator) aggregation(op itemType, grouping model.LabelNames, without if op == itemQuantile { q = ev.evalFloat(param) } - var valueLabel model.LabelName + var valueLabel string if op == itemCountValues { - valueLabel = model.LabelName(ev.evalString(param).Value) + valueLabel = ev.evalString(param).s if !without { grouping = append(grouping, valueLabel) } } for _, s := range vec { - withoutMetric := s.Metric + var ( + del []string + add []labels.Label + ) if without { - for _, l := range grouping { - withoutMetric.Del(l) - } - withoutMetric.Del(model.MetricNameLabel) - if op == itemCountValues { - withoutMetric.Set(valueLabel, model.LabelValue(s.Value.String())) - } - } else { - if op == itemCountValues { - s.Metric.Set(valueLabel, model.LabelValue(s.Value.String())) - } + del = append(grouping, MetricNameLabel) + } + if op == itemCountValues { + del = append(del, valueLabel) + add = append(add, labels.Label{Name: valueLabel, Value: fmt.Sprintf("%f", s.Value)}) } - var groupingKey uint64 - if without { - groupingKey = uint64(withoutMetric.Metric.Fingerprint()) - } else { - groupingKey = model.SignatureForLabels(s.Metric.Metric, grouping...) - } - - groupedResult, ok := result[groupingKey] + var ( + metric = modifiedLabels(s.Metric, del, add) + groupingKey = metric.Hash() + ) + group, ok := result[groupingKey] // Add a new group if it doesn't exist. if !ok { - var m metric.Metric + var m labels.Labels if keepCommon { - m = s.Metric - m.Del(model.MetricNameLabel) + m = copyLabels(metric, false) } else if without { - m = withoutMetric + m = metric } else { - m = metric.Metric{ - Metric: model.Metric{}, - Copied: true, - } - for _, l := range grouping { - if v, ok := s.Metric.Metric[l]; ok { - m.Set(l, v) + m = make(labels.Labels, 0, len(grouping)) + for _, l := range s.Metric { + for _, n := range grouping { + if l.Name == n { + m = append(m, labels.Label{Name: n, Value: l.Value}) + break + } } } } @@ -1189,45 +1323,54 @@ func (ev *evaluator) aggregation(op itemType, grouping model.LabelNames, without } // Add the sample to the existing group. if keepCommon { - groupedResult.labels = labelIntersection(groupedResult.labels, s.Metric) + group.labels = intersection(group.labels, s.Metric) } switch op { case itemSum: - groupedResult.value += s.Value + group.value += s.Value + case itemAvg: - groupedResult.value += s.Value - groupedResult.groupCount++ + group.value += s.Value + group.groupCount++ + case itemMax: - if groupedResult.value < s.Value || math.IsNaN(float64(groupedResult.value)) { - groupedResult.value = s.Value + if group.value < s.Value || math.IsNaN(float64(group.value)) { + group.value = s.Value } + case itemMin: - if groupedResult.value > s.Value || math.IsNaN(float64(groupedResult.value)) { - groupedResult.value = s.Value + if group.value > s.Value || math.IsNaN(float64(group.value)) { + group.value = s.Value } + case itemCount, itemCountValues: - groupedResult.groupCount++ + group.groupCount++ + case itemStdvar, itemStddev: - groupedResult.value += s.Value - groupedResult.valuesSquaredSum += s.Value * s.Value - groupedResult.groupCount++ + group.value += s.Value + group.valuesSquaredSum += s.Value * s.Value + group.groupCount++ + case itemTopK: - if int64(len(groupedResult.heap)) < k || groupedResult.heap[0].Value < s.Value || math.IsNaN(float64(groupedResult.heap[0].Value)) { - if int64(len(groupedResult.heap)) == k { - heap.Pop(&groupedResult.heap) + if int64(len(group.heap)) < k || group.heap[0].Value < s.Value || math.IsNaN(float64(group.heap[0].Value)) { + if int64(len(group.heap)) == k { + heap.Pop(&group.heap) } - heap.Push(&groupedResult.heap, &sample{Value: s.Value, Metric: s.Metric}) + heap.Push(&group.heap, &sample{Value: s.Value, Metric: s.Metric}) } + case itemBottomK: - if int64(len(groupedResult.reverseHeap)) < k || groupedResult.reverseHeap[0].Value > s.Value || math.IsNaN(float64(groupedResult.reverseHeap[0].Value)) { - if int64(len(groupedResult.reverseHeap)) == k { - heap.Pop(&groupedResult.reverseHeap) + if int64(len(group.reverseHeap)) < k || group.reverseHeap[0].Value > s.Value || math.IsNaN(float64(group.reverseHeap[0].Value)) { + if int64(len(group.reverseHeap)) == k { + heap.Pop(&group.reverseHeap) } - heap.Push(&groupedResult.reverseHeap, &sample{Value: s.Value, Metric: s.Metric}) + heap.Push(&group.reverseHeap, &sample{Value: s.Value, Metric: s.Metric}) } + case itemQuantile: - groupedResult.heap = append(groupedResult.heap, s) + group.heap = append(group.heap, s) + default: panic(fmt.Errorf("expected aggregation operator but got %q", op)) } @@ -1239,54 +1382,61 @@ func (ev *evaluator) aggregation(op itemType, grouping model.LabelNames, without for _, aggr := range result { switch op { case itemAvg: - aggr.value = aggr.value / model.SampleValue(aggr.groupCount) + aggr.value = aggr.value / float64(aggr.groupCount) + case itemCount, itemCountValues: - aggr.value = model.SampleValue(aggr.groupCount) + aggr.value = float64(aggr.groupCount) + case itemStdvar: avg := float64(aggr.value) / float64(aggr.groupCount) - aggr.value = model.SampleValue(float64(aggr.valuesSquaredSum)/float64(aggr.groupCount) - avg*avg) + aggr.value = float64(aggr.valuesSquaredSum)/float64(aggr.groupCount) - avg*avg + case itemStddev: avg := float64(aggr.value) / float64(aggr.groupCount) - aggr.value = model.SampleValue(math.Sqrt(float64(aggr.valuesSquaredSum)/float64(aggr.groupCount) - avg*avg)) + aggr.value = math.Sqrt(float64(aggr.valuesSquaredSum)/float64(aggr.groupCount) - avg*avg) + case itemTopK: // The heap keeps the lowest value on top, so reverse it. sort.Sort(sort.Reverse(aggr.heap)) for _, v := range aggr.heap { - resultVector = append(resultVector, &sample{ + resultVector = append(resultVector, sample{ Metric: v.Metric, Value: v.Value, Timestamp: ev.Timestamp, }) } continue // Bypass default append. + case itemBottomK: // The heap keeps the lowest value on top, so reverse it. sort.Sort(sort.Reverse(aggr.reverseHeap)) for _, v := range aggr.reverseHeap { - resultVector = append(resultVector, &sample{ + resultVector = append(resultVector, sample{ Metric: v.Metric, Value: v.Value, Timestamp: ev.Timestamp, }) } continue // Bypass default append. + case itemQuantile: - aggr.value = model.SampleValue(quantile(q, aggr.heap)) + aggr.value = quantile(q, aggr.heap) + default: // For other aggregations, we already have the right value. } - sample := &sample{ + + resultVector = append(resultVector, sample{ Metric: aggr.labels, Value: aggr.value, Timestamp: ev.Timestamp, - } - resultVector = append(resultVector, sample) + }) } return resultVector } // btos returns 1 if b is true, 0 otherwise. -func btos(b bool) model.SampleValue { +func btos(b bool) float64 { if b { return 1 } @@ -1342,13 +1492,13 @@ func (g *queryGate) Done() { // documentedType returns the internal type to the equivalent // user facing terminology as defined in the documentation. -func documentedType(t model.ValueType) string { - switch t.String() { +func documentedType(t ValueType) string { + switch t { case "vector": return "instant vector" case "matrix": return "range vector" default: - return t.String() + return string(t) } } diff --git a/promql/functions.go b/promql/functions.go index 607421470..1b0a01fc4 100644 --- a/promql/functions.go +++ b/promql/functions.go @@ -20,26 +20,25 @@ import ( "strconv" "time" + "github.com/fabxc/tsdb/labels" "github.com/prometheus/common/model" - - "github.com/prometheus/prometheus/storage/metric" ) // Function represents a function of the expression language and is // used by function nodes. type Function struct { Name string - ArgTypes []model.ValueType + ArgTypes []ValueType OptionalArgs int - ReturnType model.ValueType - Call func(ev *evaluator, args Expressions) model.Value + ReturnType ValueType + Call func(ev *evaluator, args Expressions) Value } -// === time() model.SampleValue === -func funcTime(ev *evaluator, args Expressions) model.Value { - return &model.Scalar{ - Value: model.SampleValue(ev.Timestamp.Unix()), - Timestamp: ev.Timestamp, +// === time() float64 === +func funcTime(ev *evaluator, args Expressions) Value { + return scalar{ + v: float64(ev.Timestamp / 1000), + t: ev.Timestamp, } } @@ -47,11 +46,11 @@ func funcTime(ev *evaluator, args Expressions) model.Value { // It calculates the rate (allowing for counter resets if isCounter is true), // extrapolates if the first/last sample is close to the boundary, and returns // the result as either per-second (if isRate is true) or overall. -func extrapolatedRate(ev *evaluator, arg Expr, isCounter bool, isRate bool) model.Value { +func extrapolatedRate(ev *evaluator, arg Expr, isCounter bool, isRate bool) Value { ms := arg.(*MatrixSelector) - rangeStart := ev.Timestamp.Add(-ms.Range - ms.Offset) - rangeEnd := ev.Timestamp.Add(-ms.Offset) + rangeStart := ev.Timestamp - durationMilliseconds(ms.Range+ms.Offset) + rangeEnd := ev.Timestamp - durationMilliseconds(ms.Offset) resultVector := vector{} @@ -63,26 +62,25 @@ func extrapolatedRate(ev *evaluator, arg Expr, isCounter bool, isRate bool) mode continue } var ( - counterCorrection model.SampleValue - lastValue model.SampleValue + counterCorrection float64 + lastValue float64 ) for _, sample := range samples.Values { - currentValue := sample.Value - if isCounter && currentValue < lastValue { + if isCounter && sample.v < lastValue { counterCorrection += lastValue } - lastValue = currentValue + lastValue = sample.v } - resultValue := lastValue - samples.Values[0].Value + counterCorrection + resultValue := lastValue - samples.Values[0].v + counterCorrection // Duration between first/last samples and boundary of range. - durationToStart := samples.Values[0].Timestamp.Sub(rangeStart).Seconds() - durationToEnd := rangeEnd.Sub(samples.Values[len(samples.Values)-1].Timestamp).Seconds() + durationToStart := float64(samples.Values[0].t - rangeStart) + durationToEnd := float64(rangeEnd - samples.Values[len(samples.Values)-1].t) - sampledInterval := samples.Values[len(samples.Values)-1].Timestamp.Sub(samples.Values[0].Timestamp).Seconds() - averageDurationBetweenSamples := sampledInterval / float64(len(samples.Values)-1) + sampledInterval := float64(samples.Values[len(samples.Values)-1].t - samples.Values[0].t) + averageDurationBetweenSamples := float64(sampledInterval) / float64(len(samples.Values)-1) - if isCounter && resultValue > 0 && samples.Values[0].Value >= 0 { + if isCounter && resultValue > 0 && samples.Values[0].v >= 0 { // Counters cannot be negative. If we have any slope at // all (i.e. resultValue went up), we can extrapolate // the zero point of the counter. If the duration to the @@ -90,7 +88,7 @@ func extrapolatedRate(ev *evaluator, arg Expr, isCounter bool, isRate bool) mode // take the zero point as the start of the series, // thereby avoiding extrapolation to negative counter // values. - durationToZero := sampledInterval * float64(samples.Values[0].Value/resultValue) + durationToZero := float64(sampledInterval) * float64(samples.Values[0].v/resultValue) if durationToZero < durationToStart { durationToStart = durationToZero } @@ -113,48 +111,46 @@ func extrapolatedRate(ev *evaluator, arg Expr, isCounter bool, isRate bool) mode } else { extrapolateToInterval += averageDurationBetweenSamples / 2 } - resultValue = resultValue * model.SampleValue(extrapolateToInterval/sampledInterval) + resultValue = resultValue * extrapolateToInterval / sampledInterval if isRate { - resultValue = resultValue / model.SampleValue(ms.Range.Seconds()) + resultValue = resultValue / 1000 / ms.Range.Seconds() } - resultSample := &sample{ - Metric: samples.Metric, + resultVector = append(resultVector, sample{ + Metric: copyLabels(samples.Metric, false), Value: resultValue, Timestamp: ev.Timestamp, - } - resultSample.Metric.Del(model.MetricNameLabel) - resultVector = append(resultVector, resultSample) + }) } return resultVector } -// === delta(matrix model.ValMatrix) Vector === -func funcDelta(ev *evaluator, args Expressions) model.Value { +// === delta(matrix ValueTypeMatrix) Vector === +func funcDelta(ev *evaluator, args Expressions) Value { return extrapolatedRate(ev, args[0], false, false) } -// === rate(node model.ValMatrix) Vector === -func funcRate(ev *evaluator, args Expressions) model.Value { +// === rate(node ValueTypeMatrix) Vector === +func funcRate(ev *evaluator, args Expressions) Value { return extrapolatedRate(ev, args[0], true, true) } -// === increase(node model.ValMatrix) Vector === -func funcIncrease(ev *evaluator, args Expressions) model.Value { +// === increase(node ValueTypeMatrix) Vector === +func funcIncrease(ev *evaluator, args Expressions) Value { return extrapolatedRate(ev, args[0], true, false) } -// === irate(node model.ValMatrix) Vector === -func funcIrate(ev *evaluator, args Expressions) model.Value { +// === irate(node ValueTypeMatrix) Vector === +func funcIrate(ev *evaluator, args Expressions) Value { return instantValue(ev, args[0], true) } // === idelta(node model.ValMatric) Vector === -func funcIdelta(ev *evaluator, args Expressions) model.Value { +func funcIdelta(ev *evaluator, args Expressions) Value { return instantValue(ev, args[0], false) } -func instantValue(ev *evaluator, arg Expr, isRate bool) model.Value { +func instantValue(ev *evaluator, arg Expr, isRate bool) Value { resultVector := vector{} for _, samples := range ev.evalMatrix(arg) { // No sense in trying to compute a rate without at least two points. Drop @@ -166,32 +162,29 @@ func instantValue(ev *evaluator, arg Expr, isRate bool) model.Value { lastSample := samples.Values[len(samples.Values)-1] previousSample := samples.Values[len(samples.Values)-2] - var resultValue model.SampleValue - if isRate && lastSample.Value < previousSample.Value { + var resultValue float64 + if isRate && lastSample.v < previousSample.v { // Counter reset. - resultValue = lastSample.Value + resultValue = lastSample.v } else { - resultValue = lastSample.Value - previousSample.Value + resultValue = lastSample.v - previousSample.v } - sampledInterval := lastSample.Timestamp.Sub(previousSample.Timestamp) + sampledInterval := lastSample.t - previousSample.t if sampledInterval == 0 { - // Avoid dividing by 0. - continue + // Avoid dividing by 0.float64 } if isRate { // Convert to per-second. - resultValue /= model.SampleValue(sampledInterval.Seconds()) + resultValue /= float64(sampledInterval) / 1000 } - resultSample := &sample{ - Metric: samples.Metric, + resultVector = append(resultVector, sample{ + Metric: copyLabels(samples.Metric, false), Value: resultValue, Timestamp: ev.Timestamp, - } - resultSample.Metric.Del(model.MetricNameLabel) - resultVector = append(resultVector, resultSample) + }) } return resultVector } @@ -220,7 +213,7 @@ func calcTrendValue(i int, sf, tf float64, s, b, d []float64) float64 { // data. A lower smoothing factor increases the influence of historical data. The trend factor (0 < tf < 1) affects // how trends in historical data will affect the current data. A higher trend factor increases the influence. // of trends. Algorithm taken from https://en.wikipedia.org/wiki/Exponential_smoothing titled: "Double exponential smoothing". -func funcHoltWinters(ev *evaluator, args Expressions) model.Value { +func funcHoltWinters(ev *evaluator, args Expressions) Value { mat := ev.evalMatrix(args[0]) // The smoothing factor argument. @@ -261,7 +254,7 @@ func funcHoltWinters(ev *evaluator, args Expressions) model.Value { // Fill in the d values with the raw values from the input. for i, v := range samples.Values { - d[i] = float64(v.Value) + d[i] = v.v } // Set initial values. @@ -281,10 +274,9 @@ func funcHoltWinters(ev *evaluator, args Expressions) model.Value { s[i] = x + y } - samples.Metric.Del(model.MetricNameLabel) - resultVector = append(resultVector, &sample{ - Metric: samples.Metric, - Value: model.SampleValue(s[len(s)-1]), // The last value in the vector is the smoothed result. + resultVector = append(resultVector, sample{ + Metric: copyLabels(samples.Metric, false), + Value: s[len(s)-1], // The last value in the vector is the smoothed result. Timestamp: ev.Timestamp, }) } @@ -292,8 +284,8 @@ func funcHoltWinters(ev *evaluator, args Expressions) model.Value { return resultVector } -// === sort(node model.ValVector) Vector === -func funcSort(ev *evaluator, args Expressions) model.Value { +// === sort(node ValueTypeVector) Vector === +func funcSort(ev *evaluator, args Expressions) Value { // NaN should sort to the bottom, so take descending sort with NaN first and // reverse it. byValueSorter := vectorByReverseValueHeap(ev.evalVector(args[0])) @@ -301,8 +293,8 @@ func funcSort(ev *evaluator, args Expressions) model.Value { return vector(byValueSorter) } -// === sortDesc(node model.ValVector) Vector === -func funcSortDesc(ev *evaluator, args Expressions) model.Value { +// === sortDesc(node ValueTypeVector) Vector === +func funcSortDesc(ev *evaluator, args Expressions) Value { // NaN should sort to the bottom, so take ascending sort with NaN first and // reverse it. byValueSorter := vectorByValueHeap(ev.evalVector(args[0])) @@ -310,67 +302,71 @@ func funcSortDesc(ev *evaluator, args Expressions) model.Value { return vector(byValueSorter) } -// === clamp_max(vector model.ValVector, max Scalar) Vector === -func funcClampMax(ev *evaluator, args Expressions) model.Value { +// === clamp_max(vector ValueTypeVector, max Scalar) Vector === +func funcClampMax(ev *evaluator, args Expressions) Value { vec := ev.evalVector(args[0]) max := ev.evalFloat(args[1]) for _, el := range vec { - el.Metric.Del(model.MetricNameLabel) - el.Value = model.SampleValue(math.Min(max, float64(el.Value))) + el.Metric = copyLabels(el.Metric, false) + el.Value = math.Min(max, float64(el.Value)) } return vec } -// === clamp_min(vector model.ValVector, min Scalar) Vector === -func funcClampMin(ev *evaluator, args Expressions) model.Value { +// === clamp_min(vector ValueTypeVector, min Scalar) Vector === +func funcClampMin(ev *evaluator, args Expressions) Value { vec := ev.evalVector(args[0]) min := ev.evalFloat(args[1]) for _, el := range vec { - el.Metric.Del(model.MetricNameLabel) - el.Value = model.SampleValue(math.Max(min, float64(el.Value))) + el.Metric = copyLabels(el.Metric, false) + el.Value = math.Max(min, float64(el.Value)) } return vec } -// === drop_common_labels(node model.ValVector) Vector === -func funcDropCommonLabels(ev *evaluator, args Expressions) model.Value { +// === drop_common_labels(node ValueTypeVector) Vector === +func funcDropCommonLabels(ev *evaluator, args Expressions) Value { vec := ev.evalVector(args[0]) if len(vec) < 1 { return vector{} } - common := model.LabelSet{} - for k, v := range vec[0].Metric.Metric { + common := map[string]string{} + + for _, l := range vec[0].Metric { // TODO(julius): Should we also drop common metric names? - if k == model.MetricNameLabel { + if l.Name == MetricNameLabel { continue } - common[k] = v + common[l.Name] = l.Value } for _, el := range vec[1:] { for k, v := range common { - if el.Metric.Metric[k] != v { - // Deletion of map entries while iterating over them is safe. - // From http://golang.org/ref/spec#For_statements: - // "If map entries that have not yet been reached are deleted during - // iteration, the corresponding iteration values will not be produced." - delete(common, k) + for _, l := range el.Metric { + if l.Name == k && l.Value != v { + // Deletion of map entries while iterating over them is safe. + // From http://golang.org/ref/spec#For_statements: + // "If map entries that have not yet been reached are deleted during + // iteration, the corresponding iteration values will not be produced." + delete(common, k) + } } } } + cnames := []string{} + for n := range common { + cnames = append(cnames, n) + } + for _, el := range vec { - for k := range el.Metric.Metric { - if _, ok := common[k]; ok { - el.Metric.Del(k) - } - } + el.Metric = modifiedLabels(el.Metric, cnames, nil) } return vec } -// === round(vector model.ValVector, toNearest=1 Scalar) Vector === -func funcRound(ev *evaluator, args Expressions) model.Value { +// === round(vector ValueTypeVector, toNearest=1 Scalar) Vector === +func funcRound(ev *evaluator, args Expressions) Value { // round returns a number rounded to toNearest. // Ties are solved by rounding up. toNearest := float64(1) @@ -382,36 +378,36 @@ func funcRound(ev *evaluator, args Expressions) model.Value { vec := ev.evalVector(args[0]) for _, el := range vec { - el.Metric.Del(model.MetricNameLabel) - el.Value = model.SampleValue(math.Floor(float64(el.Value)*toNearestInverse+0.5) / toNearestInverse) + el.Metric = copyLabels(el.Metric, false) + el.Value = math.Floor(float64(el.Value)*toNearestInverse+0.5) / toNearestInverse } return vec } -// === scalar(node model.ValVector) Scalar === -func funcScalar(ev *evaluator, args Expressions) model.Value { +// === scalar(node ValueTypeVector) Scalar === +func funcScalar(ev *evaluator, args Expressions) Value { v := ev.evalVector(args[0]) if len(v) != 1 { - return &model.Scalar{ - Value: model.SampleValue(math.NaN()), - Timestamp: ev.Timestamp, + return scalar{ + v: math.NaN(), + t: ev.Timestamp, } } - return &model.Scalar{ - Value: model.SampleValue(v[0].Value), - Timestamp: ev.Timestamp, + return scalar{ + v: v[0].Value, + t: ev.Timestamp, } } -// === count_scalar(vector model.ValVector) model.SampleValue === -func funcCountScalar(ev *evaluator, args Expressions) model.Value { - return &model.Scalar{ - Value: model.SampleValue(len(ev.evalVector(args[0]))), - Timestamp: ev.Timestamp, +// === count_scalar(vector ValueTypeVector) float64 === +func funcCountScalar(ev *evaluator, args Expressions) Value { + return scalar{ + v: float64(len(ev.evalVector(args[0]))), + t: ev.Timestamp, } } -func aggrOverTime(ev *evaluator, args Expressions, aggrFn func([]model.SamplePair) model.SampleValue) model.Value { +func aggrOverTime(ev *evaluator, args Expressions, aggrFn func([]samplePair) float64) Value { mat := ev.evalMatrix(args[0]) resultVector := vector{} @@ -420,9 +416,8 @@ func aggrOverTime(ev *evaluator, args Expressions, aggrFn func([]model.SamplePai continue } - el.Metric.Del(model.MetricNameLabel) - resultVector = append(resultVector, &sample{ - Metric: el.Metric, + resultVector = append(resultVector, sample{ + Metric: copyLabels(el.Metric, false), Value: aggrFn(el.Values), Timestamp: ev.Timestamp, }) @@ -430,69 +425,69 @@ func aggrOverTime(ev *evaluator, args Expressions, aggrFn func([]model.SamplePai return resultVector } -// === avg_over_time(matrix model.ValMatrix) Vector === -func funcAvgOverTime(ev *evaluator, args Expressions) model.Value { - return aggrOverTime(ev, args, func(values []model.SamplePair) model.SampleValue { - var sum model.SampleValue +// === avg_over_time(matrix ValueTypeMatrix) Vector === +func funcAvgOverTime(ev *evaluator, args Expressions) Value { + return aggrOverTime(ev, args, func(values []samplePair) float64 { + var sum float64 for _, v := range values { - sum += v.Value + sum += v.v } - return sum / model.SampleValue(len(values)) + return sum / float64(len(values)) }) } -// === count_over_time(matrix model.ValMatrix) Vector === -func funcCountOverTime(ev *evaluator, args Expressions) model.Value { - return aggrOverTime(ev, args, func(values []model.SamplePair) model.SampleValue { - return model.SampleValue(len(values)) +// === count_over_time(matrix ValueTypeMatrix) Vector === +func funcCountOverTime(ev *evaluator, args Expressions) Value { + return aggrOverTime(ev, args, func(values []samplePair) float64 { + return float64(len(values)) }) } -// === floor(vector model.ValVector) Vector === -func funcFloor(ev *evaluator, args Expressions) model.Value { +// === floor(vector ValueTypeVector) Vector === +func funcFloor(ev *evaluator, args Expressions) Value { vector := ev.evalVector(args[0]) for _, el := range vector { - el.Metric.Del(model.MetricNameLabel) - el.Value = model.SampleValue(math.Floor(float64(el.Value))) + el.Metric = copyLabels(el.Metric, false) + el.Value = math.Floor(float64(el.Value)) } return vector } -// === max_over_time(matrix model.ValMatrix) Vector === -func funcMaxOverTime(ev *evaluator, args Expressions) model.Value { - return aggrOverTime(ev, args, func(values []model.SamplePair) model.SampleValue { +// === max_over_time(matrix ValueTypeMatrix) Vector === +func funcMaxOverTime(ev *evaluator, args Expressions) Value { + return aggrOverTime(ev, args, func(values []samplePair) float64 { max := math.Inf(-1) for _, v := range values { - max = math.Max(max, float64(v.Value)) + max = math.Max(max, float64(v.v)) } - return model.SampleValue(max) + return max }) } -// === min_over_time(matrix model.ValMatrix) Vector === -func funcMinOverTime(ev *evaluator, args Expressions) model.Value { - return aggrOverTime(ev, args, func(values []model.SamplePair) model.SampleValue { +// === min_over_time(matrix ValueTypeMatrix) Vector === +func funcMinOverTime(ev *evaluator, args Expressions) Value { + return aggrOverTime(ev, args, func(values []samplePair) float64 { min := math.Inf(1) for _, v := range values { - min = math.Min(min, float64(v.Value)) + min = math.Min(min, float64(v.v)) } - return model.SampleValue(min) + return min }) } -// === sum_over_time(matrix model.ValMatrix) Vector === -func funcSumOverTime(ev *evaluator, args Expressions) model.Value { - return aggrOverTime(ev, args, func(values []model.SamplePair) model.SampleValue { - var sum model.SampleValue +// === sum_over_time(matrix ValueTypeMatrix) Vector === +func funcSumOverTime(ev *evaluator, args Expressions) Value { + return aggrOverTime(ev, args, func(values []samplePair) float64 { + var sum float64 for _, v := range values { - sum += v.Value + sum += v.v } return sum }) } -// === quantile_over_time(matrix model.ValMatrix) Vector === -func funcQuantileOverTime(ev *evaluator, args Expressions) model.Value { +// === quantile_over_time(matrix ValueTypeMatrix) Vector === +func funcQuantileOverTime(ev *evaluator, args Expressions) Value { q := ev.evalFloat(args[0]) mat := ev.evalMatrix(args[1]) resultVector := vector{} @@ -502,41 +497,41 @@ func funcQuantileOverTime(ev *evaluator, args Expressions) model.Value { continue } - el.Metric.Del(model.MetricNameLabel) + el.Metric = copyLabels(el.Metric, false) values := make(vectorByValueHeap, 0, len(el.Values)) for _, v := range el.Values { - values = append(values, &sample{Value: v.Value}) + values = append(values, sample{Value: v.v}) } - resultVector = append(resultVector, &sample{ + resultVector = append(resultVector, sample{ Metric: el.Metric, - Value: model.SampleValue(quantile(q, values)), + Value: quantile(q, values), Timestamp: ev.Timestamp, }) } return resultVector } -// === stddev_over_time(matrix model.ValMatrix) Vector === -func funcStddevOverTime(ev *evaluator, args Expressions) model.Value { - return aggrOverTime(ev, args, func(values []model.SamplePair) model.SampleValue { - var sum, squaredSum, count model.SampleValue +// === stddev_over_time(matrix ValueTypeMatrix) Vector === +func funcStddevOverTime(ev *evaluator, args Expressions) Value { + return aggrOverTime(ev, args, func(values []samplePair) float64 { + var sum, squaredSum, count float64 for _, v := range values { - sum += v.Value - squaredSum += v.Value * v.Value + sum += v.v + squaredSum += v.v * v.v count++ } avg := sum / count - return model.SampleValue(math.Sqrt(float64(squaredSum/count - avg*avg))) + return math.Sqrt(float64(squaredSum/count - avg*avg)) }) } -// === stdvar_over_time(matrix model.ValMatrix) Vector === -func funcStdvarOverTime(ev *evaluator, args Expressions) model.Value { - return aggrOverTime(ev, args, func(values []model.SamplePair) model.SampleValue { - var sum, squaredSum, count model.SampleValue +// === stdvar_over_time(matrix ValueTypeMatrix) Vector === +func funcStdvarOverTime(ev *evaluator, args Expressions) Value { + return aggrOverTime(ev, args, func(values []samplePair) float64 { + var sum, squaredSum, count float64 for _, v := range values { - sum += v.Value - squaredSum += v.Value * v.Value + sum += v.v + squaredSum += v.v * v.v count++ } avg := sum / count @@ -544,97 +539,95 @@ func funcStdvarOverTime(ev *evaluator, args Expressions) model.Value { }) } -// === abs(vector model.ValVector) Vector === -func funcAbs(ev *evaluator, args Expressions) model.Value { +// === abs(vector ValueTypeVector) Vector === +func funcAbs(ev *evaluator, args Expressions) Value { vector := ev.evalVector(args[0]) for _, el := range vector { - el.Metric.Del(model.MetricNameLabel) - el.Value = model.SampleValue(math.Abs(float64(el.Value))) + el.Metric = copyLabels(el.Metric, false) + el.Value = math.Abs(float64(el.Value)) } return vector } -// === absent(vector model.ValVector) Vector === -func funcAbsent(ev *evaluator, args Expressions) model.Value { +// === absent(vector ValueTypeVector) Vector === +func funcAbsent(ev *evaluator, args Expressions) Value { if len(ev.evalVector(args[0])) > 0 { return vector{} } - m := model.Metric{} + m := []labels.Label{} + if vs, ok := args[0].(*VectorSelector); ok { - for _, matcher := range vs.LabelMatchers { - if matcher.Type == metric.Equal && matcher.Name != model.MetricNameLabel { - m[matcher.Name] = matcher.Value + for _, ma := range vs.LabelMatchers { + if ma.Type == MatchEqual && ma.Name != MetricNameLabel { + m = append(m, labels.Label{Name: ma.Name, Value: ma.Value}) } } } return vector{ - &sample{ - Metric: metric.Metric{ - Metric: m, - Copied: true, - }, + sample{ + Metric: labels.New(m...), Value: 1, Timestamp: ev.Timestamp, }, } } -// === ceil(vector model.ValVector) Vector === -func funcCeil(ev *evaluator, args Expressions) model.Value { +// === ceil(vector ValueTypeVector) Vector === +func funcCeil(ev *evaluator, args Expressions) Value { vector := ev.evalVector(args[0]) for _, el := range vector { - el.Metric.Del(model.MetricNameLabel) - el.Value = model.SampleValue(math.Ceil(float64(el.Value))) + el.Metric = copyLabels(el.Metric, false) + el.Value = math.Ceil(float64(el.Value)) } return vector } -// === exp(vector model.ValVector) Vector === -func funcExp(ev *evaluator, args Expressions) model.Value { +// === exp(vector ValueTypeVector) Vector === +func funcExp(ev *evaluator, args Expressions) Value { vector := ev.evalVector(args[0]) for _, el := range vector { - el.Metric.Del(model.MetricNameLabel) - el.Value = model.SampleValue(math.Exp(float64(el.Value))) + el.Metric = copyLabels(el.Metric, false) + el.Value = math.Exp(float64(el.Value)) } return vector } // === sqrt(vector VectorNode) Vector === -func funcSqrt(ev *evaluator, args Expressions) model.Value { +func funcSqrt(ev *evaluator, args Expressions) Value { vector := ev.evalVector(args[0]) for _, el := range vector { - el.Metric.Del(model.MetricNameLabel) - el.Value = model.SampleValue(math.Sqrt(float64(el.Value))) + el.Metric = copyLabels(el.Metric, false) + el.Value = math.Sqrt(float64(el.Value)) } return vector } -// === ln(vector model.ValVector) Vector === -func funcLn(ev *evaluator, args Expressions) model.Value { +// === ln(vector ValueTypeVector) Vector === +func funcLn(ev *evaluator, args Expressions) Value { vector := ev.evalVector(args[0]) for _, el := range vector { - el.Metric.Del(model.MetricNameLabel) - el.Value = model.SampleValue(math.Log(float64(el.Value))) + el.Metric = copyLabels(el.Metric, false) + el.Value = math.Log(float64(el.Value)) } return vector } -// === log2(vector model.ValVector) Vector === -func funcLog2(ev *evaluator, args Expressions) model.Value { +// === log2(vector ValueTypeVector) Vector === +func funcLog2(ev *evaluator, args Expressions) Value { vector := ev.evalVector(args[0]) for _, el := range vector { - el.Metric.Del(model.MetricNameLabel) - el.Value = model.SampleValue(math.Log2(float64(el.Value))) + el.Metric = copyLabels(el.Metric, false) + el.Value = math.Log2(float64(el.Value)) } return vector } -// === log10(vector model.ValVector) Vector === -func funcLog10(ev *evaluator, args Expressions) model.Value { +// === log10(vector ValueTypeVector) Vector === +func funcLog10(ev *evaluator, args Expressions) Value { vector := ev.evalVector(args[0]) for _, el := range vector { - el.Metric.Del(model.MetricNameLabel) - el.Value = model.SampleValue(math.Log10(float64(el.Value))) + el.Metric = copyLabels(el.Metric, false) + el.Value = math.Log10(float64(el.Value)) } return vector } @@ -642,20 +635,18 @@ func funcLog10(ev *evaluator, args Expressions) model.Value { // linearRegression performs a least-square linear regression analysis on the // provided SamplePairs. It returns the slope, and the intercept value at the // provided time. -func linearRegression(samples []model.SamplePair, interceptTime model.Time) (slope, intercept model.SampleValue) { +func linearRegression(samples []samplePair, interceptTime int64) (slope, intercept float64) { var ( - n model.SampleValue - sumX, sumY model.SampleValue - sumXY, sumX2 model.SampleValue + n float64 + sumX, sumY float64 + sumXY, sumX2 float64 ) for _, sample := range samples { - x := model.SampleValue( - model.Time(sample.Timestamp-interceptTime).UnixNano(), - ) / 1e9 + x := float64(sample.t-interceptTime) / 1e6 n += 1.0 - sumY += sample.Value + sumY += sample.v sumX += x - sumXY += x * sample.Value + sumXY += x * sample.v sumX2 += x * x } covXY := sumXY - sumX*sumY/n @@ -666,8 +657,8 @@ func linearRegression(samples []model.SamplePair, interceptTime model.Time) (slo return slope, intercept } -// === deriv(node model.ValMatrix) Vector === -func funcDeriv(ev *evaluator, args Expressions) model.Value { +// === deriv(node ValueTypeMatrix) Vector === +func funcDeriv(ev *evaluator, args Expressions) Value { mat := ev.evalMatrix(args[0]) resultVector := make(vector, 0, len(mat)) @@ -678,22 +669,22 @@ func funcDeriv(ev *evaluator, args Expressions) model.Value { continue } slope, _ := linearRegression(samples.Values, 0) - resultSample := &sample{ - Metric: samples.Metric, + resultSample := sample{ + Metric: copyLabels(samples.Metric, false), Value: slope, Timestamp: ev.Timestamp, } - resultSample.Metric.Del(model.MetricNameLabel) + resultVector = append(resultVector, resultSample) } return resultVector } -// === predict_linear(node model.ValMatrix, k model.ValScalar) Vector === -func funcPredictLinear(ev *evaluator, args Expressions) model.Value { +// === predict_linear(node ValueTypeMatrix, k ValueTypeScalar) Vector === +func funcPredictLinear(ev *evaluator, args Expressions) Value { mat := ev.evalMatrix(args[0]) resultVector := make(vector, 0, len(mat)) - duration := model.SampleValue(ev.evalFloat(args[1])) + duration := ev.evalFloat(args[1]) for _, samples := range mat { // No sense in trying to predict anything without at least two points. @@ -702,48 +693,51 @@ func funcPredictLinear(ev *evaluator, args Expressions) model.Value { continue } slope, intercept := linearRegression(samples.Values, ev.Timestamp) - resultSample := &sample{ - Metric: samples.Metric, + + resultVector = append(resultVector, sample{ + Metric: copyLabels(samples.Metric, false), Value: slope*duration + intercept, Timestamp: ev.Timestamp, - } - resultSample.Metric.Del(model.MetricNameLabel) - resultVector = append(resultVector, resultSample) + }) } return resultVector } -// === histogram_quantile(k model.ValScalar, vector model.ValVector) Vector === -func funcHistogramQuantile(ev *evaluator, args Expressions) model.Value { - q := model.SampleValue(ev.evalFloat(args[0])) +// === histogram_quantile(k ValueTypeScalar, vector ValueTypeVector) Vector === +func funcHistogramQuantile(ev *evaluator, args Expressions) Value { + q := ev.evalFloat(args[0]) inVec := ev.evalVector(args[1]) outVec := vector{} signatureToMetricWithBuckets := map[uint64]*metricWithBuckets{} for _, el := range inVec { upperBound, err := strconv.ParseFloat( - string(el.Metric.Metric[model.BucketLabel]), 64, + el.Metric.Get(model.BucketLabel), 64, ) if err != nil { // Oops, no bucket label or malformed label value. Skip. // TODO(beorn7): Issue a warning somehow. continue } - signature := model.SignatureWithoutLabels(el.Metric.Metric, excludedLabels) - mb, ok := signatureToMetricWithBuckets[signature] + hash := hashWithoutLabels(el.Metric, excludedLabels...) + + mb, ok := signatureToMetricWithBuckets[hash] if !ok { - el.Metric.Del(model.BucketLabel) - el.Metric.Del(model.MetricNameLabel) + el.Metric = modifiedLabels(el.Metric, []string{ + string(model.BucketLabel), + MetricNameLabel, + }, nil) + mb = &metricWithBuckets{el.Metric, nil} - signatureToMetricWithBuckets[signature] = mb + signatureToMetricWithBuckets[hash] = mb } mb.buckets = append(mb.buckets, bucket{upperBound, el.Value}) } for _, mb := range signatureToMetricWithBuckets { - outVec = append(outVec, &sample{ + outVec = append(outVec, sample{ Metric: mb.metric, - Value: model.SampleValue(bucketQuantile(q, mb.buckets)), + Value: bucketQuantile(q, mb.buckets), Timestamp: ev.Timestamp, }) } @@ -751,68 +745,64 @@ func funcHistogramQuantile(ev *evaluator, args Expressions) model.Value { return outVec } -// === resets(matrix model.ValMatrix) Vector === -func funcResets(ev *evaluator, args Expressions) model.Value { +// === resets(matrix ValueTypeMatrix) Vector === +func funcResets(ev *evaluator, args Expressions) Value { in := ev.evalMatrix(args[0]) out := make(vector, 0, len(in)) for _, samples := range in { resets := 0 - prev := model.SampleValue(samples.Values[0].Value) + prev := samples.Values[0].v for _, sample := range samples.Values[1:] { - current := sample.Value + current := sample.v if current < prev { resets++ } prev = current } - rs := &sample{ - Metric: samples.Metric, - Value: model.SampleValue(resets), + out = append(out, sample{ + Metric: copyLabels(samples.Metric, false), + Value: float64(resets), Timestamp: ev.Timestamp, - } - rs.Metric.Del(model.MetricNameLabel) - out = append(out, rs) + }) } return out } -// === changes(matrix model.ValMatrix) Vector === -func funcChanges(ev *evaluator, args Expressions) model.Value { +// === changes(matrix ValueTypeMatrix) Vector === +func funcChanges(ev *evaluator, args Expressions) Value { in := ev.evalMatrix(args[0]) out := make(vector, 0, len(in)) for _, samples := range in { changes := 0 - prev := model.SampleValue(samples.Values[0].Value) + prev := samples.Values[0].v for _, sample := range samples.Values[1:] { - current := sample.Value + current := sample.v if current != prev && !(math.IsNaN(float64(current)) && math.IsNaN(float64(prev))) { changes++ } prev = current } - rs := &sample{ - Metric: samples.Metric, - Value: model.SampleValue(changes), + out = append(out, sample{ + Metric: copyLabels(samples.Metric, false), + Value: float64(changes), Timestamp: ev.Timestamp, - } - rs.Metric.Del(model.MetricNameLabel) - out = append(out, rs) + }) } return out } -// === label_replace(vector model.ValVector, dst_label, replacement, src_labelname, regex model.ValString) Vector === -func funcLabelReplace(ev *evaluator, args Expressions) model.Value { +// === label_replace(vector ValueTypeVector, dst_label, replacement, src_labelname, regex ValueTypeString) Vector === +func funcLabelReplace(ev *evaluator, args Expressions) Value { var ( vector = ev.evalVector(args[0]) - dst = model.LabelName(ev.evalString(args[1]).Value) - repl = ev.evalString(args[2]).Value - src = model.LabelName(ev.evalString(args[3]).Value) - regexStr = ev.evalString(args[4]).Value + dst = ev.evalString(args[1]).s + repl = ev.evalString(args[2]).s + src = ev.evalString(args[3]).s + regexStr = ev.evalString(args[4]).s ) regex, err := regexp.Compile("^(?:" + regexStr + ")$") @@ -823,26 +813,27 @@ func funcLabelReplace(ev *evaluator, args Expressions) model.Value { ev.errorf("invalid destination label name in label_replace(): %s", dst) } - outSet := make(map[model.Fingerprint]struct{}, len(vector)) + outSet := make(map[uint64]struct{}, len(vector)) for _, el := range vector { - srcVal := string(el.Metric.Metric[src]) + srcVal := el.Metric.Get(src) indexes := regex.FindStringSubmatchIndex(srcVal) // If there is no match, no replacement should take place. if indexes == nil { continue } res := regex.ExpandString([]byte{}, repl, srcVal, indexes) - if len(res) == 0 { - el.Metric.Del(dst) - } else { - el.Metric.Set(dst, model.LabelValue(res)) + del := []string{dst} + add := []labels.Label{} + if len(res) > 0 { + add = append(add, labels.Label{Name: dst, Value: string(res)}) } + el.Metric = modifiedLabels(el.Metric, del, add) - fp := el.Metric.Metric.Fingerprint() - if _, exists := outSet[fp]; exists { - ev.errorf("duplicated label set in output of label_replace(): %s", el.Metric.Metric) + h := el.Metric.Hash() + if _, ok := outSet[h]; ok { + ev.errorf("duplicated label set in output of label_replace(): %s", el.Metric) } else { - outSet[fp] = struct{}{} + outSet[h] = struct{}{} } } @@ -850,31 +841,31 @@ func funcLabelReplace(ev *evaluator, args Expressions) model.Value { } // === vector(s scalar) Vector === -func funcVector(ev *evaluator, args Expressions) model.Value { +func funcVector(ev *evaluator, args Expressions) Value { return vector{ - &sample{ - Metric: metric.Metric{}, - Value: model.SampleValue(ev.evalFloat(args[0])), + sample{ + Metric: labels.Labels{}, + Value: ev.evalFloat(args[0]), Timestamp: ev.Timestamp, }, } } // Common code for date related functions. -func dateWrapper(ev *evaluator, args Expressions, f func(time.Time) model.SampleValue) model.Value { +func dateWrapper(ev *evaluator, args Expressions, f func(time.Time) float64) Value { var v vector if len(args) == 0 { v = vector{ - &sample{ - Metric: metric.Metric{}, - Value: model.SampleValue(ev.Timestamp.Unix()), + sample{ + Metric: labels.Labels{}, + Value: float64(ev.Timestamp) / 1000, }, } } else { v = ev.evalVector(args[0]) } for _, el := range v { - el.Metric.Del(model.MetricNameLabel) + el.Metric = copyLabels(el.Metric, false) t := time.Unix(int64(el.Value), 0).UTC() el.Value = f(t) } @@ -882,337 +873,337 @@ func dateWrapper(ev *evaluator, args Expressions, f func(time.Time) model.Sample } // === days_in_month(v vector) scalar === -func funcDaysInMonth(ev *evaluator, args Expressions) model.Value { - return dateWrapper(ev, args, func(t time.Time) model.SampleValue { - return model.SampleValue(32 - time.Date(t.Year(), t.Month(), 32, 0, 0, 0, 0, time.UTC).Day()) +func funcDaysInMonth(ev *evaluator, args Expressions) Value { + return dateWrapper(ev, args, func(t time.Time) float64 { + return float64(32 - time.Date(t.Year(), t.Month(), 32, 0, 0, 0, 0, time.UTC).Day()) }) } // === day_of_month(v vector) scalar === -func funcDayOfMonth(ev *evaluator, args Expressions) model.Value { - return dateWrapper(ev, args, func(t time.Time) model.SampleValue { - return model.SampleValue(t.Day()) +func funcDayOfMonth(ev *evaluator, args Expressions) Value { + return dateWrapper(ev, args, func(t time.Time) float64 { + return float64(t.Day()) }) } // === day_of_week(v vector) scalar === -func funcDayOfWeek(ev *evaluator, args Expressions) model.Value { - return dateWrapper(ev, args, func(t time.Time) model.SampleValue { - return model.SampleValue(t.Weekday()) +func funcDayOfWeek(ev *evaluator, args Expressions) Value { + return dateWrapper(ev, args, func(t time.Time) float64 { + return float64(t.Weekday()) }) } // === hour(v vector) scalar === -func funcHour(ev *evaluator, args Expressions) model.Value { - return dateWrapper(ev, args, func(t time.Time) model.SampleValue { - return model.SampleValue(t.Hour()) +func funcHour(ev *evaluator, args Expressions) Value { + return dateWrapper(ev, args, func(t time.Time) float64 { + return float64(t.Hour()) }) } // === minute(v vector) scalar === -func funcMinute(ev *evaluator, args Expressions) model.Value { - return dateWrapper(ev, args, func(t time.Time) model.SampleValue { - return model.SampleValue(t.Minute()) +func funcMinute(ev *evaluator, args Expressions) Value { + return dateWrapper(ev, args, func(t time.Time) float64 { + return float64(t.Minute()) }) } // === month(v vector) scalar === -func funcMonth(ev *evaluator, args Expressions) model.Value { - return dateWrapper(ev, args, func(t time.Time) model.SampleValue { - return model.SampleValue(t.Month()) +func funcMonth(ev *evaluator, args Expressions) Value { + return dateWrapper(ev, args, func(t time.Time) float64 { + return float64(t.Month()) }) } // === year(v vector) scalar === -func funcYear(ev *evaluator, args Expressions) model.Value { - return dateWrapper(ev, args, func(t time.Time) model.SampleValue { - return model.SampleValue(t.Year()) +func funcYear(ev *evaluator, args Expressions) Value { + return dateWrapper(ev, args, func(t time.Time) float64 { + return float64(t.Year()) }) } var functions = map[string]*Function{ "abs": { Name: "abs", - ArgTypes: []model.ValueType{model.ValVector}, - ReturnType: model.ValVector, + ArgTypes: []ValueType{ValueTypeVector}, + ReturnType: ValueTypeVector, Call: funcAbs, }, "absent": { Name: "absent", - ArgTypes: []model.ValueType{model.ValVector}, - ReturnType: model.ValVector, + ArgTypes: []ValueType{ValueTypeVector}, + ReturnType: ValueTypeVector, Call: funcAbsent, }, "avg_over_time": { Name: "avg_over_time", - ArgTypes: []model.ValueType{model.ValMatrix}, - ReturnType: model.ValVector, + ArgTypes: []ValueType{ValueTypeMatrix}, + ReturnType: ValueTypeVector, Call: funcAvgOverTime, }, "ceil": { Name: "ceil", - ArgTypes: []model.ValueType{model.ValVector}, - ReturnType: model.ValVector, + ArgTypes: []ValueType{ValueTypeVector}, + ReturnType: ValueTypeVector, Call: funcCeil, }, "changes": { Name: "changes", - ArgTypes: []model.ValueType{model.ValMatrix}, - ReturnType: model.ValVector, + ArgTypes: []ValueType{ValueTypeMatrix}, + ReturnType: ValueTypeVector, Call: funcChanges, }, "clamp_max": { Name: "clamp_max", - ArgTypes: []model.ValueType{model.ValVector, model.ValScalar}, - ReturnType: model.ValVector, + ArgTypes: []ValueType{ValueTypeVector, ValueTypeScalar}, + ReturnType: ValueTypeVector, Call: funcClampMax, }, "clamp_min": { Name: "clamp_min", - ArgTypes: []model.ValueType{model.ValVector, model.ValScalar}, - ReturnType: model.ValVector, + ArgTypes: []ValueType{ValueTypeVector, ValueTypeScalar}, + ReturnType: ValueTypeVector, Call: funcClampMin, }, "count_over_time": { Name: "count_over_time", - ArgTypes: []model.ValueType{model.ValMatrix}, - ReturnType: model.ValVector, + ArgTypes: []ValueType{ValueTypeMatrix}, + ReturnType: ValueTypeVector, Call: funcCountOverTime, }, "count_scalar": { Name: "count_scalar", - ArgTypes: []model.ValueType{model.ValVector}, - ReturnType: model.ValScalar, + ArgTypes: []ValueType{ValueTypeVector}, + ReturnType: ValueTypeScalar, Call: funcCountScalar, }, "days_in_month": { Name: "days_in_month", - ArgTypes: []model.ValueType{model.ValVector}, + ArgTypes: []ValueType{ValueTypeVector}, OptionalArgs: 1, - ReturnType: model.ValVector, + ReturnType: ValueTypeVector, Call: funcDaysInMonth, }, "day_of_month": { Name: "day_of_month", - ArgTypes: []model.ValueType{model.ValVector}, + ArgTypes: []ValueType{ValueTypeVector}, OptionalArgs: 1, - ReturnType: model.ValVector, + ReturnType: ValueTypeVector, Call: funcDayOfMonth, }, "day_of_week": { Name: "day_of_week", - ArgTypes: []model.ValueType{model.ValVector}, + ArgTypes: []ValueType{ValueTypeVector}, OptionalArgs: 1, - ReturnType: model.ValVector, + ReturnType: ValueTypeVector, Call: funcDayOfWeek, }, "delta": { Name: "delta", - ArgTypes: []model.ValueType{model.ValMatrix}, - ReturnType: model.ValVector, + ArgTypes: []ValueType{ValueTypeMatrix}, + ReturnType: ValueTypeVector, Call: funcDelta, }, "deriv": { Name: "deriv", - ArgTypes: []model.ValueType{model.ValMatrix}, - ReturnType: model.ValVector, + ArgTypes: []ValueType{ValueTypeMatrix}, + ReturnType: ValueTypeVector, Call: funcDeriv, }, "drop_common_labels": { Name: "drop_common_labels", - ArgTypes: []model.ValueType{model.ValVector}, - ReturnType: model.ValVector, + ArgTypes: []ValueType{ValueTypeVector}, + ReturnType: ValueTypeVector, Call: funcDropCommonLabels, }, "exp": { Name: "exp", - ArgTypes: []model.ValueType{model.ValVector}, - ReturnType: model.ValVector, + ArgTypes: []ValueType{ValueTypeVector}, + ReturnType: ValueTypeVector, Call: funcExp, }, "floor": { Name: "floor", - ArgTypes: []model.ValueType{model.ValVector}, - ReturnType: model.ValVector, + ArgTypes: []ValueType{ValueTypeVector}, + ReturnType: ValueTypeVector, Call: funcFloor, }, "histogram_quantile": { Name: "histogram_quantile", - ArgTypes: []model.ValueType{model.ValScalar, model.ValVector}, - ReturnType: model.ValVector, + ArgTypes: []ValueType{ValueTypeScalar, ValueTypeVector}, + ReturnType: ValueTypeVector, Call: funcHistogramQuantile, }, "holt_winters": { Name: "holt_winters", - ArgTypes: []model.ValueType{model.ValMatrix, model.ValScalar, model.ValScalar}, - ReturnType: model.ValVector, + ArgTypes: []ValueType{ValueTypeMatrix, ValueTypeScalar, ValueTypeScalar}, + ReturnType: ValueTypeVector, Call: funcHoltWinters, }, "hour": { Name: "hour", - ArgTypes: []model.ValueType{model.ValVector}, + ArgTypes: []ValueType{ValueTypeVector}, OptionalArgs: 1, - ReturnType: model.ValVector, + ReturnType: ValueTypeVector, Call: funcHour, }, "idelta": { Name: "idelta", - ArgTypes: []model.ValueType{model.ValMatrix}, - ReturnType: model.ValVector, + ArgTypes: []ValueType{ValueTypeMatrix}, + ReturnType: ValueTypeVector, Call: funcIdelta, }, "increase": { Name: "increase", - ArgTypes: []model.ValueType{model.ValMatrix}, - ReturnType: model.ValVector, + ArgTypes: []ValueType{ValueTypeMatrix}, + ReturnType: ValueTypeVector, Call: funcIncrease, }, "irate": { Name: "irate", - ArgTypes: []model.ValueType{model.ValMatrix}, - ReturnType: model.ValVector, + ArgTypes: []ValueType{ValueTypeMatrix}, + ReturnType: ValueTypeVector, Call: funcIrate, }, "label_replace": { Name: "label_replace", - ArgTypes: []model.ValueType{model.ValVector, model.ValString, model.ValString, model.ValString, model.ValString}, - ReturnType: model.ValVector, + ArgTypes: []ValueType{ValueTypeVector, ValueTypeString, ValueTypeString, ValueTypeString, ValueTypeString}, + ReturnType: ValueTypeVector, Call: funcLabelReplace, }, "ln": { Name: "ln", - ArgTypes: []model.ValueType{model.ValVector}, - ReturnType: model.ValVector, + ArgTypes: []ValueType{ValueTypeVector}, + ReturnType: ValueTypeVector, Call: funcLn, }, "log10": { Name: "log10", - ArgTypes: []model.ValueType{model.ValVector}, - ReturnType: model.ValVector, + ArgTypes: []ValueType{ValueTypeVector}, + ReturnType: ValueTypeVector, Call: funcLog10, }, "log2": { Name: "log2", - ArgTypes: []model.ValueType{model.ValVector}, - ReturnType: model.ValVector, + ArgTypes: []ValueType{ValueTypeVector}, + ReturnType: ValueTypeVector, Call: funcLog2, }, "max_over_time": { Name: "max_over_time", - ArgTypes: []model.ValueType{model.ValMatrix}, - ReturnType: model.ValVector, + ArgTypes: []ValueType{ValueTypeMatrix}, + ReturnType: ValueTypeVector, Call: funcMaxOverTime, }, "min_over_time": { Name: "min_over_time", - ArgTypes: []model.ValueType{model.ValMatrix}, - ReturnType: model.ValVector, + ArgTypes: []ValueType{ValueTypeMatrix}, + ReturnType: ValueTypeVector, Call: funcMinOverTime, }, "minute": { Name: "minute", - ArgTypes: []model.ValueType{model.ValVector}, + ArgTypes: []ValueType{ValueTypeVector}, OptionalArgs: 1, - ReturnType: model.ValVector, + ReturnType: ValueTypeVector, Call: funcMinute, }, "month": { Name: "month", - ArgTypes: []model.ValueType{model.ValVector}, + ArgTypes: []ValueType{ValueTypeVector}, OptionalArgs: 1, - ReturnType: model.ValVector, + ReturnType: ValueTypeVector, Call: funcMonth, }, "predict_linear": { Name: "predict_linear", - ArgTypes: []model.ValueType{model.ValMatrix, model.ValScalar}, - ReturnType: model.ValVector, + ArgTypes: []ValueType{ValueTypeMatrix, ValueTypeScalar}, + ReturnType: ValueTypeVector, Call: funcPredictLinear, }, "quantile_over_time": { Name: "quantile_over_time", - ArgTypes: []model.ValueType{model.ValScalar, model.ValMatrix}, - ReturnType: model.ValVector, + ArgTypes: []ValueType{ValueTypeScalar, ValueTypeMatrix}, + ReturnType: ValueTypeVector, Call: funcQuantileOverTime, }, "rate": { Name: "rate", - ArgTypes: []model.ValueType{model.ValMatrix}, - ReturnType: model.ValVector, + ArgTypes: []ValueType{ValueTypeMatrix}, + ReturnType: ValueTypeVector, Call: funcRate, }, "resets": { Name: "resets", - ArgTypes: []model.ValueType{model.ValMatrix}, - ReturnType: model.ValVector, + ArgTypes: []ValueType{ValueTypeMatrix}, + ReturnType: ValueTypeVector, Call: funcResets, }, "round": { Name: "round", - ArgTypes: []model.ValueType{model.ValVector, model.ValScalar}, + ArgTypes: []ValueType{ValueTypeVector, ValueTypeScalar}, OptionalArgs: 1, - ReturnType: model.ValVector, + ReturnType: ValueTypeVector, Call: funcRound, }, "scalar": { Name: "scalar", - ArgTypes: []model.ValueType{model.ValVector}, - ReturnType: model.ValScalar, + ArgTypes: []ValueType{ValueTypeVector}, + ReturnType: ValueTypeScalar, Call: funcScalar, }, "sort": { Name: "sort", - ArgTypes: []model.ValueType{model.ValVector}, - ReturnType: model.ValVector, + ArgTypes: []ValueType{ValueTypeVector}, + ReturnType: ValueTypeVector, Call: funcSort, }, "sort_desc": { Name: "sort_desc", - ArgTypes: []model.ValueType{model.ValVector}, - ReturnType: model.ValVector, + ArgTypes: []ValueType{ValueTypeVector}, + ReturnType: ValueTypeVector, Call: funcSortDesc, }, "sqrt": { Name: "sqrt", - ArgTypes: []model.ValueType{model.ValVector}, - ReturnType: model.ValVector, + ArgTypes: []ValueType{ValueTypeVector}, + ReturnType: ValueTypeVector, Call: funcSqrt, }, "stddev_over_time": { Name: "stddev_over_time", - ArgTypes: []model.ValueType{model.ValMatrix}, - ReturnType: model.ValVector, + ArgTypes: []ValueType{ValueTypeMatrix}, + ReturnType: ValueTypeVector, Call: funcStddevOverTime, }, "stdvar_over_time": { Name: "stdvar_over_time", - ArgTypes: []model.ValueType{model.ValMatrix}, - ReturnType: model.ValVector, + ArgTypes: []ValueType{ValueTypeMatrix}, + ReturnType: ValueTypeVector, Call: funcStdvarOverTime, }, "sum_over_time": { Name: "sum_over_time", - ArgTypes: []model.ValueType{model.ValMatrix}, - ReturnType: model.ValVector, + ArgTypes: []ValueType{ValueTypeMatrix}, + ReturnType: ValueTypeVector, Call: funcSumOverTime, }, "time": { Name: "time", - ArgTypes: []model.ValueType{}, - ReturnType: model.ValScalar, + ArgTypes: []ValueType{}, + ReturnType: ValueTypeScalar, Call: funcTime, }, "vector": { Name: "vector", - ArgTypes: []model.ValueType{model.ValScalar}, - ReturnType: model.ValVector, + ArgTypes: []ValueType{ValueTypeScalar}, + ReturnType: ValueTypeVector, Call: funcVector, }, "year": { Name: "year", - ArgTypes: []model.ValueType{model.ValVector}, + ArgTypes: []ValueType{ValueTypeVector}, OptionalArgs: 1, - ReturnType: model.ValVector, + ReturnType: ValueTypeVector, Call: funcYear, }, } @@ -1241,7 +1232,7 @@ func (s vectorByValueHeap) Swap(i, j int) { } func (s *vectorByValueHeap) Push(x interface{}) { - *s = append(*s, x.(*sample)) + *s = append(*s, x.(sample)) } func (s *vectorByValueHeap) Pop() interface{} { @@ -1270,7 +1261,7 @@ func (s vectorByReverseValueHeap) Swap(i, j int) { } func (s *vectorByReverseValueHeap) Push(x interface{}) { - *s = append(*s, x.(*sample)) + *s = append(*s, x.(sample)) } func (s *vectorByReverseValueHeap) Pop() interface{} { diff --git a/promql/parse.go b/promql/parse.go index 891d46c90..fd1f94a23 100644 --- a/promql/parse.go +++ b/promql/parse.go @@ -16,14 +16,15 @@ package promql import ( "fmt" "runtime" + "sort" "strconv" "strings" "time" + "github.com/fabxc/tsdb/labels" "github.com/prometheus/common/log" "github.com/prometheus/common/model" - "github.com/prometheus/prometheus/storage/metric" "github.com/prometheus/prometheus/util/strutil" ) @@ -73,7 +74,7 @@ func ParseExpr(input string) (Expr, error) { } // ParseMetric parses the input into a metric -func ParseMetric(input string) (m model.Metric, err error) { +func ParseMetric(input string) (m labels.Labels, err error) { p := newParser(input) defer p.recover(&err) @@ -86,7 +87,7 @@ func ParseMetric(input string) (m model.Metric, err error) { // ParseMetricSelector parses the provided textual metric selector into a list of // label matchers. -func ParseMetricSelector(input string) (m metric.LabelMatchers, err error) { +func ParseMetricSelector(input string) (m []*LabelMatcher, err error) { p := newParser(input) defer p.recover(&err) @@ -102,7 +103,7 @@ func ParseMetricSelector(input string) (m metric.LabelMatchers, err error) { } // parseSeriesDesc parses the description of a time series. -func parseSeriesDesc(input string) (model.Metric, []sequenceValue, error) { +func parseSeriesDesc(input string) (labels.Labels, []sequenceValue, error) { p := newParser(input) p.lex.seriesDesc = true @@ -153,7 +154,7 @@ func (p *parser) parseExpr() (expr Expr, err error) { // sequenceValue is an omittable value in a sequence of time series values. type sequenceValue struct { - value model.SampleValue + value float64 omitted bool } @@ -161,11 +162,11 @@ func (v sequenceValue) String() string { if v.omitted { return "_" } - return v.value.String() + return fmt.Sprintf("%f", v.value) } // parseSeriesDesc parses a description of a time series into its metric and value sequence. -func (p *parser) parseSeriesDesc() (m model.Metric, vals []sequenceValue, err error) { +func (p *parser) parseSeriesDesc() (m labels.Labels, vals []sequenceValue, err error) { defer p.recover(&err) m = p.metric() @@ -202,7 +203,7 @@ func (p *parser) parseSeriesDesc() (m model.Metric, vals []sequenceValue, err er } k := sign * p.number(p.expect(itemNumber, ctx).val) vals = append(vals, sequenceValue{ - value: model.SampleValue(k), + value: k, }) // If there are no offset repetitions specified, proceed with the next value. @@ -230,7 +231,7 @@ func (p *parser) parseSeriesDesc() (m model.Metric, vals []sequenceValue, err er for i := uint64(0); i < times; i++ { k += offset vals = append(vals, sequenceValue{ - value: model.SampleValue(k), + value: k, }) } } @@ -385,12 +386,12 @@ func (p *parser) alertStmt() *AlertStmt { } var ( - labels = model.LabelSet{} - annotations = model.LabelSet{} + lset labels.Labels + annotations labels.Labels ) if p.peek().typ == itemLabels { p.expect(itemLabels, ctx) - labels = p.labelSet() + lset = p.labelSet() } if p.peek().typ == itemAnnotations { p.expect(itemAnnotations, ctx) @@ -401,7 +402,7 @@ func (p *parser) alertStmt() *AlertStmt { Name: name.val, Expr: expr, Duration: duration, - Labels: labels, + Labels: lset, Annotations: annotations, } } @@ -412,7 +413,7 @@ func (p *parser) recordStmt() *RecordStmt { name := p.expectOneOf(itemIdentifier, itemMetricIdentifier, ctx).val - var lset model.LabelSet + var lset labels.Labels if p.peek().typ == itemLeftBrace { lset = p.labelSet() } @@ -504,7 +505,7 @@ func (p *parser) balance(lhs Expr, op itemType, rhs Expr, vecMatching *VectorMat precd := lhsBE.Op.precedence() - op.precedence() if (precd < 0) || (precd == 0 && op.isRightAssociative()) { balanced := p.balance(lhsBE.RHS, op, rhs, vecMatching, returnBool) - if lhsBE.Op.isComparisonOperator() && !lhsBE.ReturnBool && balanced.Type() == model.ValScalar && lhsBE.LHS.Type() == model.ValScalar { + if lhsBE.Op.isComparisonOperator() && !lhsBE.ReturnBool && balanced.Type() == ValueTypeScalar && lhsBE.LHS.Type() == ValueTypeScalar { p.errorf("comparisons between scalars must use BOOL modifier") } return &BinaryExpr{ @@ -516,7 +517,7 @@ func (p *parser) balance(lhs Expr, op itemType, rhs Expr, vecMatching *VectorMat } } } - if op.isComparisonOperator() && !returnBool && rhs.Type() == model.ValScalar && lhs.Type() == model.ValScalar { + if op.isComparisonOperator() && !returnBool && rhs.Type() == ValueTypeScalar && lhs.Type() == ValueTypeScalar { p.errorf("comparisons between scalars must use BOOL modifier") } return &BinaryExpr{ @@ -631,7 +632,7 @@ func (p *parser) primaryExpr() Expr { switch t := p.next(); { case t.typ == itemNumber: f := p.number(t.val) - return &NumberLiteral{model.SampleValue(f)} + return &NumberLiteral{f} case t.typ == itemString: return &StringLiteral{p.unquoteString(t.val)} @@ -665,19 +666,19 @@ func (p *parser) primaryExpr() Expr { // // '(' , ... ')' // -func (p *parser) labels() model.LabelNames { +func (p *parser) labels() []string { const ctx = "grouping opts" p.expect(itemLeftParen, ctx) - labels := model.LabelNames{} + labels := []string{} if p.peek().typ != itemRightParen { for { id := p.next() if !isLabel(id.val) { p.errorf("unexpected %s in %s, expected label", id.desc(), ctx) } - labels = append(labels, model.LabelName(id.val)) + labels = append(labels, id.val) if p.peek().typ != itemComma { break @@ -702,7 +703,7 @@ func (p *parser) aggrExpr() *AggregateExpr { if !agop.typ.isAggregator() { p.errorf("expected aggregation operator but got %s", agop) } - var grouping model.LabelNames + var grouping []string var keepCommon, without bool modifiersFirst := false @@ -802,22 +803,22 @@ func (p *parser) call(name string) *Call { // // '{' [ '=' , ... ] '}' // -func (p *parser) labelSet() model.LabelSet { - set := model.LabelSet{} +func (p *parser) labelSet() labels.Labels { + set := []labels.Label{} for _, lm := range p.labelMatchers(itemEQL) { - set[lm.Name] = lm.Value + set = append(set, labels.Label{Name: lm.Name, Value: lm.Value}) } - return set + return labels.New(set...) } // labelMatchers parses a set of label matchers. // // '{' [ , ... ] '}' // -func (p *parser) labelMatchers(operators ...itemType) metric.LabelMatchers { +func (p *parser) labelMatchers(operators ...itemType) []*LabelMatcher { const ctx = "label matching" - matchers := metric.LabelMatchers{} + matchers := []*LabelMatcher{} p.expect(itemLeftBrace, ctx) @@ -847,25 +848,21 @@ func (p *parser) labelMatchers(operators ...itemType) metric.LabelMatchers { val := p.unquoteString(p.expect(itemString, ctx).val) // Map the item to the respective match type. - var matchType metric.MatchType + var matchType MatchType switch op { case itemEQL: - matchType = metric.Equal + matchType = MatchEqual case itemNEQ: - matchType = metric.NotEqual + matchType = MatchNotEqual case itemEQLRegex: - matchType = metric.RegexMatch + matchType = MatchRegexp case itemNEQRegex: - matchType = metric.RegexNoMatch + matchType = MatchNotRegexp default: p.errorf("item %q is not a metric match type", op) } - m, err := metric.NewLabelMatcher( - matchType, - model.LabelName(label.val), - model.LabelValue(val), - ) + m, err := NewLabelMatcher(matchType, label.val, val) if err != nil { p.error(err) } @@ -898,9 +895,9 @@ func (p *parser) labelMatchers(operators ...itemType) metric.LabelMatchers { // // [] // -func (p *parser) metric() model.Metric { +func (p *parser) metric() labels.Labels { name := "" - m := model.Metric{} + var m labels.Labels t := p.peek().typ if t == itemIdentifier || t == itemMetricIdentifier { @@ -911,10 +908,11 @@ func (p *parser) metric() model.Metric { p.errorf("missing metric name or metric selector") } if t == itemLeftBrace { - m = model.Metric(p.labelSet()) + m = p.labelSet() } if name != "" { - m[model.MetricNameLabel] = model.LabelValue(name) + m = append(m, labels.Label{Name: MetricNameLabel, Value: name}) + sort.Sort(m) } return m } @@ -943,7 +941,7 @@ func (p *parser) offset() time.Duration { // [] // func (p *parser) vectorSelector(name string) *VectorSelector { - var matchers metric.LabelMatchers + var matchers []*LabelMatcher // Parse label matching if any. if t := p.peek(); t.typ == itemLeftBrace { matchers = p.labelMatchers(itemEQL, itemNEQ, itemEQLRegex, itemNEQRegex) @@ -956,7 +954,7 @@ func (p *parser) vectorSelector(name string) *VectorSelector { } } // Set name label matching. - m, err := metric.NewLabelMatcher(metric.Equal, model.MetricNameLabel, model.LabelValue(name)) + m, err := NewLabelMatcher(MatchEqual, MetricNameLabel, name) if err != nil { panic(err) // Must not happen with metric.Equal. } @@ -970,7 +968,7 @@ func (p *parser) vectorSelector(name string) *VectorSelector { // implicit selection of all metrics (e.g. by a typo). notEmpty := false for _, lm := range matchers { - if !lm.MatchesEmptyString() { + if !lm.matcher().Matches("") { notEmpty = true break } @@ -987,7 +985,7 @@ func (p *parser) vectorSelector(name string) *VectorSelector { // expectType checks the type of the node and raises an error if it // is not of the expected type. -func (p *parser) expectType(node Node, want model.ValueType, context string) { +func (p *parser) expectType(node Node, want ValueType, context string) { t := p.checkType(node) if t != want { p.errorf("expected type %s in %s, got %s", documentedType(want), context, documentedType(t)) @@ -999,12 +997,12 @@ func (p *parser) expectType(node Node, want model.ValueType, context string) { // // Some of these checks are redundant as the the parsing stage does not allow // them, but the costs are small and might reveal errors when making changes. -func (p *parser) checkType(node Node) (typ model.ValueType) { +func (p *parser) checkType(node Node) (typ ValueType) { // For expressions the type is determined by their Type function. // Statements and lists do not have a type but are not invalid either. switch n := node.(type) { case Statements, Expressions, Statement: - typ = model.ValNone + typ = ValueTypeNone case Expr: typ = n.Type() default: @@ -1016,27 +1014,27 @@ func (p *parser) checkType(node Node) (typ model.ValueType) { switch n := node.(type) { case Statements: for _, s := range n { - p.expectType(s, model.ValNone, "statement list") + p.expectType(s, ValueTypeNone, "statement list") } case *AlertStmt: - p.expectType(n.Expr, model.ValVector, "alert statement") + p.expectType(n.Expr, ValueTypeVector, "alert statement") case *EvalStmt: ty := p.checkType(n.Expr) - if ty == model.ValNone { + if ty == ValueTypeNone { p.errorf("evaluation statement must have a valid expression type but got %s", documentedType(ty)) } case *RecordStmt: ty := p.checkType(n.Expr) - if ty != model.ValVector && ty != model.ValScalar { + if ty != ValueTypeVector && ty != ValueTypeScalar { p.errorf("record statement must have a valid expression of type instant vector or scalar but got %s", documentedType(ty)) } case Expressions: for _, e := range n { ty := p.checkType(e) - if ty == model.ValNone { + if ty == ValueTypeNone { p.errorf("expression must have a valid expression type but got %s", documentedType(ty)) } } @@ -1044,12 +1042,12 @@ func (p *parser) checkType(node Node) (typ model.ValueType) { if !n.Op.isAggregator() { p.errorf("aggregation operator expected in aggregation expression but got %q", n.Op) } - p.expectType(n.Expr, model.ValVector, "aggregation expression") + p.expectType(n.Expr, ValueTypeVector, "aggregation expression") if n.Op == itemTopK || n.Op == itemBottomK || n.Op == itemQuantile { - p.expectType(n.Param, model.ValScalar, "aggregation parameter") + p.expectType(n.Param, ValueTypeScalar, "aggregation parameter") } if n.Op == itemCountValues { - p.expectType(n.Param, model.ValString, "aggregation parameter") + p.expectType(n.Param, ValueTypeString, "aggregation parameter") } case *BinaryExpr: @@ -1059,11 +1057,11 @@ func (p *parser) checkType(node Node) (typ model.ValueType) { if !n.Op.isOperator() { p.errorf("binary expression does not support operator %q", n.Op) } - if (lt != model.ValScalar && lt != model.ValVector) || (rt != model.ValScalar && rt != model.ValVector) { + if (lt != ValueTypeScalar && lt != ValueTypeVector) || (rt != ValueTypeScalar && rt != ValueTypeVector) { p.errorf("binary expression must contain only scalar and instant vector types") } - if (lt != model.ValVector || rt != model.ValVector) && n.VectorMatching != nil { + if (lt != ValueTypeVector || rt != ValueTypeVector) && n.VectorMatching != nil { if len(n.VectorMatching.MatchingLabels) > 0 { p.errorf("vector matching only allowed between instant vectors") } @@ -1080,7 +1078,7 @@ func (p *parser) checkType(node Node) (typ model.ValueType) { } } - if (lt == model.ValScalar || rt == model.ValScalar) && n.Op.isSetOperator() { + if (lt == ValueTypeScalar || rt == ValueTypeScalar) && n.Op.isSetOperator() { p.errorf("set operator %q not allowed in binary scalar expression", n.Op) } @@ -1103,7 +1101,7 @@ func (p *parser) checkType(node Node) (typ model.ValueType) { if n.Op != itemADD && n.Op != itemSUB { p.errorf("only + and - operators allowed for unary expressions") } - if t := p.checkType(n.Expr); t != model.ValScalar && t != model.ValVector { + if t := p.checkType(n.Expr); t != ValueTypeScalar && t != ValueTypeVector { p.errorf("unary expression only allowed on expressions of type scalar or instant vector, got %q", documentedType(t)) } diff --git a/promql/parse_test.go b/promql/parse_test.go index 38f4c1f19..8319f5cb0 100644 --- a/promql/parse_test.go +++ b/promql/parse_test.go @@ -21,9 +21,8 @@ import ( "testing" "time" + "github.com/fabxc/tsdb/labels" "github.com/prometheus/common/model" - - "github.com/prometheus/prometheus/storage/metric" ) var testExpr = []struct { @@ -38,10 +37,10 @@ var testExpr = []struct { expected: &NumberLiteral{1}, }, { input: "+Inf", - expected: &NumberLiteral{model.SampleValue(math.Inf(1))}, + expected: &NumberLiteral{math.Inf(1)}, }, { input: "-Inf", - expected: &NumberLiteral{model.SampleValue(math.Inf(-1))}, + expected: &NumberLiteral{math.Inf(-1)}, }, { input: ".5", expected: &NumberLiteral{0.5}, @@ -143,8 +142,8 @@ var testExpr = []struct { Op: itemSUB, Expr: &VectorSelector{ Name: "some_metric", - LabelMatchers: metric.LabelMatchers{ - mustLabelMatcher(metric.Equal, model.MetricNameLabel, "some_metric"), + LabelMatchers: []*LabelMatcher{ + mustLabelMatcher(MatchEqual, string(model.MetricNameLabel), "some_metric"), }, }, }, @@ -153,8 +152,8 @@ var testExpr = []struct { Op: itemADD, Expr: &VectorSelector{ Name: "some_metric", - LabelMatchers: metric.LabelMatchers{ - mustLabelMatcher(metric.Equal, model.MetricNameLabel, "some_metric"), + LabelMatchers: []*LabelMatcher{ + mustLabelMatcher(MatchEqual, string(model.MetricNameLabel), "some_metric"), }, }, }, @@ -262,14 +261,14 @@ var testExpr = []struct { Op: itemMUL, LHS: &VectorSelector{ Name: "foo", - LabelMatchers: metric.LabelMatchers{ - mustLabelMatcher(metric.Equal, model.MetricNameLabel, "foo"), + LabelMatchers: []*LabelMatcher{ + mustLabelMatcher(MatchEqual, string(model.MetricNameLabel), "foo"), }, }, RHS: &VectorSelector{ Name: "bar", - LabelMatchers: metric.LabelMatchers{ - mustLabelMatcher(metric.Equal, model.MetricNameLabel, "bar"), + LabelMatchers: []*LabelMatcher{ + mustLabelMatcher(MatchEqual, string(model.MetricNameLabel), "bar"), }, }, VectorMatching: &VectorMatching{Card: CardOneToOne}, @@ -280,8 +279,8 @@ var testExpr = []struct { Op: itemEQL, LHS: &VectorSelector{ Name: "foo", - LabelMatchers: metric.LabelMatchers{ - mustLabelMatcher(metric.Equal, model.MetricNameLabel, "foo"), + LabelMatchers: []*LabelMatcher{ + mustLabelMatcher(MatchEqual, string(model.MetricNameLabel), "foo"), }, }, RHS: &NumberLiteral{1}, @@ -292,8 +291,8 @@ var testExpr = []struct { Op: itemEQL, LHS: &VectorSelector{ Name: "foo", - LabelMatchers: metric.LabelMatchers{ - mustLabelMatcher(metric.Equal, model.MetricNameLabel, "foo"), + LabelMatchers: []*LabelMatcher{ + mustLabelMatcher(MatchEqual, string(model.MetricNameLabel), "foo"), }, }, RHS: &NumberLiteral{1}, @@ -306,8 +305,8 @@ var testExpr = []struct { LHS: &NumberLiteral{2.5}, RHS: &VectorSelector{ Name: "bar", - LabelMatchers: metric.LabelMatchers{ - mustLabelMatcher(metric.Equal, model.MetricNameLabel, "bar"), + LabelMatchers: []*LabelMatcher{ + mustLabelMatcher(MatchEqual, string(model.MetricNameLabel), "bar"), }, }, }, @@ -317,14 +316,14 @@ var testExpr = []struct { Op: itemLAND, LHS: &VectorSelector{ Name: "foo", - LabelMatchers: metric.LabelMatchers{ - mustLabelMatcher(metric.Equal, model.MetricNameLabel, "foo"), + LabelMatchers: []*LabelMatcher{ + mustLabelMatcher(MatchEqual, string(model.MetricNameLabel), "foo"), }, }, RHS: &VectorSelector{ Name: "bar", - LabelMatchers: metric.LabelMatchers{ - mustLabelMatcher(metric.Equal, model.MetricNameLabel, "bar"), + LabelMatchers: []*LabelMatcher{ + mustLabelMatcher(MatchEqual, string(model.MetricNameLabel), "bar"), }, }, VectorMatching: &VectorMatching{Card: CardManyToMany}, @@ -335,14 +334,14 @@ var testExpr = []struct { Op: itemLOR, LHS: &VectorSelector{ Name: "foo", - LabelMatchers: metric.LabelMatchers{ - mustLabelMatcher(metric.Equal, model.MetricNameLabel, "foo"), + LabelMatchers: []*LabelMatcher{ + mustLabelMatcher(MatchEqual, string(model.MetricNameLabel), "foo"), }, }, RHS: &VectorSelector{ Name: "bar", - LabelMatchers: metric.LabelMatchers{ - mustLabelMatcher(metric.Equal, model.MetricNameLabel, "bar"), + LabelMatchers: []*LabelMatcher{ + mustLabelMatcher(MatchEqual, string(model.MetricNameLabel), "bar"), }, }, VectorMatching: &VectorMatching{Card: CardManyToMany}, @@ -353,14 +352,14 @@ var testExpr = []struct { Op: itemLUnless, LHS: &VectorSelector{ Name: "foo", - LabelMatchers: metric.LabelMatchers{ - mustLabelMatcher(metric.Equal, model.MetricNameLabel, "foo"), + LabelMatchers: []*LabelMatcher{ + mustLabelMatcher(MatchEqual, string(model.MetricNameLabel), "foo"), }, }, RHS: &VectorSelector{ Name: "bar", - LabelMatchers: metric.LabelMatchers{ - mustLabelMatcher(metric.Equal, model.MetricNameLabel, "bar"), + LabelMatchers: []*LabelMatcher{ + mustLabelMatcher(MatchEqual, string(model.MetricNameLabel), "bar"), }, }, VectorMatching: &VectorMatching{Card: CardManyToMany}, @@ -374,14 +373,14 @@ var testExpr = []struct { Op: itemADD, LHS: &VectorSelector{ Name: "foo", - LabelMatchers: metric.LabelMatchers{ - mustLabelMatcher(metric.Equal, model.MetricNameLabel, "foo"), + LabelMatchers: []*LabelMatcher{ + mustLabelMatcher(MatchEqual, string(model.MetricNameLabel), "foo"), }, }, RHS: &VectorSelector{ Name: "bar", - LabelMatchers: metric.LabelMatchers{ - mustLabelMatcher(metric.Equal, model.MetricNameLabel, "bar"), + LabelMatchers: []*LabelMatcher{ + mustLabelMatcher(MatchEqual, string(model.MetricNameLabel), "bar"), }, }, VectorMatching: &VectorMatching{Card: CardOneToOne}, @@ -390,14 +389,14 @@ var testExpr = []struct { Op: itemLAND, LHS: &VectorSelector{ Name: "bla", - LabelMatchers: metric.LabelMatchers{ - mustLabelMatcher(metric.Equal, model.MetricNameLabel, "bla"), + LabelMatchers: []*LabelMatcher{ + mustLabelMatcher(MatchEqual, string(model.MetricNameLabel), "bla"), }, }, RHS: &VectorSelector{ Name: "blub", - LabelMatchers: metric.LabelMatchers{ - mustLabelMatcher(metric.Equal, model.MetricNameLabel, "blub"), + LabelMatchers: []*LabelMatcher{ + mustLabelMatcher(MatchEqual, string(model.MetricNameLabel), "blub"), }, }, VectorMatching: &VectorMatching{Card: CardManyToMany}, @@ -415,30 +414,30 @@ var testExpr = []struct { Op: itemLAND, LHS: &VectorSelector{ Name: "foo", - LabelMatchers: metric.LabelMatchers{ - mustLabelMatcher(metric.Equal, model.MetricNameLabel, "foo"), + LabelMatchers: []*LabelMatcher{ + mustLabelMatcher(MatchEqual, string(model.MetricNameLabel), "foo"), }, }, RHS: &VectorSelector{ Name: "bar", - LabelMatchers: metric.LabelMatchers{ - mustLabelMatcher(metric.Equal, model.MetricNameLabel, "bar"), + LabelMatchers: []*LabelMatcher{ + mustLabelMatcher(MatchEqual, string(model.MetricNameLabel), "bar"), }, }, VectorMatching: &VectorMatching{Card: CardManyToMany}, }, RHS: &VectorSelector{ Name: "baz", - LabelMatchers: metric.LabelMatchers{ - mustLabelMatcher(metric.Equal, model.MetricNameLabel, "baz"), + LabelMatchers: []*LabelMatcher{ + mustLabelMatcher(MatchEqual, string(model.MetricNameLabel), "baz"), }, }, VectorMatching: &VectorMatching{Card: CardManyToMany}, }, RHS: &VectorSelector{ Name: "qux", - LabelMatchers: metric.LabelMatchers{ - mustLabelMatcher(metric.Equal, model.MetricNameLabel, "qux"), + LabelMatchers: []*LabelMatcher{ + mustLabelMatcher(MatchEqual, string(model.MetricNameLabel), "qux"), }, }, VectorMatching: &VectorMatching{Card: CardManyToMany}, @@ -450,34 +449,34 @@ var testExpr = []struct { Op: itemADD, LHS: &VectorSelector{ Name: "bar", - LabelMatchers: metric.LabelMatchers{ - mustLabelMatcher(metric.Equal, model.MetricNameLabel, "bar"), + LabelMatchers: []*LabelMatcher{ + mustLabelMatcher(MatchEqual, string(model.MetricNameLabel), "bar"), }, }, RHS: &BinaryExpr{ Op: itemDIV, LHS: &VectorSelector{ Name: "bla", - LabelMatchers: metric.LabelMatchers{ - mustLabelMatcher(metric.Equal, model.MetricNameLabel, "bla"), + LabelMatchers: []*LabelMatcher{ + mustLabelMatcher(MatchEqual, string(model.MetricNameLabel), "bla"), }, }, RHS: &VectorSelector{ Name: "blub", - LabelMatchers: metric.LabelMatchers{ - mustLabelMatcher(metric.Equal, model.MetricNameLabel, "blub"), + LabelMatchers: []*LabelMatcher{ + mustLabelMatcher(MatchEqual, string(model.MetricNameLabel), "blub"), }, }, VectorMatching: &VectorMatching{ Card: CardOneToMany, - MatchingLabels: model.LabelNames{"baz", "buz"}, + MatchingLabels: []string{"baz", "buz"}, On: true, - Include: model.LabelNames{"test"}, + Include: []string{"test"}, }, }, VectorMatching: &VectorMatching{ Card: CardOneToOne, - MatchingLabels: model.LabelNames{"foo"}, + MatchingLabels: []string{"foo"}, On: true, }, }, @@ -487,19 +486,19 @@ var testExpr = []struct { Op: itemMUL, LHS: &VectorSelector{ Name: "foo", - LabelMatchers: metric.LabelMatchers{ - mustLabelMatcher(metric.Equal, model.MetricNameLabel, "foo"), + LabelMatchers: []*LabelMatcher{ + mustLabelMatcher(MatchEqual, string(model.MetricNameLabel), "foo"), }, }, RHS: &VectorSelector{ Name: "bar", - LabelMatchers: metric.LabelMatchers{ - mustLabelMatcher(metric.Equal, model.MetricNameLabel, "bar"), + LabelMatchers: []*LabelMatcher{ + mustLabelMatcher(MatchEqual, string(model.MetricNameLabel), "bar"), }, }, VectorMatching: &VectorMatching{ Card: CardOneToOne, - MatchingLabels: model.LabelNames{"test", "blub"}, + MatchingLabels: []string{"test", "blub"}, On: true, }, }, @@ -509,19 +508,19 @@ var testExpr = []struct { Op: itemMUL, LHS: &VectorSelector{ Name: "foo", - LabelMatchers: metric.LabelMatchers{ - mustLabelMatcher(metric.Equal, model.MetricNameLabel, "foo"), + LabelMatchers: []*LabelMatcher{ + mustLabelMatcher(MatchEqual, string(model.MetricNameLabel), "foo"), }, }, RHS: &VectorSelector{ Name: "bar", - LabelMatchers: metric.LabelMatchers{ - mustLabelMatcher(metric.Equal, model.MetricNameLabel, "bar"), + LabelMatchers: []*LabelMatcher{ + mustLabelMatcher(MatchEqual, string(model.MetricNameLabel), "bar"), }, }, VectorMatching: &VectorMatching{ Card: CardManyToOne, - MatchingLabels: model.LabelNames{"test", "blub"}, + MatchingLabels: []string{"test", "blub"}, On: true, }, }, @@ -531,19 +530,19 @@ var testExpr = []struct { Op: itemLAND, LHS: &VectorSelector{ Name: "foo", - LabelMatchers: metric.LabelMatchers{ - mustLabelMatcher(metric.Equal, model.MetricNameLabel, "foo"), + LabelMatchers: []*LabelMatcher{ + mustLabelMatcher(MatchEqual, string(model.MetricNameLabel), "foo"), }, }, RHS: &VectorSelector{ Name: "bar", - LabelMatchers: metric.LabelMatchers{ - mustLabelMatcher(metric.Equal, model.MetricNameLabel, "bar"), + LabelMatchers: []*LabelMatcher{ + mustLabelMatcher(MatchEqual, string(model.MetricNameLabel), "bar"), }, }, VectorMatching: &VectorMatching{ Card: CardManyToMany, - MatchingLabels: model.LabelNames{"test", "blub"}, + MatchingLabels: []string{"test", "blub"}, On: true, }, }, @@ -553,19 +552,19 @@ var testExpr = []struct { Op: itemLAND, LHS: &VectorSelector{ Name: "foo", - LabelMatchers: metric.LabelMatchers{ - mustLabelMatcher(metric.Equal, model.MetricNameLabel, "foo"), + LabelMatchers: []*LabelMatcher{ + mustLabelMatcher(MatchEqual, string(model.MetricNameLabel), "foo"), }, }, RHS: &VectorSelector{ Name: "bar", - LabelMatchers: metric.LabelMatchers{ - mustLabelMatcher(metric.Equal, model.MetricNameLabel, "bar"), + LabelMatchers: []*LabelMatcher{ + mustLabelMatcher(MatchEqual, string(model.MetricNameLabel), "bar"), }, }, VectorMatching: &VectorMatching{ Card: CardManyToMany, - MatchingLabels: model.LabelNames{}, + MatchingLabels: []string{}, On: true, }, }, @@ -575,19 +574,19 @@ var testExpr = []struct { Op: itemLAND, LHS: &VectorSelector{ Name: "foo", - LabelMatchers: metric.LabelMatchers{ - mustLabelMatcher(metric.Equal, model.MetricNameLabel, "foo"), + LabelMatchers: []*LabelMatcher{ + mustLabelMatcher(MatchEqual, string(model.MetricNameLabel), "foo"), }, }, RHS: &VectorSelector{ Name: "bar", - LabelMatchers: metric.LabelMatchers{ - mustLabelMatcher(metric.Equal, model.MetricNameLabel, "bar"), + LabelMatchers: []*LabelMatcher{ + mustLabelMatcher(MatchEqual, string(model.MetricNameLabel), "bar"), }, }, VectorMatching: &VectorMatching{ Card: CardManyToMany, - MatchingLabels: model.LabelNames{"test", "blub"}, + MatchingLabels: []string{"test", "blub"}, }, }, }, { @@ -596,19 +595,19 @@ var testExpr = []struct { Op: itemLAND, LHS: &VectorSelector{ Name: "foo", - LabelMatchers: metric.LabelMatchers{ - mustLabelMatcher(metric.Equal, model.MetricNameLabel, "foo"), + LabelMatchers: []*LabelMatcher{ + mustLabelMatcher(MatchEqual, string(model.MetricNameLabel), "foo"), }, }, RHS: &VectorSelector{ Name: "bar", - LabelMatchers: metric.LabelMatchers{ - mustLabelMatcher(metric.Equal, model.MetricNameLabel, "bar"), + LabelMatchers: []*LabelMatcher{ + mustLabelMatcher(MatchEqual, string(model.MetricNameLabel), "bar"), }, }, VectorMatching: &VectorMatching{ Card: CardManyToMany, - MatchingLabels: model.LabelNames{}, + MatchingLabels: []string{}, }, }, }, { @@ -617,19 +616,19 @@ var testExpr = []struct { Op: itemLUnless, LHS: &VectorSelector{ Name: "foo", - LabelMatchers: metric.LabelMatchers{ - mustLabelMatcher(metric.Equal, model.MetricNameLabel, "foo"), + LabelMatchers: []*LabelMatcher{ + mustLabelMatcher(MatchEqual, string(model.MetricNameLabel), "foo"), }, }, RHS: &VectorSelector{ Name: "baz", - LabelMatchers: metric.LabelMatchers{ - mustLabelMatcher(metric.Equal, model.MetricNameLabel, "baz"), + LabelMatchers: []*LabelMatcher{ + mustLabelMatcher(MatchEqual, string(model.MetricNameLabel), "baz"), }, }, VectorMatching: &VectorMatching{ Card: CardManyToMany, - MatchingLabels: model.LabelNames{"bar"}, + MatchingLabels: []string{"bar"}, On: true, }, }, @@ -639,21 +638,21 @@ var testExpr = []struct { Op: itemDIV, LHS: &VectorSelector{ Name: "foo", - LabelMatchers: metric.LabelMatchers{ - mustLabelMatcher(metric.Equal, model.MetricNameLabel, "foo"), + LabelMatchers: []*LabelMatcher{ + mustLabelMatcher(MatchEqual, string(model.MetricNameLabel), "foo"), }, }, RHS: &VectorSelector{ Name: "bar", - LabelMatchers: metric.LabelMatchers{ - mustLabelMatcher(metric.Equal, model.MetricNameLabel, "bar"), + LabelMatchers: []*LabelMatcher{ + mustLabelMatcher(MatchEqual, string(model.MetricNameLabel), "bar"), }, }, VectorMatching: &VectorMatching{ Card: CardManyToOne, - MatchingLabels: model.LabelNames{"test", "blub"}, + MatchingLabels: []string{"test", "blub"}, On: true, - Include: model.LabelNames{"bar"}, + Include: []string{"bar"}, }, }, }, { @@ -662,20 +661,20 @@ var testExpr = []struct { Op: itemDIV, LHS: &VectorSelector{ Name: "foo", - LabelMatchers: metric.LabelMatchers{ - mustLabelMatcher(metric.Equal, model.MetricNameLabel, "foo"), + LabelMatchers: []*LabelMatcher{ + mustLabelMatcher(MatchEqual, string(model.MetricNameLabel), "foo"), }, }, RHS: &VectorSelector{ Name: "bar", - LabelMatchers: metric.LabelMatchers{ - mustLabelMatcher(metric.Equal, model.MetricNameLabel, "bar"), + LabelMatchers: []*LabelMatcher{ + mustLabelMatcher(MatchEqual, string(model.MetricNameLabel), "bar"), }, }, VectorMatching: &VectorMatching{ Card: CardManyToOne, - MatchingLabels: model.LabelNames{"test", "blub"}, - Include: model.LabelNames{"blub"}, + MatchingLabels: []string{"test", "blub"}, + Include: []string{"blub"}, }, }, }, { @@ -684,20 +683,20 @@ var testExpr = []struct { Op: itemDIV, LHS: &VectorSelector{ Name: "foo", - LabelMatchers: metric.LabelMatchers{ - mustLabelMatcher(metric.Equal, model.MetricNameLabel, "foo"), + LabelMatchers: []*LabelMatcher{ + mustLabelMatcher(MatchEqual, string(model.MetricNameLabel), "foo"), }, }, RHS: &VectorSelector{ Name: "bar", - LabelMatchers: metric.LabelMatchers{ - mustLabelMatcher(metric.Equal, model.MetricNameLabel, "bar"), + LabelMatchers: []*LabelMatcher{ + mustLabelMatcher(MatchEqual, string(model.MetricNameLabel), "bar"), }, }, VectorMatching: &VectorMatching{ Card: CardManyToOne, - MatchingLabels: model.LabelNames{"test", "blub"}, - Include: model.LabelNames{"bar"}, + MatchingLabels: []string{"test", "blub"}, + Include: []string{"bar"}, }, }, }, { @@ -706,20 +705,20 @@ var testExpr = []struct { Op: itemSUB, LHS: &VectorSelector{ Name: "foo", - LabelMatchers: metric.LabelMatchers{ - mustLabelMatcher(metric.Equal, model.MetricNameLabel, "foo"), + LabelMatchers: []*LabelMatcher{ + mustLabelMatcher(MatchEqual, string(model.MetricNameLabel), "foo"), }, }, RHS: &VectorSelector{ Name: "bar", - LabelMatchers: metric.LabelMatchers{ - mustLabelMatcher(metric.Equal, model.MetricNameLabel, "bar"), + LabelMatchers: []*LabelMatcher{ + mustLabelMatcher(MatchEqual, string(model.MetricNameLabel), "bar"), }, }, VectorMatching: &VectorMatching{ Card: CardOneToMany, - MatchingLabels: model.LabelNames{"test", "blub"}, - Include: model.LabelNames{"bar", "foo"}, + MatchingLabels: []string{"test", "blub"}, + Include: []string{"bar", "foo"}, On: true, }, }, @@ -729,20 +728,20 @@ var testExpr = []struct { Op: itemSUB, LHS: &VectorSelector{ Name: "foo", - LabelMatchers: metric.LabelMatchers{ - mustLabelMatcher(metric.Equal, model.MetricNameLabel, "foo"), + LabelMatchers: []*LabelMatcher{ + mustLabelMatcher(MatchEqual, string(model.MetricNameLabel), "foo"), }, }, RHS: &VectorSelector{ Name: "bar", - LabelMatchers: metric.LabelMatchers{ - mustLabelMatcher(metric.Equal, model.MetricNameLabel, "bar"), + LabelMatchers: []*LabelMatcher{ + mustLabelMatcher(MatchEqual, string(model.MetricNameLabel), "bar"), }, }, VectorMatching: &VectorMatching{ Card: CardOneToMany, - MatchingLabels: model.LabelNames{"test", "blub"}, - Include: model.LabelNames{"bar", "foo"}, + MatchingLabels: []string{"test", "blub"}, + Include: []string{"bar", "foo"}, }, }, }, { @@ -824,8 +823,8 @@ var testExpr = []struct { expected: &VectorSelector{ Name: "foo", Offset: 0, - LabelMatchers: metric.LabelMatchers{ - mustLabelMatcher(metric.Equal, model.MetricNameLabel, "foo"), + LabelMatchers: []*LabelMatcher{ + mustLabelMatcher(MatchEqual, string(model.MetricNameLabel), "foo"), }, }, }, { @@ -833,8 +832,8 @@ var testExpr = []struct { expected: &VectorSelector{ Name: "foo", Offset: 5 * time.Minute, - LabelMatchers: metric.LabelMatchers{ - mustLabelMatcher(metric.Equal, model.MetricNameLabel, "foo"), + LabelMatchers: []*LabelMatcher{ + mustLabelMatcher(MatchEqual, string(model.MetricNameLabel), "foo"), }, }, }, { @@ -842,9 +841,9 @@ var testExpr = []struct { expected: &VectorSelector{ Name: "foo:bar", Offset: 0, - LabelMatchers: metric.LabelMatchers{ - mustLabelMatcher(metric.Equal, "a", "bc"), - mustLabelMatcher(metric.Equal, model.MetricNameLabel, "foo:bar"), + LabelMatchers: []*LabelMatcher{ + mustLabelMatcher(MatchEqual, "a", "bc"), + mustLabelMatcher(MatchEqual, string(model.MetricNameLabel), "foo:bar"), }, }, }, { @@ -852,9 +851,9 @@ var testExpr = []struct { expected: &VectorSelector{ Name: "foo", Offset: 0, - LabelMatchers: metric.LabelMatchers{ - mustLabelMatcher(metric.Equal, "NaN", "bc"), - mustLabelMatcher(metric.Equal, model.MetricNameLabel, "foo"), + LabelMatchers: []*LabelMatcher{ + mustLabelMatcher(MatchEqual, "NaN", "bc"), + mustLabelMatcher(MatchEqual, string(model.MetricNameLabel), "foo"), }, }, }, { @@ -862,12 +861,12 @@ var testExpr = []struct { expected: &VectorSelector{ Name: "foo", Offset: 0, - LabelMatchers: metric.LabelMatchers{ - mustLabelMatcher(metric.Equal, "a", "b"), - mustLabelMatcher(metric.NotEqual, "foo", "bar"), - mustLabelMatcher(metric.RegexMatch, "test", "test"), - mustLabelMatcher(metric.RegexNoMatch, "bar", "baz"), - mustLabelMatcher(metric.Equal, model.MetricNameLabel, "foo"), + LabelMatchers: []*LabelMatcher{ + mustLabelMatcher(MatchEqual, "a", "b"), + mustLabelMatcher(MatchNotEqual, "foo", "bar"), + mustLabelMatcher(MatchRegexp, "test", "test"), + mustLabelMatcher(MatchNotRegexp, "bar", "baz"), + mustLabelMatcher(MatchEqual, string(model.MetricNameLabel), "foo"), }, }, }, { @@ -948,8 +947,8 @@ var testExpr = []struct { Name: "test", Offset: 0, Range: 5 * time.Second, - LabelMatchers: metric.LabelMatchers{ - mustLabelMatcher(metric.Equal, model.MetricNameLabel, "test"), + LabelMatchers: []*LabelMatcher{ + mustLabelMatcher(MatchEqual, string(model.MetricNameLabel), "test"), }, }, }, { @@ -958,8 +957,8 @@ var testExpr = []struct { Name: "test", Offset: 0, Range: 5 * time.Minute, - LabelMatchers: metric.LabelMatchers{ - mustLabelMatcher(metric.Equal, model.MetricNameLabel, "test"), + LabelMatchers: []*LabelMatcher{ + mustLabelMatcher(MatchEqual, string(model.MetricNameLabel), "test"), }, }, }, { @@ -968,8 +967,8 @@ var testExpr = []struct { Name: "test", Offset: 5 * time.Minute, Range: 5 * time.Hour, - LabelMatchers: metric.LabelMatchers{ - mustLabelMatcher(metric.Equal, model.MetricNameLabel, "test"), + LabelMatchers: []*LabelMatcher{ + mustLabelMatcher(MatchEqual, string(model.MetricNameLabel), "test"), }, }, }, { @@ -978,8 +977,8 @@ var testExpr = []struct { Name: "test", Offset: 10 * time.Second, Range: 5 * 24 * time.Hour, - LabelMatchers: metric.LabelMatchers{ - mustLabelMatcher(metric.Equal, model.MetricNameLabel, "test"), + LabelMatchers: []*LabelMatcher{ + mustLabelMatcher(MatchEqual, string(model.MetricNameLabel), "test"), }, }, }, { @@ -988,8 +987,8 @@ var testExpr = []struct { Name: "test", Offset: 14 * 24 * time.Hour, Range: 5 * 7 * 24 * time.Hour, - LabelMatchers: metric.LabelMatchers{ - mustLabelMatcher(metric.Equal, model.MetricNameLabel, "test"), + LabelMatchers: []*LabelMatcher{ + mustLabelMatcher(MatchEqual, string(model.MetricNameLabel), "test"), }, }, }, { @@ -998,9 +997,9 @@ var testExpr = []struct { Name: "test", Offset: 3 * 24 * time.Hour, Range: 5 * 365 * 24 * time.Hour, - LabelMatchers: metric.LabelMatchers{ - mustLabelMatcher(metric.Equal, "a", "b"), - mustLabelMatcher(metric.Equal, model.MetricNameLabel, "test"), + LabelMatchers: []*LabelMatcher{ + mustLabelMatcher(MatchEqual, "a", "b"), + mustLabelMatcher(MatchEqual, string(model.MetricNameLabel), "test"), }, }, }, { @@ -1058,11 +1057,11 @@ var testExpr = []struct { Op: itemSum, Expr: &VectorSelector{ Name: "some_metric", - LabelMatchers: metric.LabelMatchers{ - mustLabelMatcher(metric.Equal, model.MetricNameLabel, "some_metric"), + LabelMatchers: []*LabelMatcher{ + mustLabelMatcher(MatchEqual, string(model.MetricNameLabel), "some_metric"), }, }, - Grouping: model.LabelNames{"foo"}, + Grouping: []string{"foo"}, }, }, { input: "sum by (foo) keep_common (some_metric)", @@ -1071,11 +1070,11 @@ var testExpr = []struct { KeepCommonLabels: true, Expr: &VectorSelector{ Name: "some_metric", - LabelMatchers: metric.LabelMatchers{ - mustLabelMatcher(metric.Equal, model.MetricNameLabel, "some_metric"), + LabelMatchers: []*LabelMatcher{ + mustLabelMatcher(MatchEqual, string(model.MetricNameLabel), "some_metric"), }, }, - Grouping: model.LabelNames{"foo"}, + Grouping: []string{"foo"}, }, }, { input: "sum (some_metric) by (foo,bar) keep_common", @@ -1084,11 +1083,11 @@ var testExpr = []struct { KeepCommonLabels: true, Expr: &VectorSelector{ Name: "some_metric", - LabelMatchers: metric.LabelMatchers{ - mustLabelMatcher(metric.Equal, model.MetricNameLabel, "some_metric"), + LabelMatchers: []*LabelMatcher{ + mustLabelMatcher(MatchEqual, string(model.MetricNameLabel), "some_metric"), }, }, - Grouping: model.LabelNames{"foo", "bar"}, + Grouping: []string{"foo", "bar"}, }, }, { input: "avg by (foo)(some_metric)", @@ -1096,11 +1095,11 @@ var testExpr = []struct { Op: itemAvg, Expr: &VectorSelector{ Name: "some_metric", - LabelMatchers: metric.LabelMatchers{ - mustLabelMatcher(metric.Equal, model.MetricNameLabel, "some_metric"), + LabelMatchers: []*LabelMatcher{ + mustLabelMatcher(MatchEqual, string(model.MetricNameLabel), "some_metric"), }, }, - Grouping: model.LabelNames{"foo"}, + Grouping: []string{"foo"}, }, }, { input: "COUNT by (foo) keep_common (some_metric)", @@ -1108,11 +1107,11 @@ var testExpr = []struct { Op: itemCount, Expr: &VectorSelector{ Name: "some_metric", - LabelMatchers: metric.LabelMatchers{ - mustLabelMatcher(metric.Equal, model.MetricNameLabel, "some_metric"), + LabelMatchers: []*LabelMatcher{ + mustLabelMatcher(MatchEqual, string(model.MetricNameLabel), "some_metric"), }, }, - Grouping: model.LabelNames{"foo"}, + Grouping: []string{"foo"}, KeepCommonLabels: true, }, }, { @@ -1121,11 +1120,11 @@ var testExpr = []struct { Op: itemMin, Expr: &VectorSelector{ Name: "some_metric", - LabelMatchers: metric.LabelMatchers{ - mustLabelMatcher(metric.Equal, model.MetricNameLabel, "some_metric"), + LabelMatchers: []*LabelMatcher{ + mustLabelMatcher(MatchEqual, string(model.MetricNameLabel), "some_metric"), }, }, - Grouping: model.LabelNames{"foo"}, + Grouping: []string{"foo"}, KeepCommonLabels: true, }, }, { @@ -1134,11 +1133,11 @@ var testExpr = []struct { Op: itemMax, Expr: &VectorSelector{ Name: "some_metric", - LabelMatchers: metric.LabelMatchers{ - mustLabelMatcher(metric.Equal, model.MetricNameLabel, "some_metric"), + LabelMatchers: []*LabelMatcher{ + mustLabelMatcher(MatchEqual, string(model.MetricNameLabel), "some_metric"), }, }, - Grouping: model.LabelNames{"foo"}, + Grouping: []string{"foo"}, }, }, { input: "sum without (foo) (some_metric)", @@ -1147,11 +1146,11 @@ var testExpr = []struct { Without: true, Expr: &VectorSelector{ Name: "some_metric", - LabelMatchers: metric.LabelMatchers{ - mustLabelMatcher(metric.Equal, model.MetricNameLabel, "some_metric"), + LabelMatchers: []*LabelMatcher{ + mustLabelMatcher(MatchEqual, string(model.MetricNameLabel), "some_metric"), }, }, - Grouping: model.LabelNames{"foo"}, + Grouping: []string{"foo"}, }, }, { input: "sum (some_metric) without (foo)", @@ -1160,11 +1159,11 @@ var testExpr = []struct { Without: true, Expr: &VectorSelector{ Name: "some_metric", - LabelMatchers: metric.LabelMatchers{ - mustLabelMatcher(metric.Equal, model.MetricNameLabel, "some_metric"), + LabelMatchers: []*LabelMatcher{ + mustLabelMatcher(MatchEqual, string(model.MetricNameLabel), "some_metric"), }, }, - Grouping: model.LabelNames{"foo"}, + Grouping: []string{"foo"}, }, }, { input: "stddev(some_metric)", @@ -1172,8 +1171,8 @@ var testExpr = []struct { Op: itemStddev, Expr: &VectorSelector{ Name: "some_metric", - LabelMatchers: metric.LabelMatchers{ - mustLabelMatcher(metric.Equal, model.MetricNameLabel, "some_metric"), + LabelMatchers: []*LabelMatcher{ + mustLabelMatcher(MatchEqual, string(model.MetricNameLabel), "some_metric"), }, }, }, @@ -1183,11 +1182,11 @@ var testExpr = []struct { Op: itemStdvar, Expr: &VectorSelector{ Name: "some_metric", - LabelMatchers: metric.LabelMatchers{ - mustLabelMatcher(metric.Equal, model.MetricNameLabel, "some_metric"), + LabelMatchers: []*LabelMatcher{ + mustLabelMatcher(MatchEqual, string(model.MetricNameLabel), "some_metric"), }, }, - Grouping: model.LabelNames{"foo"}, + Grouping: []string{"foo"}, }, }, { input: "sum by ()(some_metric)", @@ -1195,11 +1194,11 @@ var testExpr = []struct { Op: itemSum, Expr: &VectorSelector{ Name: "some_metric", - LabelMatchers: metric.LabelMatchers{ - mustLabelMatcher(metric.Equal, model.MetricNameLabel, "some_metric"), + LabelMatchers: []*LabelMatcher{ + mustLabelMatcher(MatchEqual, string(model.MetricNameLabel), "some_metric"), }, }, - Grouping: model.LabelNames{}, + Grouping: []string{}, }, }, { input: "topk(5, some_metric)", @@ -1207,8 +1206,8 @@ var testExpr = []struct { Op: itemTopK, Expr: &VectorSelector{ Name: "some_metric", - LabelMatchers: metric.LabelMatchers{ - mustLabelMatcher(metric.Equal, model.MetricNameLabel, "some_metric"), + LabelMatchers: []*LabelMatcher{ + mustLabelMatcher(MatchEqual, string(model.MetricNameLabel), "some_metric"), }, }, Param: &NumberLiteral{5}, @@ -1219,8 +1218,8 @@ var testExpr = []struct { Op: itemCountValues, Expr: &VectorSelector{ Name: "some_metric", - LabelMatchers: metric.LabelMatchers{ - mustLabelMatcher(metric.Equal, model.MetricNameLabel, "some_metric"), + LabelMatchers: []*LabelMatcher{ + mustLabelMatcher(MatchEqual, string(model.MetricNameLabel), "some_metric"), }, }, Param: &StringLiteral{"value"}, @@ -1233,11 +1232,11 @@ var testExpr = []struct { Without: true, Expr: &VectorSelector{ Name: "some_metric", - LabelMatchers: metric.LabelMatchers{ - mustLabelMatcher(metric.Equal, model.MetricNameLabel, "some_metric"), + LabelMatchers: []*LabelMatcher{ + mustLabelMatcher(MatchEqual, string(model.MetricNameLabel), "some_metric"), }, }, - Grouping: model.LabelNames{"and", "by", "avg", "count", "alert", "annotations"}, + Grouping: []string{"and", "by", "avg", "count", "alert", "annotations"}, }, }, { input: "sum without(==)(some_metric)", @@ -1305,9 +1304,9 @@ var testExpr = []struct { Args: Expressions{ &VectorSelector{ Name: "some_metric", - LabelMatchers: metric.LabelMatchers{ - mustLabelMatcher(metric.NotEqual, "foo", "bar"), - mustLabelMatcher(metric.Equal, model.MetricNameLabel, "some_metric"), + LabelMatchers: []*LabelMatcher{ + mustLabelMatcher(MatchNotEqual, "foo", "bar"), + mustLabelMatcher(MatchEqual, string(model.MetricNameLabel), "some_metric"), }, }, }, @@ -1319,8 +1318,8 @@ var testExpr = []struct { Args: Expressions{ &MatrixSelector{ Name: "some_metric", - LabelMatchers: metric.LabelMatchers{ - mustLabelMatcher(metric.Equal, model.MetricNameLabel, "some_metric"), + LabelMatchers: []*LabelMatcher{ + mustLabelMatcher(MatchEqual, string(model.MetricNameLabel), "some_metric"), }, Range: 5 * time.Minute, }, @@ -1333,8 +1332,8 @@ var testExpr = []struct { Args: Expressions{ &VectorSelector{ Name: "some_metric", - LabelMatchers: metric.LabelMatchers{ - mustLabelMatcher(metric.Equal, model.MetricNameLabel, "some_metric"), + LabelMatchers: []*LabelMatcher{ + mustLabelMatcher(MatchEqual, string(model.MetricNameLabel), "some_metric"), }, }, }, @@ -1346,8 +1345,8 @@ var testExpr = []struct { Args: Expressions{ &VectorSelector{ Name: "some_metric", - LabelMatchers: metric.LabelMatchers{ - mustLabelMatcher(metric.Equal, model.MetricNameLabel, "some_metric"), + LabelMatchers: []*LabelMatcher{ + mustLabelMatcher(MatchEqual, string(model.MetricNameLabel), "some_metric"), }, }, &NumberLiteral{5}, @@ -1548,14 +1547,14 @@ var testStatement = []struct { Name: "dc:http_request:rate5m", Expr: &AggregateExpr{ Op: itemSum, - Grouping: model.LabelNames{"dc"}, + Grouping: []string{"dc"}, Expr: &Call{ Func: mustGetFunction("rate"), Args: Expressions{ &MatrixSelector{ Name: "http_request_count", - LabelMatchers: metric.LabelMatchers{ - mustLabelMatcher(metric.Equal, model.MetricNameLabel, "http_request_count"), + LabelMatchers: []*LabelMatcher{ + mustLabelMatcher(MatchEqual, string(model.MetricNameLabel), "http_request_count"), }, Range: 5 * time.Minute, }, @@ -1570,26 +1569,26 @@ var testStatement = []struct { Op: itemLSS, LHS: &VectorSelector{ Name: "dc:http_request:rate5m", - LabelMatchers: metric.LabelMatchers{ - mustLabelMatcher(metric.Equal, model.MetricNameLabel, "dc:http_request:rate5m"), + LabelMatchers: []*LabelMatcher{ + mustLabelMatcher(MatchEqual, string(model.MetricNameLabel), "dc:http_request:rate5m"), }, }, RHS: &NumberLiteral{10000}, }}, - Labels: model.LabelSet{"service": "testservice"}, + Labels: labels.FromStrings("service", "testservice"), Duration: 5 * time.Minute, - Annotations: model.LabelSet{ - "summary": "Global request rate low", - "description": "The global request rate is low", - }, + Annotations: labels.FromStrings( + "summary", "Global request rate low", + "description", "The global request rate is low", + ), }, &RecordStmt{ Name: "foo", Expr: &VectorSelector{ Name: "bar", - LabelMatchers: metric.LabelMatchers{ - mustLabelMatcher(metric.Equal, "label1", "value1"), - mustLabelMatcher(metric.Equal, model.MetricNameLabel, "bar"), + LabelMatchers: []*LabelMatcher{ + mustLabelMatcher(MatchEqual, "label1", "value1"), + mustLabelMatcher(MatchEqual, string(model.MetricNameLabel), "bar"), }, }, Labels: nil, @@ -1600,18 +1599,18 @@ var testStatement = []struct { Op: itemGTR, LHS: &VectorSelector{ Name: "foo", - LabelMatchers: metric.LabelMatchers{ - mustLabelMatcher(metric.Equal, model.MetricNameLabel, "foo"), + LabelMatchers: []*LabelMatcher{ + mustLabelMatcher(MatchEqual, string(model.MetricNameLabel), "foo"), }, }, RHS: &NumberLiteral{10}, }, - Labels: model.LabelSet{}, - Annotations: model.LabelSet{ - "summary": "Baz", - "description": "BazAlert", - "runbook": "http://my.url", - }, + Labels: labels.Labels{}, + Annotations: labels.FromStrings( + "summary", "Baz", + "description", "BazAlert", + "runbook", "http://my.url", + ), }, }, }, { @@ -1621,13 +1620,13 @@ var testStatement = []struct { Name: "foo", Expr: &VectorSelector{ Name: "bar", - LabelMatchers: metric.LabelMatchers{ - mustLabelMatcher(metric.Equal, "a", "b"), - mustLabelMatcher(metric.RegexMatch, "x", "y"), - mustLabelMatcher(metric.Equal, model.MetricNameLabel, "bar"), + LabelMatchers: []*LabelMatcher{ + mustLabelMatcher(MatchEqual, "a", "b"), + mustLabelMatcher(MatchRegexp, "x", "y"), + mustLabelMatcher(MatchEqual, string(model.MetricNameLabel), "bar"), }, }, - Labels: model.LabelSet{"x": "", "a": "z"}, + Labels: labels.FromStrings("x", "", "a", "z"), }, }, }, { @@ -1645,17 +1644,17 @@ var testStatement = []struct { Op: itemGTR, LHS: &VectorSelector{ Name: "some_metric", - LabelMatchers: metric.LabelMatchers{ - mustLabelMatcher(metric.Equal, model.MetricNameLabel, "some_metric"), + LabelMatchers: []*LabelMatcher{ + mustLabelMatcher(MatchEqual, string(model.MetricNameLabel), "some_metric"), }, }, RHS: &NumberLiteral{1}, }, - Labels: model.LabelSet{}, - Annotations: model.LabelSet{ - "summary": "Global request rate low", - "description": "The global request rate is low", - }, + Labels: labels.Labels{}, + Annotations: labels.FromStrings( + "summary", "Global request rate low", + "description", "The global request rate is low", + ), }, }, }, { @@ -1765,8 +1764,8 @@ func TestParseStatements(t *testing.T) { } } -func mustLabelMatcher(mt metric.MatchType, name model.LabelName, val model.LabelValue) *metric.LabelMatcher { - m, err := metric.NewLabelMatcher(mt, name, val) +func mustLabelMatcher(mt MatchType, name, val string) *LabelMatcher { + m, err := NewLabelMatcher(mt, name, val) if err != nil { panic(err) } @@ -1851,7 +1850,7 @@ func newSeq(vals ...float64) (res []sequenceValue) { if v == none { res = append(res, sequenceValue{omitted: true}) } else { - res = append(res, sequenceValue{value: model.SampleValue(v)}) + res = append(res, sequenceValue{value: v}) } } return res diff --git a/promql/printer.go b/promql/printer.go index 40ca02e65..56fab7a9c 100644 --- a/promql/printer.go +++ b/promql/printer.go @@ -20,8 +20,6 @@ import ( "time" "github.com/prometheus/common/model" - - "github.com/prometheus/prometheus/storage/metric" ) // Tree returns a string of the tree structure of the given node. @@ -218,7 +216,7 @@ func (node *VectorSelector) String() string { labelStrings := make([]string, 0, len(node.LabelMatchers)-1) for _, matcher := range node.LabelMatchers { // Only include the __name__ label if its no equality matching. - if matcher.Name == model.MetricNameLabel && matcher.Type == metric.Equal { + if matcher.Name == MetricNameLabel && matcher.Type == MatchEqual { continue } labelStrings = append(labelStrings, matcher.String()) diff --git a/promql/printer_test.go b/promql/printer_test.go index 2419a2b39..1f64e2aee 100644 --- a/promql/printer_test.go +++ b/promql/printer_test.go @@ -17,8 +17,7 @@ import ( "testing" "time" - "github.com/prometheus/common/model" - "github.com/prometheus/prometheus/storage/metric" + "github.com/fabxc/tsdb/labels" ) func TestStatementString(t *testing.T) { @@ -28,17 +27,15 @@ func TestStatementString(t *testing.T) { Op: itemGTR, LHS: &VectorSelector{ Name: "foo", - LabelMatchers: metric.LabelMatchers{ - {Type: metric.Equal, Name: model.MetricNameLabel, Value: "bar"}, + LabelMatchers: []*LabelMatcher{ + {Type: MatchEqual, Name: MetricNameLabel, Value: "bar"}, }, }, RHS: &NumberLiteral{10}, }, - Duration: 5 * time.Minute, - Labels: model.LabelSet{"foo": "bar"}, - Annotations: model.LabelSet{ - "notify": "team-a", - }, + Duration: 5 * time.Minute, + Labels: labels.FromStrings("foo", "bar"), + Annotations: labels.FromStrings("notify", "team-a"), } expected := `ALERT FooAlert diff --git a/promql/quantile.go b/promql/quantile.go index a4a6f136b..4e69006c5 100644 --- a/promql/quantile.go +++ b/promql/quantile.go @@ -17,23 +17,22 @@ import ( "math" "sort" + "github.com/fabxc/tsdb/labels" "github.com/prometheus/common/model" - - "github.com/prometheus/prometheus/storage/metric" ) // Helpers to calculate quantiles. // excludedLabels are the labels to exclude from signature calculation for // quantiles. -var excludedLabels = map[model.LabelName]struct{}{ - model.MetricNameLabel: {}, - model.BucketLabel: {}, +var excludedLabels = []string{ + model.MetricNameLabel, + model.BucketLabel, } type bucket struct { upperBound float64 - count model.SampleValue + count float64 } // buckets implements sort.Interface. @@ -44,7 +43,7 @@ func (b buckets) Swap(i, j int) { b[i], b[j] = b[j], b[i] } func (b buckets) Less(i, j int) bool { return b[i].upperBound < b[j].upperBound } type metricWithBuckets struct { - metric metric.Metric + metric labels.Labels buckets buckets } @@ -70,7 +69,7 @@ type metricWithBuckets struct { // If q<0, -Inf is returned. // // If q>1, +Inf is returned. -func bucketQuantile(q model.SampleValue, buckets buckets) float64 { +func bucketQuantile(q float64, buckets buckets) float64 { if q < 0 { return math.Inf(-1) } diff --git a/promql/test.go b/promql/test.go index 8b10d450f..fc8497921 100644 --- a/promql/test.go +++ b/promql/test.go @@ -22,6 +22,7 @@ import ( "strings" "time" + "github.com/fabxc/tsdb/labels" "github.com/prometheus/common/model" "golang.org/x/net/context" @@ -39,10 +40,11 @@ var ( ) const ( - testStartTime = model.Time(0) - epsilon = 0.000001 // Relative error allowed for sample values. + epsilon = 0.000001 // Relative error allowed for sample values. ) +var testStartTime = time.Time{} + // Test is a sequence of read and write commands that are run // against a test storage. type Test struct { @@ -170,7 +172,7 @@ func (t *Test) parseEval(lines []string, i int) (int, *evalCmd, error) { break } if f, err := parseNumber(defLine); err == nil { - cmd.expect(0, nil, sequenceValue{value: model.SampleValue(f)}) + cmd.expect(0, nil, sequenceValue{value: f}) break } metric, vals, err := parseSeriesDesc(defLine) @@ -243,15 +245,15 @@ func (*evalCmd) testCmd() {} // metrics into the storage. type loadCmd struct { gap time.Duration - metrics map[model.Fingerprint]model.Metric - defs map[model.Fingerprint][]model.SamplePair + metrics map[uint64]labels.Labels + defs map[uint64][]samplePair } func newLoadCmd(gap time.Duration) *loadCmd { return &loadCmd{ gap: gap, - metrics: map[model.Fingerprint]model.Metric{}, - defs: map[model.Fingerprint][]model.SamplePair{}, + metrics: map[uint64]labels.Labels{}, + defs: map[uint64][]samplePair{}, } } @@ -260,51 +262,52 @@ func (cmd loadCmd) String() string { } // set a sequence of sample values for the given metric. -func (cmd *loadCmd) set(m model.Metric, vals ...sequenceValue) { - fp := m.Fingerprint() +func (cmd *loadCmd) set(m labels.Labels, vals ...sequenceValue) { + h := m.Hash() - samples := make([]model.SamplePair, 0, len(vals)) + samples := make([]samplePair, 0, len(vals)) ts := testStartTime for _, v := range vals { if !v.omitted { - samples = append(samples, model.SamplePair{ - Timestamp: ts, - Value: v.value, + samples = append(samples, samplePair{ + t: ts.UnixNano() / int64(time.Millisecond/time.Nanosecond), + v: v.value, }) } ts = ts.Add(cmd.gap) } - cmd.defs[fp] = samples - cmd.metrics[fp] = m + cmd.defs[h] = samples + cmd.metrics[h] = m } // append the defined time series to the storage. func (cmd *loadCmd) append(a storage.SampleAppender) { - for fp, samples := range cmd.defs { - met := cmd.metrics[fp] - for _, smpl := range samples { - s := &model.Sample{ - Metric: met, - Value: smpl.Value, - Timestamp: smpl.Timestamp, - } - a.Append(s) - } - } + // TODO(fabxc): commented out until Appender refactoring. + // for fp, samples := range cmd.defs { + // met := cmd.metrics[fp] + // for _, smpl := range samples { + // s := &model.Sample{ + // Metric: met, + // Value: smpl.Value, + // Timestamp: smpl.Timestamp, + // } + // a.Append(s) + // } + // } } // evalCmd is a command that evaluates an expression for the given time (range) // and expects a specific result. type evalCmd struct { expr Expr - start, end model.Time + start, end time.Time interval time.Duration instant bool fail, ordered bool - metrics map[model.Fingerprint]model.Metric - expected map[model.Fingerprint]entry + metrics map[uint64]labels.Labels + expected map[uint64]entry } type entry struct { @@ -316,7 +319,7 @@ func (e entry) String() string { return fmt.Sprintf("%d: %s", e.pos, e.vals) } -func newEvalCmd(expr Expr, start, end model.Time, interval time.Duration) *evalCmd { +func newEvalCmd(expr Expr, start, end time.Time, interval time.Duration) *evalCmd { return &evalCmd{ expr: expr, start: start, @@ -324,8 +327,8 @@ func newEvalCmd(expr Expr, start, end model.Time, interval time.Duration) *evalC interval: interval, instant: start == end && interval == 0, - metrics: map[model.Fingerprint]model.Metric{}, - expected: map[model.Fingerprint]entry{}, + metrics: map[uint64]labels.Labels{}, + expected: map[uint64]entry{}, } } @@ -335,26 +338,26 @@ func (ev *evalCmd) String() string { // expect adds a new metric with a sequence of values to the set of expected // results for the query. -func (ev *evalCmd) expect(pos int, m model.Metric, vals ...sequenceValue) { +func (ev *evalCmd) expect(pos int, m labels.Labels, vals ...sequenceValue) { if m == nil { ev.expected[0] = entry{pos: pos, vals: vals} return } - fp := m.Fingerprint() - ev.metrics[fp] = m - ev.expected[fp] = entry{pos: pos, vals: vals} + h := m.Hash() + ev.metrics[h] = m + ev.expected[h] = entry{pos: pos, vals: vals} } // compareResult compares the result value with the defined expectation. -func (ev *evalCmd) compareResult(result model.Value) error { +func (ev *evalCmd) compareResult(result Value) error { switch val := result.(type) { - case model.Matrix: + case matrix: if ev.instant { return fmt.Errorf("received range result on instant evaluation") } - seen := map[model.Fingerprint]bool{} + seen := map[uint64]bool{} for pos, v := range val { - fp := v.Metric.Fingerprint() + fp := v.Metric.Hash() if _, ok := ev.metrics[fp]; !ok { return fmt.Errorf("unexpected metric %s in result", v.Metric) } @@ -363,7 +366,7 @@ func (ev *evalCmd) compareResult(result model.Value) error { return fmt.Errorf("expected metric %s with %v at position %d but was at %d", v.Metric, exp.vals, exp.pos, pos+1) } for i, expVal := range exp.vals { - if !almostEqual(float64(expVal.value), float64(v.Values[i].Value)) { + if !almostEqual(expVal.value, v.Values[i].v) { return fmt.Errorf("expected %v for %s but got %v", expVal, v.Metric, v.Values) } } @@ -375,13 +378,13 @@ func (ev *evalCmd) compareResult(result model.Value) error { } } - case model.Vector: + case vector: if !ev.instant { return fmt.Errorf("received instant result on range evaluation") } - seen := map[model.Fingerprint]bool{} + seen := map[uint64]bool{} for pos, v := range val { - fp := v.Metric.Fingerprint() + fp := v.Metric.Hash() if _, ok := ev.metrics[fp]; !ok { return fmt.Errorf("unexpected metric %s in result", v.Metric) } @@ -401,9 +404,9 @@ func (ev *evalCmd) compareResult(result model.Value) error { } } - case *model.Scalar: - if !almostEqual(float64(ev.expected[0].vals[0].value), float64(val.Value)) { - return fmt.Errorf("expected scalar %v but got %v", val.Value, ev.expected[0].vals[0].value) + case scalar: + if !almostEqual(ev.expected[0].vals[0].value, val.v) { + return fmt.Errorf("expected scalar %v but got %v", val.v, ev.expected[0].vals[0].value) } default: @@ -506,7 +509,8 @@ func (t *Test) clear() { t.storage, closer = local.NewTestStorage(t, 2) t.closeStorage = closer.Close - t.queryEngine = NewEngine(t.storage, nil) + // TODO(fabxc): add back + // t.queryEngine = NewEngine(t.storage, nil) t.context, t.cancelCtx = context.WithCancel(context.Background()) }