func BenchmarkEvalBool_OneOperatorValueChanges_ReferenceNodeInt64_NumberInt64(b *testing.B) { scope := stateful.NewScope() initialValue := int64(20) scope.Set("value", initialValue) b.ReportAllocs() b.ResetTimer() se, err := stateful.NewExpression(&ast.BinaryNode{ Operator: ast.TokenGreater, Left: &ast.ReferenceNode{ Reference: "value", }, Right: &ast.NumberNode{ IsInt: true, Int64: int64(10), }, }) if err != nil { b.Fatalf("Failed to compile the expression: %v", err) } // We have maximum value because we want to limit the maximum number in // the reference node so we don't get too much big numbers and the benchmark suite will increase our iterations number (b.N) currentValue := initialValue maximumValue := int64(40) var result bool for i := 0; i < b.N; i++ { b.StopTimer() currentValue += int64(1) if currentValue > maximumValue { currentValue = initialValue } scope.Set("value", currentValue) b.StartTimer() result, err := se.EvalBool(scope) if err != nil || !result { v, _ := scope.Get("value") b.Errorf("Failed to evaluate: error=%v, result=%t, value=%v, init=%v, maximum=%v", err, result, v, initialValue, maximumValue) } } evalBoolResult = result }
func benchmarkEvalBool(b *testing.B, scope *stateful.Scope, node ast.Node) { b.ReportAllocs() b.ResetTimer() var err error se, err := stateful.NewExpression(node) if err != nil { b.Fatalf("Failed to compile the expression: %v", err) } for i := 0; i < b.N; i++ { evalBoolResult, err = se.EvalBool(scope) if err != nil { b.Fatal(err) } } }
// Create a new CombineNode, which combines a stream with itself dynamically. func newCombineNode(et *ExecutingTask, n *pipeline.CombineNode, l *log.Logger) (*CombineNode, error) { cn := &CombineNode{ c: n, node: node{Node: n, et: et, logger: l}, expressionsByGroup: make(map[models.GroupID][]stateful.Expression), combination: combination{max: n.Max}, } // Create stateful expressions cn.expressions = make([]stateful.Expression, len(n.Lambdas)) cn.scopePools = make([]stateful.ScopePool, len(n.Lambdas)) for i, lambda := range n.Lambdas { statefulExpr, err := stateful.NewExpression(lambda.Expression) if err != nil { return nil, fmt.Errorf("Failed to compile %v expression: %v", i, err) } cn.expressions[i] = statefulExpr cn.scopePools[i] = stateful.NewScopePool(stateful.FindReferenceVariables(lambda.Expression)) } cn.node.runF = cn.runCombine return cn, nil }
func (k *K8sAutoscaleNode) evalExpr( current int, group models.GroupID, lambda *ast.LambdaNode, expressionsMap map[models.GroupID]stateful.Expression, pool stateful.ScopePool, t time.Time, fields models.Fields, tags models.Tags, ) (int, error) { expr, ok := expressionsMap[group] if !ok { var err error expr, err = stateful.NewExpression(lambda.Expression) if err != nil { return 0, err } expressionsMap[group] = expr } i, err := k.evalInt(int64(current), expr, pool, t, fields, tags) return int(i), err }
// Create a new FromNode which filters data from a source. func newFromNode(et *ExecutingTask, n *pipeline.FromNode, l *log.Logger) (*FromNode, error) { sn := &FromNode{ node: node{Node: n, et: et, logger: l}, s: n, db: n.Database, rp: n.RetentionPolicy, name: n.Measurement, } sn.node.runF = sn.runStream sn.allDimensions, sn.dimensions = determineDimensions(n.Dimensions) if n.Lambda != nil { expr, err := stateful.NewExpression(n.Lambda.Expression) if err != nil { return nil, fmt.Errorf("Failed to compile from expression: %v", err) } sn.expression = expr sn.scopePool = stateful.NewScopePool(stateful.FindReferenceVariables(n.Lambda.Expression)) } return sn, nil }
// Create a new EvalNode which applies a transformation func to each point in a stream and returns a single point. func newEvalNode(et *ExecutingTask, n *pipeline.EvalNode, l *log.Logger) (*EvalNode, error) { if len(n.AsList) != len(n.Lambdas) { return nil, errors.New("must provide one name per expression via the 'As' property") } en := &EvalNode{ node: node{Node: n, et: et, logger: l}, e: n, expressionsByGroup: make(map[models.GroupID][]stateful.Expression), } // Create stateful expressions en.expressions = make([]stateful.Expression, len(n.Lambdas)) en.refVarList = make([][]string, len(n.Lambdas)) expressions := make([]ast.Node, len(n.Lambdas)) for i, lambda := range n.Lambdas { expressions[i] = lambda.Expression statefulExpr, err := stateful.NewExpression(lambda.Expression) if err != nil { return nil, fmt.Errorf("Failed to compile %v expression: %v", i, err) } en.expressions[i] = statefulExpr refVars := stateful.FindReferenceVariables(lambda.Expression) en.refVarList[i] = refVars } // Create a single pool for the combination of all expressions en.scopePool = stateful.NewScopePool(stateful.FindReferenceVariables(expressions...)) // Create map of tags if l := len(n.TagsList); l > 0 { en.tags = make(map[string]bool, l) for _, tag := range n.TagsList { en.tags[tag] = true } } en.node.runF = en.runEval return en, nil }
func (w *WhereNode) runWhere(snapshot []byte) error { switch w.Wants() { case pipeline.StreamEdge: for p, ok := w.ins[0].NextPoint(); ok; p, ok = w.ins[0].NextPoint() { w.timer.Start() expr := w.expressions[p.Group] scopePool := w.scopePools[p.Group] if expr == nil { compiledExpr, err := stateful.NewExpression(w.w.Lambda.Expression) if err != nil { return fmt.Errorf("Failed to compile expression in where clause: %v", err) } expr = compiledExpr w.expressions[p.Group] = expr scopePool = stateful.NewScopePool(stateful.FindReferenceVariables(w.w.Lambda.Expression)) w.scopePools[p.Group] = scopePool } if pass, err := EvalPredicate(expr, scopePool, p.Time, p.Fields, p.Tags); pass { w.timer.Pause() for _, child := range w.outs { err := child.CollectPoint(p) if err != nil { return err } } w.timer.Resume() } else if err != nil { w.logger.Println("E! error while evaluating expression:", err) } w.timer.Stop() } case pipeline.BatchEdge: for b, ok := w.ins[0].NextBatch(); ok; b, ok = w.ins[0].NextBatch() { w.timer.Start() expr := w.expressions[b.Group] scopePool := w.scopePools[b.Group] if expr == nil { compiledExpr, err := stateful.NewExpression(w.w.Lambda.Expression) if err != nil { return fmt.Errorf("Failed to compile expression in where clause: %v", err) } expr = compiledExpr w.expressions[b.Group] = expr scopePool = stateful.NewScopePool(stateful.FindReferenceVariables(w.w.Lambda.Expression)) w.scopePools[b.Group] = scopePool } points := b.Points b.Points = make([]models.BatchPoint, 0, len(b.Points)) for _, p := range points { if pass, err := EvalPredicate(expr, scopePool, p.Time, p.Fields, p.Tags); pass { if err != nil { w.logger.Println("E! error while evaluating WHERE expression:", err) } b.Points = append(b.Points, p) } } w.timer.Stop() for _, child := range w.outs { err := child.CollectBatch(b) if err != nil { return err } } } } return nil }
// Create a new AlertNode which caches the most recent item and exposes it over the HTTP API. func newAlertNode(et *ExecutingTask, n *pipeline.AlertNode, l *log.Logger) (an *AlertNode, err error) { an = &AlertNode{ node: node{Node: n, et: et, logger: l}, a: n, } an.node.runF = an.runAlert // Create buffer pool for the templates an.bufPool = sync.Pool{ New: func() interface{} { return new(bytes.Buffer) }, } // Parse templates an.idTmpl, err = text.New("id").Parse(n.Id) if err != nil { return nil, err } an.messageTmpl, err = text.New("message").Parse(n.Message) if err != nil { return nil, err } an.detailsTmpl, err = html.New("details").Funcs(html.FuncMap{ "json": func(v interface{}) html.JS { tmpBuffer := an.bufPool.Get().(*bytes.Buffer) defer func() { tmpBuffer.Reset() an.bufPool.Put(tmpBuffer) }() json.NewEncoder(tmpBuffer).Encode(v) return html.JS(tmpBuffer.String()) }, }).Parse(n.Details) if err != nil { return nil, err } // Construct alert handlers an.handlers = make([]AlertHandler, 0) for _, post := range n.PostHandlers { post := post an.handlers = append(an.handlers, func(ad *AlertData) { an.handlePost(post, ad) }) } for _, email := range n.EmailHandlers { email := email an.handlers = append(an.handlers, func(ad *AlertData) { an.handleEmail(email, ad) }) } if len(n.EmailHandlers) == 0 && (et.tm.SMTPService != nil && et.tm.SMTPService.Global()) { an.handlers = append(an.handlers, func(ad *AlertData) { an.handleEmail(&pipeline.EmailHandler{}, ad) }) } // If email has been configured with state changes only set it. if et.tm.SMTPService != nil && et.tm.SMTPService.Global() && et.tm.SMTPService.StateChangesOnly() { n.IsStateChangesOnly = true } for _, exec := range n.ExecHandlers { exec := exec an.handlers = append(an.handlers, func(ad *AlertData) { an.handleExec(exec, ad) }) } for _, log := range n.LogHandlers { log := log if !filepath.IsAbs(log.FilePath) { return nil, fmt.Errorf("alert log path must be absolute: %s is not absolute", log.FilePath) } an.handlers = append(an.handlers, func(ad *AlertData) { an.handleLog(log, ad) }) } for _, vo := range n.VictorOpsHandlers { vo := vo an.handlers = append(an.handlers, func(ad *AlertData) { an.handleVictorOps(vo, ad) }) } if len(n.VictorOpsHandlers) == 0 && (et.tm.VictorOpsService != nil && et.tm.VictorOpsService.Global()) { an.handlers = append(an.handlers, func(ad *AlertData) { an.handleVictorOps(&pipeline.VictorOpsHandler{}, ad) }) } for _, pd := range n.PagerDutyHandlers { pd := pd an.handlers = append(an.handlers, func(ad *AlertData) { an.handlePagerDuty(pd, ad) }) } if len(n.PagerDutyHandlers) == 0 && (et.tm.PagerDutyService != nil && et.tm.PagerDutyService.Global()) { an.handlers = append(an.handlers, func(ad *AlertData) { an.handlePagerDuty(&pipeline.PagerDutyHandler{}, ad) }) } for _, sensu := range n.SensuHandlers { sensu := sensu an.handlers = append(an.handlers, func(ad *AlertData) { an.handleSensu(sensu, ad) }) } for _, slack := range n.SlackHandlers { slack := slack an.handlers = append(an.handlers, func(ad *AlertData) { an.handleSlack(slack, ad) }) } if len(n.SlackHandlers) == 0 && (et.tm.SlackService != nil && et.tm.SlackService.Global()) { an.handlers = append(an.handlers, func(ad *AlertData) { an.handleSlack(&pipeline.SlackHandler{}, ad) }) } // If slack has been configured with state changes only set it. if et.tm.SlackService != nil && et.tm.SlackService.Global() && et.tm.SlackService.StateChangesOnly() { n.IsStateChangesOnly = true } for _, telegram := range n.TelegramHandlers { telegram := telegram an.handlers = append(an.handlers, func(ad *AlertData) { an.handleTelegram(telegram, ad) }) } if len(n.TelegramHandlers) == 0 && (et.tm.TelegramService != nil && et.tm.TelegramService.Global()) { an.handlers = append(an.handlers, func(ad *AlertData) { an.handleTelegram(&pipeline.TelegramHandler{}, ad) }) } // If telegram has been configured with state changes only set it. if et.tm.TelegramService != nil && et.tm.TelegramService.Global() && et.tm.TelegramService.StateChangesOnly() { n.IsStateChangesOnly = true } for _, hipchat := range n.HipChatHandlers { hipchat := hipchat an.handlers = append(an.handlers, func(ad *AlertData) { an.handleHipChat(hipchat, ad) }) } if len(n.HipChatHandlers) == 0 && (et.tm.HipChatService != nil && et.tm.HipChatService.Global()) { an.handlers = append(an.handlers, func(ad *AlertData) { an.handleHipChat(&pipeline.HipChatHandler{}, ad) }) } // If HipChat has been configured with state changes only set it. if et.tm.HipChatService != nil && et.tm.HipChatService.Global() && et.tm.HipChatService.StateChangesOnly() { n.IsStateChangesOnly = true } for _, alerta := range n.AlertaHandlers { // Validate alerta templates rtmpl, err := text.New("resource").Parse(alerta.Resource) if err != nil { return nil, err } evtmpl, err := text.New("event").Parse(alerta.Event) if err != nil { return nil, err } etmpl, err := text.New("environment").Parse(alerta.Environment) if err != nil { return nil, err } gtmpl, err := text.New("group").Parse(alerta.Group) if err != nil { return nil, err } vtmpl, err := text.New("value").Parse(alerta.Value) if err != nil { return nil, err } ai := alertaHandler{ AlertaHandler: alerta, resourceTmpl: rtmpl, eventTmpl: evtmpl, environmentTmpl: etmpl, groupTmpl: gtmpl, valueTmpl: vtmpl, } an.handlers = append(an.handlers, func(ad *AlertData) { an.handleAlerta(ai, ad) }) } for _, og := range n.OpsGenieHandlers { og := og an.handlers = append(an.handlers, func(ad *AlertData) { an.handleOpsGenie(og, ad) }) } if len(n.OpsGenieHandlers) == 0 && (et.tm.OpsGenieService != nil && et.tm.OpsGenieService.Global()) { an.handlers = append(an.handlers, func(ad *AlertData) { an.handleOpsGenie(&pipeline.OpsGenieHandler{}, ad) }) } for _, talk := range n.TalkHandlers { talk := talk an.handlers = append(an.handlers, func(ad *AlertData) { an.handleTalk(talk, ad) }) } // Parse level expressions an.levels = make([]stateful.Expression, CritAlert+1) an.scopePools = make([]stateful.ScopePool, CritAlert+1) if n.Info != nil { statefulExpression, expressionCompileError := stateful.NewExpression(n.Info.Expression) if expressionCompileError != nil { return nil, fmt.Errorf("Failed to compile stateful expression for info: %s", expressionCompileError) } an.levels[InfoAlert] = statefulExpression an.scopePools[InfoAlert] = stateful.NewScopePool(stateful.FindReferenceVariables(n.Info.Expression)) } if n.Warn != nil { statefulExpression, expressionCompileError := stateful.NewExpression(n.Warn.Expression) if expressionCompileError != nil { return nil, fmt.Errorf("Failed to compile stateful expression for warn: %s", expressionCompileError) } an.levels[WarnAlert] = statefulExpression an.scopePools[WarnAlert] = stateful.NewScopePool(stateful.FindReferenceVariables(n.Warn.Expression)) } if n.Crit != nil { statefulExpression, expressionCompileError := stateful.NewExpression(n.Crit.Expression) if expressionCompileError != nil { return nil, fmt.Errorf("Failed to compile stateful expression for crit: %s", expressionCompileError) } an.levels[CritAlert] = statefulExpression an.scopePools[CritAlert] = stateful.NewScopePool(stateful.FindReferenceVariables(n.Crit.Expression)) } // Setup states if n.History < 2 { n.History = 2 } an.states = make(map[models.GroupID]*alertState) // Configure flapping if n.UseFlapping { if n.FlapLow > 1 || n.FlapHigh > 1 { return nil, errors.New("alert flap thresholds are percentages and should be between 0 and 1") } } return }
// Evaluate a node using a stack machine in a given scope func eval(n ast.Node, scope *stateful.Scope, stck *stack, predefinedVars, defaultVars map[string]Var, ignoreMissingVars bool) (err error) { switch node := n.(type) { case *ast.BoolNode: stck.Push(node.Bool) case *ast.NumberNode: if node.IsInt { stck.Push(node.Int64) } else { stck.Push(node.Float64) } case *ast.DurationNode: stck.Push(node.Dur) case *ast.StringNode: stck.Push(node.Literal) case *ast.RegexNode: stck.Push(node.Regex) case *ast.UnaryNode: err = eval(node.Node, scope, stck, predefinedVars, defaultVars, ignoreMissingVars) if err != nil { return } err = evalUnary(node, node.Operator, scope, stck) if err != nil { return } case *ast.BinaryNode: // Switch over to using the stateful expressions for evaluating a BinaryNode n, err := resolveIdents(node, scope) if err != nil { return err } expr, err := stateful.NewExpression(n) if err != nil { return err } value, err := expr.Eval(stateful.NewScope()) if err != nil { return err } stck.Push(value) case *ast.LambdaNode: node.Expression, err = resolveIdents(node.Expression, scope) if err != nil { return } stck.Push(node) case *ast.ListNode: nodes := make([]interface{}, len(node.Nodes)) for i, n := range node.Nodes { err = eval(n, scope, stck, predefinedVars, defaultVars, ignoreMissingVars) if err != nil { return } a := stck.Pop() switch typed := a.(type) { case *ast.IdentifierNode: // Resolve identifier a, err = scope.Get(typed.Ident) if err != nil { return err } } nodes[i] = a } stck.Push(nodes) case *ast.TypeDeclarationNode: err = evalTypeDeclaration(node, scope, predefinedVars, defaultVars, ignoreMissingVars) if err != nil { return } case *ast.DeclarationNode: err = eval(node.Right, scope, stck, predefinedVars, defaultVars, ignoreMissingVars) if err != nil { return } err = evalDeclaration(node, scope, stck, predefinedVars, defaultVars) if err != nil { return } case *ast.ChainNode: err = eval(node.Left, scope, stck, predefinedVars, defaultVars, ignoreMissingVars) if err != nil { return } err = eval(node.Right, scope, stck, predefinedVars, defaultVars, ignoreMissingVars) if err != nil { return } err = evalChain(node, scope, stck) if err != nil { return } case *ast.FunctionNode: args := make([]interface{}, len(node.Args)) for i, arg := range node.Args { err = eval(arg, scope, stck, predefinedVars, defaultVars, ignoreMissingVars) if err != nil { return } a := stck.Pop() switch typed := a.(type) { case *ast.IdentifierNode: // Resolve identifier a, err = scope.Get(typed.Ident) if err != nil { return err } case unboundFunc: // Call global func a, err = typed(nil) if err != nil { return err } } args[i] = a } err = evalFunc(node, scope, stck, args) if err != nil { return } case *ast.ProgramNode: for _, n := range node.Nodes { err = eval(n, scope, stck, predefinedVars, defaultVars, ignoreMissingVars) if err != nil { return } // Pop unused result if stck.Len() > 0 { ret := stck.Pop() if f, ok := ret.(unboundFunc); ok { // Call global function _, err := f(nil) if err != nil { return err } } } } default: stck.Push(node) } return nil }