prometheus/rules/ast/ast.go
Matt T. Proud efe61c18fa Refactor target scheduling to separate facility.
``Target`` will be refactored down the road to support various
nuanced endpoint types.  Thusly incorporating the scheduling
behavior within it will be problematic.  To that end, the scheduling
behavior has been moved into a separate assistance type to improve
conciseness and testability.

``make format`` was also run.
2013-01-13 10:43:37 +01:00

597 lines
13 KiB
Go

package ast
import (
"errors"
"github.com/matttproud/prometheus/model"
"log"
"math"
"strings"
"time"
)
// ----------------------------------------------------------------------------
// Raw data value types.
type Vector []*model.Sample
type Matrix []*model.SampleSet
type groupedAggregation struct {
labels model.Metric
value model.SampleValue
groupCount int
}
type labelValuePair struct {
label model.LabelName
value model.LabelValue
}
// ----------------------------------------------------------------------------
// Enums.
// Rule language expression types.
type ExprType int
const (
SCALAR ExprType = iota
VECTOR
MATRIX
STRING
)
// Binary operator types.
type BinOpType int
const (
ADD BinOpType = iota
SUB
MUL
DIV
MOD
NE
EQ
GT
LT
GE
LE
AND
OR
)
// Aggregation types.
type AggrType int
const (
SUM AggrType = iota
AVG
MIN
MAX
)
// ----------------------------------------------------------------------------
// Interfaces.
// All node interfaces include the Node interface.
type Node interface {
Type() ExprType
NodeTreeToDotGraph() string
}
// All node types implement one of the following interfaces. The name of the
// interface represents the type returned to the parent node.
type ScalarNode interface {
Node
Eval(timestamp *time.Time) model.SampleValue
}
type VectorNode interface {
Node
Eval(timestamp *time.Time) Vector
}
type MatrixNode interface {
Node
Eval(timestamp *time.Time) Matrix
EvalBoundaries(timestamp *time.Time) Matrix
}
type StringNode interface {
Node
Eval(timestamp *time.Time) string
}
// ----------------------------------------------------------------------------
// ScalarNode types.
type (
// A numeric literal.
ScalarLiteral struct {
value model.SampleValue
}
// A function of numeric return type.
ScalarFunctionCall struct {
function *Function
args []Node
}
// An arithmetic expression of numeric type.
ScalarArithExpr struct {
opType BinOpType
lhs ScalarNode
rhs ScalarNode
}
)
// ----------------------------------------------------------------------------
// VectorNode types.
type (
// Vector literal, i.e. metric name plus labelset.
VectorLiteral struct {
labels model.LabelSet
}
// A function of vector return type.
VectorFunctionCall struct {
function *Function
args []Node
}
// A vector aggregation with vector return type.
VectorAggregation struct {
aggrType AggrType
groupBy []model.LabelName
vector VectorNode
}
// An arithmetic expression of vector type.
VectorArithExpr struct {
opType BinOpType
lhs VectorNode
rhs Node
}
)
// ----------------------------------------------------------------------------
// MatrixNode types.
type (
// Matrix literal, i.e. metric name plus labelset and timerange.
MatrixLiteral struct {
labels model.LabelSet
interval time.Duration
}
)
// ----------------------------------------------------------------------------
// StringNode types.
type (
// String literal.
StringLiteral struct {
str string
}
// A function of string return type.
StringFunctionCall struct {
function *Function
args []Node
}
)
// ----------------------------------------------------------------------------
// Implementations.
func (node ScalarLiteral) Type() ExprType { return SCALAR }
func (node ScalarFunctionCall) Type() ExprType { return SCALAR }
func (node ScalarArithExpr) Type() ExprType { return SCALAR }
func (node VectorLiteral) Type() ExprType { return VECTOR }
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 }
func (node StringFunctionCall) Type() ExprType { return STRING }
func (node *ScalarLiteral) Eval(timestamp *time.Time) model.SampleValue {
return node.value
}
func (node *ScalarArithExpr) Eval(timestamp *time.Time) model.SampleValue {
lhs := node.lhs.Eval(timestamp)
rhs := node.rhs.Eval(timestamp)
return evalScalarBinop(node.opType, lhs, rhs)
}
func (node *ScalarFunctionCall) Eval(timestamp *time.Time) model.SampleValue {
return node.function.callFn(timestamp, node.args).(model.SampleValue)
}
func (node *VectorAggregation) labelsToGroupingKey(labels model.Metric) string {
keyParts := []string{}
for _, keyLabel := range node.groupBy {
keyParts = append(keyParts, string(labels[keyLabel]))
}
return strings.Join(keyParts, ",") // TODO not safe when label value contains comma.
}
func labelIntersection(metric1, metric2 model.Metric) model.Metric {
intersection := model.Metric{}
for label, value := range metric1 {
if metric2[label] == value {
intersection[label] = value
}
}
return intersection
}
func (node *VectorAggregation) groupedAggregationsToVector(aggregations map[string]*groupedAggregation, timestamp *time.Time) Vector {
vector := Vector{}
for _, aggregation := range aggregations {
if node.aggrType == AVG {
aggregation.value = aggregation.value / model.SampleValue(aggregation.groupCount)
}
sample := &model.Sample{
Metric: aggregation.labels,
Value: aggregation.value,
Timestamp: *timestamp,
}
vector = append(vector, sample)
}
return vector
}
func (node *VectorAggregation) Eval(timestamp *time.Time) Vector {
vector := node.vector.Eval(timestamp)
result := map[string]*groupedAggregation{}
for _, sample := range vector {
groupingKey := node.labelsToGroupingKey(sample.Metric)
if groupedResult, ok := result[groupingKey]; ok {
groupedResult.labels = labelIntersection(groupedResult.labels, sample.Metric)
switch node.aggrType {
case SUM:
groupedResult.value += sample.Value
case AVG:
groupedResult.value += sample.Value
groupedResult.groupCount++
case MAX:
if groupedResult.value < sample.Value {
groupedResult.value = sample.Value
}
case MIN:
if groupedResult.value > sample.Value {
groupedResult.value = sample.Value
}
}
} else {
result[groupingKey] = &groupedAggregation{
labels: sample.Metric,
value: sample.Value,
groupCount: 1,
}
}
}
return node.groupedAggregationsToVector(result, timestamp)
}
func (node *VectorLiteral) Eval(timestamp *time.Time) Vector {
values, err := persistence.GetValueAtTime(node.labels, timestamp, &stalenessPolicy)
if err != nil {
log.Printf("Unable to get vector values")
return Vector{}
}
return values
}
func (node *VectorFunctionCall) Eval(timestamp *time.Time) Vector {
return node.function.callFn(timestamp, node.args).(Vector)
}
func evalScalarBinop(opType BinOpType,
lhs model.SampleValue,
rhs model.SampleValue) model.SampleValue {
switch opType {
case ADD:
return lhs + rhs
case SUB:
return lhs - rhs
case MUL:
return lhs * rhs
case DIV:
if rhs != 0 {
return lhs / rhs
} else {
return model.SampleValue(math.Inf(int(rhs)))
}
case MOD:
if rhs != 0 {
return model.SampleValue(int(lhs) % int(rhs))
} else {
return model.SampleValue(math.Inf(int(rhs)))
}
case EQ:
if lhs == rhs {
return 1
} else {
return 0
}
case NE:
if lhs != rhs {
return 1
} else {
return 0
}
case GT:
if lhs > rhs {
return 1
} else {
return 0
}
case LT:
if lhs < rhs {
return 1
} else {
return 0
}
case GE:
if lhs >= rhs {
return 1
} else {
return 0
}
case LE:
if lhs <= rhs {
return 1
} else {
return 0
}
}
panic("Not all enum values enumerated in switch")
}
func evalVectorBinop(opType BinOpType,
lhs model.SampleValue,
rhs model.SampleValue) (model.SampleValue, bool) {
switch opType {
case ADD:
return lhs + rhs, true
case SUB:
return lhs - rhs, true
case MUL:
return lhs * rhs, true
case DIV:
if rhs != 0 {
return lhs / rhs, true
} else {
return model.SampleValue(math.Inf(int(rhs))), true
}
case MOD:
if rhs != 0 {
return model.SampleValue(int(lhs) % int(rhs)), true
} else {
return model.SampleValue(math.Inf(int(rhs))), true
}
case EQ:
if lhs == rhs {
return lhs, true
} else {
return 0, false
}
case NE:
if lhs != rhs {
return lhs, true
} else {
return 0, false
}
case GT:
if lhs > rhs {
return lhs, true
} else {
return 0, false
}
case LT:
if lhs < rhs {
return lhs, true
} else {
return 0, false
}
case GE:
if lhs >= rhs {
return lhs, true
} else {
return 0, false
}
case LE:
if lhs <= rhs {
return lhs, true
} else {
return 0, false
}
case AND:
return lhs, true
}
panic("Not all enum values enumerated in switch")
}
func labelsEqual(labels1, labels2 model.Metric) bool {
if len(labels1) != len(labels2) {
return false
}
for label, value := range labels1 {
if labels2[label] != value && label != "name" {
return false
}
}
return true
}
func (node *VectorArithExpr) Eval(timestamp *time.Time) Vector {
lhs := node.lhs.Eval(timestamp)
result := Vector{}
if node.rhs.Type() == SCALAR {
rhs := node.rhs.(ScalarNode).Eval(timestamp)
for _, lhsSample := range lhs {
value, keep := evalVectorBinop(node.opType, lhsSample.Value, rhs)
if keep {
lhsSample.Value = value
result = append(result, lhsSample)
}
}
return result
} else if node.rhs.Type() == VECTOR {
rhs := node.rhs.(VectorNode).Eval(timestamp)
for _, lhsSample := range lhs {
for _, rhsSample := range rhs {
if labelsEqual(lhsSample.Metric, rhsSample.Metric) {
value, keep := evalVectorBinop(node.opType, lhsSample.Value, rhsSample.Value)
if keep {
lhsSample.Value = value
result = append(result, lhsSample)
}
}
}
}
return result
}
panic("Invalid vector arithmetic expression operands")
}
func (node *MatrixLiteral) Eval(timestamp *time.Time) Matrix {
interval := &model.Interval{
OldestInclusive: timestamp.Add(-node.interval),
NewestInclusive: *timestamp,
}
values, err := persistence.GetRangeValues(node.labels, interval, &stalenessPolicy)
if err != nil {
log.Printf("Unable to get values for vector interval")
return Matrix{}
}
return values
}
func (node *MatrixLiteral) EvalBoundaries(timestamp *time.Time) Matrix {
interval := &model.Interval{
OldestInclusive: timestamp.Add(-node.interval),
NewestInclusive: *timestamp,
}
values, err := persistence.GetBoundaryValues(node.labels, interval, &stalenessPolicy)
if err != nil {
log.Printf("Unable to get boundary values for vector interval")
return Matrix{}
}
return values
}
func (node *StringLiteral) Eval(timestamp *time.Time) string {
return node.str
}
func (node *StringFunctionCall) Eval(timestamp *time.Time) string {
return node.function.callFn(timestamp, node.args).(string)
}
// ----------------------------------------------------------------------------
// Constructors.
func NewScalarLiteral(value model.SampleValue) *ScalarLiteral {
return &ScalarLiteral{
value: value,
}
}
func NewVectorLiteral(labels model.LabelSet) *VectorLiteral {
return &VectorLiteral{
labels: labels,
}
}
func NewVectorAggregation(aggrType AggrType, vector VectorNode, groupBy []model.LabelName) *VectorAggregation {
return &VectorAggregation{
aggrType: aggrType,
groupBy: groupBy,
vector: vector,
}
}
func NewFunctionCall(function *Function, args []Node) (Node, error) {
if err := function.CheckArgTypes(args); err != nil {
return nil, err
}
switch function.returnType {
case SCALAR:
return &ScalarFunctionCall{
function: function,
args: args,
}, nil
case VECTOR:
return &VectorFunctionCall{
function: function,
args: args,
}, nil
case STRING:
return &StringFunctionCall{
function: function,
args: args,
}, nil
}
panic("Function with invalid return type")
}
func nodesHaveTypes(nodes []Node, exprTypes []ExprType) bool {
for _, node := range nodes {
correctType := false
for _, exprType := range exprTypes {
if node.Type() == exprType {
correctType = true
}
}
if !correctType {
return false
}
}
return true
}
func NewArithExpr(opType BinOpType, lhs Node, rhs Node) (Node, error) {
if !nodesHaveTypes([]Node{lhs, rhs}, []ExprType{SCALAR, VECTOR}) {
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")
}
if opType == AND || opType == OR {
if lhs.Type() == SCALAR || rhs.Type() == SCALAR {
return nil, errors.New("AND and OR operators may only be used between vectors")
}
}
if lhs.Type() == VECTOR || rhs.Type() == VECTOR {
return &VectorArithExpr{
opType: opType,
lhs: lhs.(VectorNode),
rhs: rhs,
}, nil
}
return &ScalarArithExpr{
opType: opType,
lhs: lhs.(ScalarNode),
rhs: rhs.(ScalarNode),
}, nil
}
func NewMatrixLiteral(vector *VectorLiteral, interval time.Duration) *MatrixLiteral {
return &MatrixLiteral{
labels: vector.labels,
interval: interval,
}
}
func NewStringLiteral(str string) *StringLiteral {
return &StringLiteral{
str: str,
}
}