Introduce TSDB service (#31520)
* Introduce TSDB service Signed-off-by: Arve Knudsen <arve.knudsen@gmail.com> Co-authored-by: Erik Sundell <erik.sundell87@gmail.com> Co-authored-by: Will Browne <will.browne@grafana.com> Co-authored-by: Torkel Ödegaard <torkel@grafana.org> Co-authored-by: Will Browne <wbrowne@users.noreply.github.com> Co-authored-by: Zoltán Bedi <zoltan.bedi@gmail.com>
This commit is contained in:
co-authored by
Erik Sundell
Will Browne
Torkel Ödegaard
Will Browne
Zoltán Bedi
parent
c899bf3592
commit
b79e61656a
@@ -9,6 +9,7 @@ import (
|
||||
"github.com/grafana/grafana/pkg/api/routing"
|
||||
"github.com/grafana/grafana/pkg/middleware"
|
||||
"github.com/grafana/grafana/pkg/models"
|
||||
"github.com/grafana/grafana/pkg/plugins"
|
||||
"github.com/grafana/grafana/pkg/services/datasources"
|
||||
"github.com/grafana/grafana/pkg/services/ngalert/eval"
|
||||
"github.com/grafana/grafana/pkg/setting"
|
||||
@@ -20,6 +21,7 @@ type apiImpl struct {
|
||||
Cfg *setting.Cfg `inject:""`
|
||||
DatasourceCache datasources.CacheService `inject:""`
|
||||
RouteRegister routing.RouteRegister `inject:""`
|
||||
DataService *tsdb.Service
|
||||
schedule scheduleService
|
||||
store store
|
||||
}
|
||||
@@ -64,13 +66,13 @@ func (api *apiImpl) conditionEvalEndpoint(c *models.ReqContext, cmd evalAlertCon
|
||||
}
|
||||
|
||||
evaluator := eval.Evaluator{Cfg: api.Cfg}
|
||||
evalResults, err := evaluator.ConditionEval(&evalCond, timeNow())
|
||||
evalResults, err := evaluator.ConditionEval(&evalCond, timeNow(), api.DataService)
|
||||
if err != nil {
|
||||
return response.Error(400, "Failed to evaluate conditions", err)
|
||||
}
|
||||
|
||||
frame := evalResults.AsDataFrame()
|
||||
df := tsdb.NewDecodedDataFrames([]*data.Frame{&frame})
|
||||
df := plugins.NewDecodedDataFrames([]*data.Frame{&frame})
|
||||
instances, err := df.Encoded()
|
||||
if err != nil {
|
||||
return response.Error(400, "Failed to encode result dataframes", err)
|
||||
@@ -95,13 +97,13 @@ func (api *apiImpl) alertDefinitionEvalEndpoint(c *models.ReqContext) response.R
|
||||
}
|
||||
|
||||
evaluator := eval.Evaluator{Cfg: api.Cfg}
|
||||
evalResults, err := evaluator.ConditionEval(condition, timeNow())
|
||||
evalResults, err := evaluator.ConditionEval(condition, timeNow(), api.DataService)
|
||||
if err != nil {
|
||||
return response.Error(400, "Failed to evaluate alert", err)
|
||||
}
|
||||
frame := evalResults.AsDataFrame()
|
||||
|
||||
df := tsdb.NewDecodedDataFrames([]*data.Frame{&frame})
|
||||
df := plugins.NewDecodedDataFrames([]*data.Frame{&frame})
|
||||
if err != nil {
|
||||
return response.Error(400, "Failed to instantiate Dataframes from the decoded frames", err)
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/grafana/grafana/pkg/setting"
|
||||
"github.com/grafana/grafana/pkg/tsdb"
|
||||
|
||||
"github.com/grafana/grafana-plugin-sdk-go/backend"
|
||||
"github.com/grafana/grafana-plugin-sdk-go/data"
|
||||
@@ -100,7 +101,7 @@ type AlertExecCtx struct {
|
||||
}
|
||||
|
||||
// execute runs the Condition's expressions or queries.
|
||||
func (c *Condition) execute(ctx AlertExecCtx, now time.Time) (*ExecutionResults, error) {
|
||||
func (c *Condition) execute(ctx AlertExecCtx, now time.Time, dataService *tsdb.Service) (*ExecutionResults, error) {
|
||||
result := ExecutionResults{}
|
||||
if !c.IsValid() {
|
||||
return nil, fmt.Errorf("invalid conditions")
|
||||
@@ -140,7 +141,10 @@ func (c *Condition) execute(ctx AlertExecCtx, now time.Time) (*ExecutionResults,
|
||||
})
|
||||
}
|
||||
|
||||
exprService := expr.Service{Cfg: &setting.Cfg{ExpressionsEnabled: ctx.ExpressionsEnabled}}
|
||||
exprService := expr.Service{
|
||||
Cfg: &setting.Cfg{ExpressionsEnabled: ctx.ExpressionsEnabled},
|
||||
DataService: dataService,
|
||||
}
|
||||
pbRes, err := exprService.TransformData(ctx.Ctx, queryDataReq)
|
||||
if err != nil {
|
||||
return &result, err
|
||||
@@ -218,13 +222,13 @@ func (evalResults Results) AsDataFrame() data.Frame {
|
||||
}
|
||||
|
||||
// ConditionEval executes conditions and evaluates the result.
|
||||
func (e *Evaluator) ConditionEval(condition *Condition, now time.Time) (Results, error) {
|
||||
func (e *Evaluator) ConditionEval(condition *Condition, now time.Time, dataService *tsdb.Service) (Results, error) {
|
||||
alertCtx, cancelFn := context.WithTimeout(context.Background(), alertingEvaluationTimeout)
|
||||
defer cancelFn()
|
||||
|
||||
alertExecCtx := AlertExecCtx{OrgID: condition.OrgID, Ctx: alertCtx, ExpressionsEnabled: e.Cfg.ExpressionsEnabled}
|
||||
|
||||
execResult, err := condition.execute(alertExecCtx, now)
|
||||
execResult, err := condition.execute(alertExecCtx, now, dataService)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to execute conditions: %w", err)
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ import (
|
||||
"github.com/benbjohnson/clock"
|
||||
"github.com/grafana/grafana/pkg/services/ngalert/eval"
|
||||
"github.com/grafana/grafana/pkg/services/sqlstore"
|
||||
"github.com/grafana/grafana/pkg/tsdb"
|
||||
|
||||
"github.com/grafana/grafana/pkg/api/routing"
|
||||
"github.com/grafana/grafana/pkg/infra/log"
|
||||
@@ -34,6 +35,7 @@ type AlertNG struct {
|
||||
DatasourceCache datasources.CacheService `inject:""`
|
||||
RouteRegister routing.RouteRegister `inject:""`
|
||||
SQLStore *sqlstore.SQLStore `inject:""`
|
||||
DataService *tsdb.Service `inject:""`
|
||||
log log.Logger
|
||||
schedule scheduleService
|
||||
}
|
||||
@@ -57,14 +59,16 @@ func (ng *AlertNG) Init() error {
|
||||
evaluator: eval.Evaluator{Cfg: ng.Cfg},
|
||||
store: store,
|
||||
}
|
||||
ng.schedule = newScheduler(schedCfg)
|
||||
ng.schedule = newScheduler(schedCfg, ng.DataService)
|
||||
|
||||
api := apiImpl{
|
||||
Cfg: ng.Cfg,
|
||||
DatasourceCache: ng.DatasourceCache,
|
||||
RouteRegister: ng.RouteRegister,
|
||||
DataService: ng.DataService,
|
||||
schedule: ng.schedule,
|
||||
store: store}
|
||||
store: store,
|
||||
}
|
||||
api.registerAPIEndpoints()
|
||||
|
||||
return nil
|
||||
|
||||
@@ -10,6 +10,7 @@ import (
|
||||
"github.com/grafana/grafana/pkg/infra/log"
|
||||
"github.com/grafana/grafana/pkg/services/alerting"
|
||||
"github.com/grafana/grafana/pkg/services/ngalert/eval"
|
||||
"github.com/grafana/grafana/pkg/tsdb"
|
||||
"golang.org/x/sync/errgroup"
|
||||
)
|
||||
|
||||
@@ -24,7 +25,8 @@ type scheduleService interface {
|
||||
overrideCfg(cfg schedulerCfg)
|
||||
}
|
||||
|
||||
func (sch *schedule) definitionRoutine(grafanaCtx context.Context, key alertDefinitionKey, evalCh <-chan *evalContext, stopCh <-chan struct{}) error {
|
||||
func (sch *schedule) definitionRoutine(grafanaCtx context.Context, key alertDefinitionKey,
|
||||
evalCh <-chan *evalContext, stopCh <-chan struct{}) error {
|
||||
sch.log.Debug("alert definition routine started", "key", key)
|
||||
|
||||
evalRunning := false
|
||||
@@ -58,11 +60,12 @@ func (sch *schedule) definitionRoutine(grafanaCtx context.Context, key alertDefi
|
||||
OrgID: alertDefinition.OrgID,
|
||||
QueriesAndExpressions: alertDefinition.Data,
|
||||
}
|
||||
results, err := sch.evaluator.ConditionEval(&condition, ctx.now)
|
||||
results, err := sch.evaluator.ConditionEval(&condition, ctx.now, sch.dataService)
|
||||
end = timeNow()
|
||||
if err != nil {
|
||||
// consider saving alert instance on error
|
||||
sch.log.Error("failed to evaluate alert definition", "title", alertDefinition.Title, "key", key, "attempt", attempt, "now", ctx.now, "duration", end.Sub(start), "error", err)
|
||||
sch.log.Error("failed to evaluate alert definition", "title", alertDefinition.Title,
|
||||
"key", key, "attempt", attempt, "now", ctx.now, "duration", end.Sub(start), "error", err)
|
||||
return err
|
||||
}
|
||||
for _, r := range results {
|
||||
@@ -129,6 +132,8 @@ type schedule struct {
|
||||
evaluator eval.Evaluator
|
||||
|
||||
store store
|
||||
|
||||
dataService *tsdb.Service
|
||||
}
|
||||
|
||||
type schedulerCfg struct {
|
||||
@@ -142,7 +147,7 @@ type schedulerCfg struct {
|
||||
}
|
||||
|
||||
// newScheduler returns a new schedule.
|
||||
func newScheduler(cfg schedulerCfg) *schedule {
|
||||
func newScheduler(cfg schedulerCfg, dataService *tsdb.Service) *schedule {
|
||||
ticker := alerting.NewTicker(cfg.c.Now(), time.Second*0, cfg.c, int64(cfg.baseInterval.Seconds()))
|
||||
sch := schedule{
|
||||
registry: alertDefinitionRegistry{alertDefinitionInfo: make(map[alertDefinitionKey]alertDefinitionInfo)},
|
||||
@@ -155,6 +160,7 @@ func newScheduler(cfg schedulerCfg) *schedule {
|
||||
stopAppliedFunc: cfg.stopAppliedFunc,
|
||||
evaluator: cfg.evaluator,
|
||||
store: cfg.store,
|
||||
dataService: dataService,
|
||||
}
|
||||
return &sch
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user