diff --git a/rules/ast/ast.go b/rules/ast/ast.go index f58fabc92..862c96449 100644 --- a/rules/ast/ast.go +++ b/rules/ast/ast.go @@ -32,8 +32,12 @@ import ( // ---------------------------------------------------------------------------- // Raw data value types. +// Vector is basically only an alias for clientmodel.Samples, but the +// contract is that in a Vector, all Samples have the same timestamp. type Vector clientmodel.Samples +// Matrix is a slice of SampleSets that implements sort.Interface and +// has a String method. // BUG(julius): Pointerize this. type Matrix []metric.SampleSet @@ -46,9 +50,10 @@ type groupedAggregation struct { // ---------------------------------------------------------------------------- // Enums. -// Rule language expression types. +// ExprType is an enum for the rule language expression types. type ExprType int +// Possible language expression types. const ( SCALAR ExprType = iota VECTOR @@ -56,9 +61,10 @@ const ( STRING ) -// Binary operator types. +// BinOpType is an enum for binary operator types. type BinOpType int +// Possible binary operator types. const ( ADD BinOpType = iota SUB @@ -75,9 +81,10 @@ const ( OR ) -// Aggregation types. +// AggrType is an enum for aggregation types. type AggrType int +// Possible aggregation types. const ( SUM AggrType = iota AVG @@ -89,9 +96,13 @@ const ( // ---------------------------------------------------------------------------- // Interfaces. -// All node interfaces include the Node interface. +// Nodes is a slice of any mix of node types as all node types +// implement the Node interface. type Nodes []Node +// Node is the top-level interface for any kind of nodes. Each node +// type implements one of the ...Node interfaces, each of which embeds +// this Node interface. type Node interface { Type() ExprType Children() Nodes @@ -99,26 +110,37 @@ type Node interface { String() string } -// All node types implement one of the following interfaces. The name of the -// interface represents the type returned to the parent node. +// ScalarNode is a Node for scalar values. type ScalarNode interface { Node + // Eval evaluates and returns the value of the scalar represented by this node. Eval(timestamp clientmodel.Timestamp, view *viewAdapter) clientmodel.SampleValue } +// VectorNode is a Node for vector values. type VectorNode interface { Node + // Eval evaluates the node recursively and returns the result + // as a Vector (i.e. a slice of Samples all at the given + // Timestamp). Eval(timestamp clientmodel.Timestamp, view *viewAdapter) Vector } +// MatrixNode is a Node for matrix values. type MatrixNode interface { Node + // Eval evaluates the node recursively and returns the result as a Matrix. Eval(timestamp clientmodel.Timestamp, view *viewAdapter) Matrix + // Eval evaluates the node recursively and returns the result + // as a Matrix that only contains the boundary values. EvalBoundaries(timestamp clientmodel.Timestamp, view *viewAdapter) Matrix } +// StringNode is a Node for string values. type StringNode interface { Node + // Eval evaluates and returns the value of the string + // represented by this node. Eval(timestamp clientmodel.Timestamp, view *viewAdapter) string } @@ -126,18 +148,20 @@ type StringNode interface { // ScalarNode types. type ( - // A numeric literal. + // ScalarLiteral represents a numeric literal. ScalarLiteral struct { value clientmodel.SampleValue } - // A function of numeric return type. + // ScalarFunctionCall represents a function with a numeric + // return type. ScalarFunctionCall struct { function *Function args Nodes } - // An arithmetic expression of numeric type. + // ScalarArithExpr represents an arithmetic expression of + // numeric type. ScalarArithExpr struct { opType BinOpType lhs ScalarNode @@ -149,20 +173,21 @@ type ( // VectorNode types. type ( - // Vector literal, i.e. metric name plus labelset. + // A VectorLiteral represents a metric name plus labelset. VectorLiteral struct { labels clientmodel.LabelSet // Fingerprints are populated from labels at query analysis time. fingerprints clientmodel.Fingerprints } - // A function of vector return type. + // VectorFunctionCall represents a function with vector return + // type. VectorFunctionCall struct { function *Function args Nodes } - // A vector aggregation with vector return type. + // A VectorAggregation with vector return type. VectorAggregation struct { aggrType AggrType groupBy clientmodel.LabelNames @@ -170,7 +195,8 @@ type ( vector VectorNode } - // An arithmetic expression of vector type. + // VectorArithExpr represents an arithmetic expression of + // vector type. VectorArithExpr struct { opType BinOpType lhs VectorNode @@ -182,10 +208,12 @@ type ( // MatrixNode types. type ( - // Matrix literal, i.e. metric name plus labelset and timerange. + // A MatrixLiteral represents a metric name plus labelset and + // timerange. MatrixLiteral struct { labels clientmodel.LabelSet - // Fingerprints are populated from labels at query analysis time. + // Fingerprints are populated from labels at query + // analysis time. fingerprints clientmodel.Fingerprints interval time.Duration } @@ -195,12 +223,13 @@ type ( // StringNode types. type ( - // String literal. + // A StringLiteral is what you think it is. StringLiteral struct { str string } - // A function of string return type. + // StringFunctionCall represents a function with string return + // type. StringFunctionCall struct { function *Function args Nodes @@ -210,40 +239,88 @@ type ( // ---------------------------------------------------------------------------- // Implementations. -// Node.Type() methods. -func (node ScalarLiteral) Type() ExprType { return SCALAR } +// Type implements the Node interface. +func (node ScalarLiteral) Type() ExprType { return SCALAR } + +// Type implements the Node interface. func (node ScalarFunctionCall) Type() ExprType { return SCALAR } -func (node ScalarArithExpr) Type() ExprType { return SCALAR } -func (node VectorLiteral) Type() ExprType { return VECTOR } + +// Type implements the Node interface. +func (node ScalarArithExpr) Type() ExprType { return SCALAR } + +// Type implements the Node interface. +func (node VectorLiteral) Type() ExprType { return VECTOR } + +// Type implements the Node interface. func (node VectorFunctionCall) Type() ExprType { return VECTOR } -func (node VectorAggregation) Type() ExprType { return VECTOR } -func (node VectorArithExpr) Type() ExprType { return VECTOR } -func (node MatrixLiteral) Type() ExprType { return MATRIX } -func (node StringLiteral) Type() ExprType { return STRING } + +// Type implements the Node interface. +func (node VectorAggregation) Type() ExprType { return VECTOR } + +// Type implements the Node interface. +func (node VectorArithExpr) Type() ExprType { return VECTOR } + +// Type implements the Node interface. +func (node MatrixLiteral) Type() ExprType { return MATRIX } + +// Type implements the Node interface. +func (node StringLiteral) Type() ExprType { return STRING } + +// Type implements the Node interface. func (node StringFunctionCall) Type() ExprType { return STRING } -// Node.Children() methods. -func (node ScalarLiteral) Children() Nodes { return Nodes{} } +// Children implements the Node interface and returns an empty slice. +func (node ScalarLiteral) Children() Nodes { return Nodes{} } + +// Children implements the Node interface and returns the args of the +// function call. func (node ScalarFunctionCall) Children() Nodes { return node.args } -func (node ScalarArithExpr) Children() Nodes { return Nodes{node.lhs, node.rhs} } -func (node VectorLiteral) Children() Nodes { return Nodes{} } + +// Children implements the Node interface and returns the LHS and the RHS +// of the expression. +func (node ScalarArithExpr) Children() Nodes { return Nodes{node.lhs, node.rhs} } + +// Children implements the Node interface and returns an empty slice. +func (node VectorLiteral) Children() Nodes { return Nodes{} } + +// Children implements the Node interface and returns the args of the +// function call. func (node VectorFunctionCall) Children() Nodes { return node.args } -func (node VectorAggregation) Children() Nodes { return Nodes{node.vector} } -func (node VectorArithExpr) Children() Nodes { return Nodes{node.lhs, node.rhs} } -func (node MatrixLiteral) Children() Nodes { return Nodes{} } -func (node StringLiteral) Children() Nodes { return Nodes{} } + +// Children implements the Node interface and returns the vector to be +// aggregated. +func (node VectorAggregation) Children() Nodes { return Nodes{node.vector} } + +// Children implements the Node interface and returns the LHS and the RHS +// of the expression. +func (node VectorArithExpr) Children() Nodes { return Nodes{node.lhs, node.rhs} } + +// Children implements the Node interface and returns an empty slice. +func (node MatrixLiteral) Children() Nodes { return Nodes{} } + +// Children implements the Node interface and returns an empty slice. +func (node StringLiteral) Children() Nodes { return Nodes{} } + +// Children implements the Node interface and returns the args of the +// function call. func (node StringFunctionCall) Children() Nodes { return node.args } +// Eval implements the ScalarNode interface and returns the literal +// value. func (node *ScalarLiteral) Eval(timestamp clientmodel.Timestamp, view *viewAdapter) clientmodel.SampleValue { return node.value } +// Eval implements the ScalarNode interface and returns the result of +// the expression. func (node *ScalarArithExpr) Eval(timestamp clientmodel.Timestamp, view *viewAdapter) clientmodel.SampleValue { lhs := node.lhs.Eval(timestamp, view) rhs := node.rhs.Eval(timestamp, view) return evalScalarBinop(node.opType, lhs, rhs) } +// Eval implements the ScalarNode interface and returns the result of +// the function call. func (node *ScalarFunctionCall) Eval(timestamp clientmodel.Timestamp, view *viewAdapter) clientmodel.SampleValue { return node.function.callFn(timestamp, view, node.args).(clientmodel.SampleValue) } @@ -278,6 +355,7 @@ func labelsToKey(labels clientmodel.Metric) uint64 { return summer.Sum64() } +// EvalVectorInstant evaluates a VectorNode with an instant query. func EvalVectorInstant(node VectorNode, timestamp clientmodel.Timestamp, storage *metric.TieredStorage, queryStats *stats.TimerGroup) (vector Vector, err error) { viewAdapter, err := viewAdapterForInstantQuery(node, timestamp, storage, queryStats) if err != nil { @@ -287,6 +365,7 @@ func EvalVectorInstant(node VectorNode, timestamp clientmodel.Timestamp, storage return } +// EvalVectorRange evaluates a VectorNode with a range query. func EvalVectorRange(node VectorNode, start clientmodel.Timestamp, end clientmodel.Timestamp, interval time.Duration, storage *metric.TieredStorage, queryStats *stats.TimerGroup) (Matrix, error) { // Explicitly initialize to an empty matrix since a nil Matrix encodes to // null in JSON. @@ -362,6 +441,8 @@ func (node *VectorAggregation) groupedAggregationsToVector(aggregations map[uint return vector } +// Eval implements the VectorNode interface and returns the aggregated +// Vector. func (node *VectorAggregation) Eval(timestamp clientmodel.Timestamp, view *viewAdapter) Vector { vector := node.vector.Eval(timestamp, view) result := map[uint64]*groupedAggregation{} @@ -414,6 +495,8 @@ func (node *VectorAggregation) Eval(timestamp clientmodel.Timestamp, view *viewA return node.groupedAggregationsToVector(result, timestamp) } +// Eval implements the VectorNode interface and returns the value of +// the literal. func (node *VectorLiteral) Eval(timestamp clientmodel.Timestamp, view *viewAdapter) Vector { values, err := view.GetValueAtTime(node.fingerprints, timestamp) if err != nil { @@ -423,6 +506,8 @@ func (node *VectorLiteral) Eval(timestamp clientmodel.Timestamp, view *viewAdapt return values } +// Eval implements the VectorNode interface and returns the result of +// the function call. func (node *VectorFunctionCall) Eval(timestamp clientmodel.Timestamp, view *viewAdapter) Vector { return node.function.callFn(timestamp, view, node.args).(Vector) } @@ -440,51 +525,43 @@ func evalScalarBinop(opType BinOpType, case DIV: if rhs != 0 { return lhs / rhs - } else { - return clientmodel.SampleValue(math.Inf(int(rhs))) } + return clientmodel.SampleValue(math.Inf(int(rhs))) case MOD: if rhs != 0 { return clientmodel.SampleValue(int(lhs) % int(rhs)) - } else { - return clientmodel.SampleValue(math.Inf(int(rhs))) } + return clientmodel.SampleValue(math.Inf(int(rhs))) case EQ: if lhs == rhs { return 1 - } else { - return 0 } + return 0 case NE: if lhs != rhs { return 1 - } else { - return 0 } + return 0 case GT: if lhs > rhs { return 1 - } else { - return 0 } + return 0 case LT: if lhs < rhs { return 1 - } else { - return 0 } + return 0 case GE: if lhs >= rhs { return 1 - } else { - return 0 } + return 0 case LE: if lhs <= rhs { return 1 - } else { - return 0 } + return 0 } panic("Not all enum values enumerated in switch") } @@ -502,51 +579,43 @@ func evalVectorBinop(opType BinOpType, case DIV: if rhs != 0 { return lhs / rhs, true - } else { - return clientmodel.SampleValue(math.Inf(int(rhs))), true } + return clientmodel.SampleValue(math.Inf(int(rhs))), true case MOD: if rhs != 0 { return clientmodel.SampleValue(int(lhs) % int(rhs)), true - } else { - return clientmodel.SampleValue(math.Inf(int(rhs))), true } + return clientmodel.SampleValue(math.Inf(int(rhs))), true case EQ: if lhs == rhs { return lhs, true - } else { - return 0, false } + return 0, false case NE: if lhs != rhs { return lhs, true - } else { - return 0, false } + return 0, false case GT: if lhs > rhs { return lhs, true - } else { - return 0, false } + return 0, false case LT: if lhs < rhs { return lhs, true - } else { - return 0, false } + return 0, false case GE: if lhs >= rhs { return lhs, true - } else { - return 0, false } + return 0, false case LE: if lhs <= rhs { return lhs, true - } else { - return 0, false } + return 0, false case AND: return lhs, true case OR: @@ -567,6 +636,8 @@ func labelsEqual(labels1, labels2 clientmodel.Metric) bool { return true } +// Eval implements the VectorNode interface and returns the result of +// the expression. func (node *VectorArithExpr) Eval(timestamp clientmodel.Timestamp, view *viewAdapter) Vector { lhs := node.lhs.Eval(timestamp, view) result := Vector{} @@ -598,6 +669,8 @@ func (node *VectorArithExpr) Eval(timestamp clientmodel.Timestamp, view *viewAda panic("Invalid vector arithmetic expression operands") } +// Eval implements the MatrixNode interface and returns the value of +// the literal. func (node *MatrixLiteral) Eval(timestamp clientmodel.Timestamp, view *viewAdapter) Matrix { interval := &metric.Interval{ OldestInclusive: timestamp.Add(-node.interval), @@ -611,6 +684,8 @@ func (node *MatrixLiteral) Eval(timestamp clientmodel.Timestamp, view *viewAdapt return values } +// EvalBoundaries implements the MatrixNode interface and returns the +// boundary values of the literal. func (node *MatrixLiteral) EvalBoundaries(timestamp clientmodel.Timestamp, view *viewAdapter) Matrix { interval := &metric.Interval{ OldestInclusive: timestamp.Add(-node.interval), @@ -624,22 +699,29 @@ func (node *MatrixLiteral) EvalBoundaries(timestamp clientmodel.Timestamp, view return values } +// Len implements sort.Interface. func (matrix Matrix) Len() int { return len(matrix) } +// Less implements sort.Interface. func (matrix Matrix) Less(i, j int) bool { return matrix[i].Metric.String() < matrix[j].Metric.String() } +// Swap implements sort.Interface. func (matrix Matrix) Swap(i, j int) { matrix[i], matrix[j] = matrix[j], matrix[i] } +// Eval implements the StringNode interface and returns the value of +// the literal. func (node *StringLiteral) Eval(timestamp clientmodel.Timestamp, view *viewAdapter) string { return node.str } +// Eval implements the StringNode interface and returns the result of +// the function call. func (node *StringFunctionCall) Eval(timestamp clientmodel.Timestamp, view *viewAdapter) string { return node.function.callFn(timestamp, view, node.args).(string) } @@ -647,18 +729,24 @@ func (node *StringFunctionCall) Eval(timestamp clientmodel.Timestamp, view *view // ---------------------------------------------------------------------------- // Constructors. +// NewScalarLiteral returns a ScalarLiteral with the given value. func NewScalarLiteral(value clientmodel.SampleValue) *ScalarLiteral { return &ScalarLiteral{ value: value, } } +// NewVectorLiteral returns a (not yet evaluated) VectorLiteral with +// the given LabelSet. func NewVectorLiteral(labels clientmodel.LabelSet) *VectorLiteral { return &VectorLiteral{ labels: labels, } } +// NewVectorAggregation returns a (not yet evaluated) +// VectorAggregation, aggregating the given VectorNode using the given +// AggrType, grouping by the given LabelNames. func NewVectorAggregation(aggrType AggrType, vector VectorNode, groupBy clientmodel.LabelNames, keepExtraLabels bool) *VectorAggregation { return &VectorAggregation{ aggrType: aggrType, @@ -668,6 +756,9 @@ func NewVectorAggregation(aggrType AggrType, vector VectorNode, groupBy clientmo } } +// NewFunctionCall returns a (not yet evaluated) function call node +// (of type ScalarFunctionCall, VectorFunctionCall, or +// StringFunctionCall). func NewFunctionCall(function *Function, args Nodes) (Node, error) { if err := function.CheckArgTypes(args); err != nil { return nil, err @@ -707,12 +798,14 @@ func nodesHaveTypes(nodes Nodes, exprTypes []ExprType) bool { return true } +// NewArithExpr returns a (not yet evaluated) expression node (of type +// VectorArithExpr or ScalarArithExpr). func NewArithExpr(opType BinOpType, lhs Node, rhs Node) (Node, error) { if !nodesHaveTypes(Nodes{lhs, rhs}, []ExprType{SCALAR, VECTOR}) { - return nil, errors.New("Binary operands must be of vector or scalar type") + return nil, errors.New("binary operands must be of vector or scalar type") } if lhs.Type() == SCALAR && rhs.Type() == VECTOR { - return nil, errors.New("Left side of vector binary operation must be of vector type") + return nil, errors.New("left side of vector binary operation must be of vector type") } if opType == AND || opType == OR { @@ -736,6 +829,8 @@ func NewArithExpr(opType BinOpType, lhs Node, rhs Node) (Node, error) { }, nil } +// NewMatrixLiteral returns a (not yet evaluated) MatrixLiteral with +// the given VectorLiteral and Duration. func NewMatrixLiteral(vector *VectorLiteral, interval time.Duration) *MatrixLiteral { return &MatrixLiteral{ labels: vector.labels, @@ -743,6 +838,8 @@ func NewMatrixLiteral(vector *VectorLiteral, interval time.Duration) *MatrixLite } } +// NewStringLiteral returns a StringLiteral with the given string as +// value. func NewStringLiteral(str string) *StringLiteral { return &StringLiteral{ str: str, diff --git a/rules/ast/functions.go b/rules/ast/functions.go index 06d389a17..1ffa6e5fe 100644 --- a/rules/ast/functions.go +++ b/rules/ast/functions.go @@ -14,7 +14,6 @@ package ast import ( - "errors" "fmt" "math" "sort" @@ -25,6 +24,8 @@ import ( "github.com/prometheus/prometheus/utility" ) +// Function represents a function of the expression language and is +// used by function nodes. type Function struct { name string argTypes []ExprType @@ -32,11 +33,14 @@ type Function struct { callFn func(timestamp clientmodel.Timestamp, view *viewAdapter, args []Node) interface{} } +// CheckArgTypes returns a non-nil error if the number or types of +// passed in arg nodes do not match the function's expectations. func (function *Function) CheckArgTypes(args []Node) error { if len(function.argTypes) != len(args) { - return errors.New( - fmt.Sprintf("Wrong number of arguments to function %v(): %v expected, %v given", - function.name, len(function.argTypes), len(args))) + return fmt.Errorf( + "wrong number of arguments to function %v(): %v expected, %v given", + function.name, len(function.argTypes), len(args), + ) } for idx, argType := range function.argTypes { invalidType := false @@ -59,9 +63,10 @@ func (function *Function) CheckArgTypes(args []Node) error { } if invalidType { - return errors.New( - fmt.Sprintf("Wrong type for argument %v in function %v(), expected %v", - idx, function.name, expectedType)) + return fmt.Errorf( + "wrong type for argument %v in function %v(), expected %v", + idx, function.name, expectedType, + ) } } return nil @@ -175,7 +180,7 @@ func sortImpl(timestamp clientmodel.Timestamp, view *viewAdapter, args []Node) i // === sortDesc(node *VectorNode) Vector === func sortDescImpl(timestamp clientmodel.Timestamp, view *viewAdapter, args []Node) interface{} { descByValueSorter := utility.ReverseSorter{ - vectorByValueSorter{ + Interface: vectorByValueSorter{ vector: args[0].(VectorNode).Eval(timestamp, view), }, } @@ -321,10 +326,12 @@ var functions = map[string]*Function{ }, } +// GetFunction returns a predefined Function object for the given +// name. func GetFunction(name string) (*Function, error) { function, ok := functions[name] if !ok { - return nil, errors.New(fmt.Sprintf("Couldn't find function %v()", name)) + return nil, fmt.Errorf("couldn't find function %v()", name) } return function, nil } diff --git a/rules/ast/persistence_adapter.go b/rules/ast/persistence_adapter.go index d12629293..4c4033356 100644 --- a/rules/ast/persistence_adapter.go +++ b/rules/ast/persistence_adapter.go @@ -25,7 +25,8 @@ import ( var defaultStalenessDelta = flag.Int("defaultStalenessDelta", 300, "Default staleness delta allowance in seconds during expression evaluations.") -// Describes the lenience limits to apply to values from the materialized view. +// StalenessPolicy describes the lenience limits to apply to values +// from the materialized view. type StalenessPolicy struct { // Describes the inclusive limit at which individual points if requested will // be matched and subject to interpolation. @@ -181,6 +182,8 @@ func (v *viewAdapter) GetRangeValues(fingerprints clientmodel.Fingerprints, inte return sampleSets, nil } +// NewViewAdapter returns an initialized view adapter with a default +// staleness policy (based on the --defaultStalenessDelta flag). func NewViewAdapter(view metric.View, storage *metric.TieredStorage, queryStats *stats.TimerGroup) *viewAdapter { stalenessPolicy := StalenessPolicy{ DeltaAllowance: time.Duration(*defaultStalenessDelta) * time.Second, diff --git a/rules/ast/printer.go b/rules/ast/printer.go index 6c0e9b768..122267e72 100644 --- a/rules/ast/printer.go +++ b/rules/ast/printer.go @@ -26,8 +26,10 @@ import ( "github.com/prometheus/prometheus/utility" ) +// OutputFormat is an enum for the possible output formats. type OutputFormat int +// Possible output formats. const ( TEXT OutputFormat = iota JSON @@ -113,6 +115,7 @@ func (matrix Matrix) String() string { return strings.Join(metricStrings, "\n") } +// ErrorToJSON converts the given error into JSON. func ErrorToJSON(err error) string { errorStruct := struct { Type string @@ -129,6 +132,8 @@ func ErrorToJSON(err error) string { return string(errorJSON) } +// TypedValueToJSON converts the given data of type 'scalar', +// 'vector', or 'matrix' into its JSON representation. func TypedValueToJSON(data interface{}, typeStr string) string { dataStruct := struct { Type string @@ -144,6 +149,7 @@ func TypedValueToJSON(data interface{}, typeStr string) string { return string(dataJSON) } +// EvalToString evaluates the given node into a string of the given format. func EvalToString(node Node, timestamp clientmodel.Timestamp, format OutputFormat, storage *metric.TieredStorage, queryStats *stats.TimerGroup) string { viewTimer := queryStats.GetTimer(stats.TotalViewBuildingTime).Start() viewAdapter, err := viewAdapterForInstantQuery(node, timestamp, storage, queryStats) @@ -194,6 +200,8 @@ func EvalToString(node Node, timestamp clientmodel.Timestamp, format OutputForma panic("Switch didn't cover all node types") } +// NodeTreeToDotGraph returns a DOT representation of the scalar +// literal. func (node *ScalarLiteral) NodeTreeToDotGraph() string { return fmt.Sprintf("%#p[label=\"%v\"];\n", node, node.value) } @@ -209,12 +217,15 @@ func functionArgsToDotGraph(node Node, args []Node) string { return graph } +// NodeTreeToDotGraph returns a DOT representation of the function +// call. func (node *ScalarFunctionCall) NodeTreeToDotGraph() string { graph := fmt.Sprintf("%#p[label=\"%s\"];\n", node, node.function.name) graph += functionArgsToDotGraph(node, node.args) return graph } +// NodeTreeToDotGraph returns a DOT representation of the expression. func (node *ScalarArithExpr) NodeTreeToDotGraph() string { graph := fmt.Sprintf(` %#p[label="%s"]; @@ -226,16 +237,21 @@ func (node *ScalarArithExpr) NodeTreeToDotGraph() string { return graph } +// NodeTreeToDotGraph returns a DOT representation of the vector literal. func (node *VectorLiteral) NodeTreeToDotGraph() string { return fmt.Sprintf("%#p[label=\"%s\"];\n", node, node) } +// NodeTreeToDotGraph returns a DOT representation of the function +// call. func (node *VectorFunctionCall) NodeTreeToDotGraph() string { graph := fmt.Sprintf("%#p[label=\"%s\"];\n", node, node.function.name) graph += functionArgsToDotGraph(node, node.args) return graph } +// NodeTreeToDotGraph returns a DOT representation of the vector +// aggregation. func (node *VectorAggregation) NodeTreeToDotGraph() string { groupByStrings := make([]string, 0, len(node.groupBy)) for _, label := range node.groupBy { @@ -251,6 +267,7 @@ func (node *VectorAggregation) NodeTreeToDotGraph() string { return graph } +// NodeTreeToDotGraph returns a DOT representation of the expression. func (node *VectorArithExpr) NodeTreeToDotGraph() string { graph := fmt.Sprintf(` %#p[label="%s"]; @@ -262,14 +279,20 @@ func (node *VectorArithExpr) NodeTreeToDotGraph() string { return graph } +// NodeTreeToDotGraph returns a DOT representation of the matrix +// literal. func (node *MatrixLiteral) NodeTreeToDotGraph() string { return fmt.Sprintf("%#p[label=\"%s\"];\n", node, node) } +// NodeTreeToDotGraph returns a DOT representation of the string +// literal. func (node *StringLiteral) NodeTreeToDotGraph() string { return fmt.Sprintf("%#p[label=\"'%q'\"];\n", node, node.str) } +// NodeTreeToDotGraph returns a DOT representation of the function +// call. func (node *StringFunctionCall) NodeTreeToDotGraph() string { graph := fmt.Sprintf("%#p[label=\"%s\"];\n", node, node.function.name) graph += functionArgsToDotGraph(node, node.args) @@ -325,9 +348,8 @@ func (node *VectorAggregation) String() string { aggrString := fmt.Sprintf("%s(%s)", node.aggrType, node.vector) if len(node.groupBy) > 0 { return fmt.Sprintf("%s BY (%s)", aggrString, node.groupBy) - } else { - return aggrString } + return aggrString } func (node *VectorArithExpr) String() string { diff --git a/rules/ast/query_analyzer.go b/rules/ast/query_analyzer.go index b1fc11aab..4945c61cc 100644 --- a/rules/ast/query_analyzer.go +++ b/rules/ast/query_analyzer.go @@ -24,9 +24,16 @@ import ( "github.com/prometheus/prometheus/storage/metric" ) +// FullRangeMap maps the fingerprint of a full range to the duration +// of the matrix literal it resulted from. type FullRangeMap map[clientmodel.Fingerprint]time.Duration + +// IntervalRangeMap is a set of fingerprints of interval ranges. type IntervalRangeMap map[clientmodel.Fingerprint]bool +// A QueryAnalyzer recursively traverses the AST to look for any nodes +// which will need data from the datastore. Instantiate with +// NewQueryAnalyzer. type QueryAnalyzer struct { // Values collected by query analysis. // @@ -37,13 +44,16 @@ type QueryAnalyzer struct { // This is because full ranges can only result from matrix literals (like // "foo[5m]"), which have said time-spanning behavior during a ranged query. FullRanges FullRangeMap - // Interval ranges always implicitly span the whole query interval. + // Interval ranges always implicitly span the whole query range. IntervalRanges IntervalRangeMap // The underlying storage to which the query will be applied. Needed for // extracting timeseries fingerprint information during query analysis. storage *metric.TieredStorage } +// NewQueryAnalyzer returns a pointer to a newly instantiated +// QueryAnalyzer. The storage is needed to extract timeseries +// fingerprint information during query analysis. func NewQueryAnalyzer(storage *metric.TieredStorage) *QueryAnalyzer { return &QueryAnalyzer{ FullRanges: FullRangeMap{}, @@ -52,6 +62,7 @@ func NewQueryAnalyzer(storage *metric.TieredStorage) *QueryAnalyzer { } } +// Visit implements the Visitor interface. func (analyzer *QueryAnalyzer) Visit(node Node) { switch n := node.(type) { case *VectorLiteral: @@ -79,6 +90,8 @@ func (analyzer *QueryAnalyzer) Visit(node Node) { } } +// AnalyzeQueries walks the AST, starting at node, calling Visit on +// each node to collect fingerprints. func (analyzer *QueryAnalyzer) AnalyzeQueries(node Node) { Walk(analyzer, node) // Find and dedupe overlaps between full and stepped ranges. Full ranges diff --git a/rules/ast/walk.go b/rules/ast/walk.go index c74c0fd9d..a00fb334d 100644 --- a/rules/ast/walk.go +++ b/rules/ast/walk.go @@ -13,12 +13,13 @@ package ast +// Visitor is the interface for a Node visitor. type Visitor interface { Visit(node Node) } -// Walk() does a depth-first traversal of the AST, calling visitor.Visit() for -// each encountered node in the tree. +// Walk does a depth-first traversal of the AST, starting at node, +// calling visitor.Visit for each encountered Node in the tree. func Walk(visitor Visitor, node Node) { visitor.Visit(node) for _, childNode := range node.Children() {