2020-09-13 15:07:59 +00:00
|
|
|
// Copyright 2020 The Prometheus Authors
|
|
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
// you may not use this file except in compliance with the License.
|
|
|
|
// You may obtain a copy of the License at
|
|
|
|
//
|
|
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
//
|
|
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
// See the License for the specific language governing permissions and
|
|
|
|
// limitations under the License.
|
|
|
|
|
|
|
|
package main
|
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
|
|
|
"fmt"
|
|
|
|
"os"
|
|
|
|
"time"
|
|
|
|
|
|
|
|
"github.com/go-kit/kit/log"
|
2020-11-22 22:24:38 +00:00
|
|
|
"github.com/go-kit/kit/log/level"
|
2020-09-13 15:07:59 +00:00
|
|
|
"github.com/pkg/errors"
|
|
|
|
v1 "github.com/prometheus/client_golang/api/prometheus/v1"
|
|
|
|
"github.com/prometheus/common/model"
|
|
|
|
"github.com/prometheus/prometheus/pkg/labels"
|
|
|
|
"github.com/prometheus/prometheus/rules"
|
2020-11-22 22:24:38 +00:00
|
|
|
"github.com/prometheus/prometheus/storage"
|
2020-10-17 15:36:58 +00:00
|
|
|
"github.com/prometheus/prometheus/tsdb"
|
2020-09-13 15:07:59 +00:00
|
|
|
)
|
|
|
|
|
2020-11-26 16:30:06 +00:00
|
|
|
const maxSamplesInMemory = 5000
|
|
|
|
|
|
|
|
type queryRangeAPI interface {
|
|
|
|
QueryRange(ctx context.Context, query string, r v1.Range) (model.Value, v1.Warnings, error)
|
|
|
|
}
|
|
|
|
|
2020-10-31 13:40:24 +00:00
|
|
|
// ruleImporter is the importer to backfill rules.
|
|
|
|
type ruleImporter struct {
|
2020-09-13 15:07:59 +00:00
|
|
|
logger log.Logger
|
2020-10-31 13:40:24 +00:00
|
|
|
config ruleImporterConfig
|
2020-09-13 15:07:59 +00:00
|
|
|
|
2020-11-26 16:30:06 +00:00
|
|
|
apiClient queryRangeAPI
|
2020-09-13 15:07:59 +00:00
|
|
|
|
2020-11-26 16:30:06 +00:00
|
|
|
appender *multipleAppender
|
2020-09-13 15:07:59 +00:00
|
|
|
|
2020-11-26 16:30:06 +00:00
|
|
|
groups map[string]*rules.Group
|
|
|
|
ruleManager *rules.Manager
|
2020-09-13 15:07:59 +00:00
|
|
|
}
|
|
|
|
|
2020-10-31 13:40:24 +00:00
|
|
|
// ruleImporterConfig is the config for the rule importer.
|
|
|
|
type ruleImporterConfig struct {
|
2020-09-13 15:07:59 +00:00
|
|
|
Start time.Time
|
|
|
|
End time.Time
|
|
|
|
EvalInterval time.Duration
|
|
|
|
}
|
|
|
|
|
2020-10-31 13:40:24 +00:00
|
|
|
// newRuleImporter creates a new rule importer that can be used to backfill rules.
|
2020-11-26 16:30:06 +00:00
|
|
|
func newRuleImporter(logger log.Logger, config ruleImporterConfig, apiClient queryRangeAPI, appender *multipleAppender) *ruleImporter {
|
2020-10-31 13:40:24 +00:00
|
|
|
return &ruleImporter{
|
2020-11-22 22:24:38 +00:00
|
|
|
logger: logger,
|
2020-09-13 15:07:59 +00:00
|
|
|
config: config,
|
2020-11-26 16:30:06 +00:00
|
|
|
apiClient: apiClient,
|
|
|
|
appender: appender,
|
|
|
|
ruleManager: rules.NewManager(&rules.ManagerOptions{}),
|
2020-09-13 15:07:59 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-11-26 16:30:06 +00:00
|
|
|
// loadGroups parses groups from a list of rule files.
|
2020-10-31 13:40:24 +00:00
|
|
|
func (importer *ruleImporter) loadGroups(ctx context.Context, filenames []string) (errs []error) {
|
2020-11-26 16:30:06 +00:00
|
|
|
groups, errs := importer.ruleManager.LoadGroups(importer.config.EvalInterval, labels.Labels{}, filenames...)
|
|
|
|
if errs != nil {
|
|
|
|
return errs
|
2020-09-13 15:07:59 +00:00
|
|
|
}
|
|
|
|
importer.groups = groups
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2020-10-31 13:40:24 +00:00
|
|
|
// importAll evaluates all the groups and rules and creates new time series
|
2020-09-13 15:07:59 +00:00
|
|
|
// and stores them in new blocks.
|
2020-11-26 16:30:06 +00:00
|
|
|
func (importer *ruleImporter) importAll(ctx context.Context) (errs []error) {
|
2020-11-22 22:24:38 +00:00
|
|
|
for name, group := range importer.groups {
|
|
|
|
level.Info(importer.logger).Log("backfiller", fmt.Sprintf("processing group, name: %s", name))
|
2020-09-13 15:07:59 +00:00
|
|
|
stimeWithAlignment := group.EvalTimestamp(importer.config.Start.UnixNano())
|
|
|
|
|
2020-11-26 16:30:06 +00:00
|
|
|
for stimeWithAlignment.Before(importer.config.End) {
|
|
|
|
|
|
|
|
currentBlockEnd := stimeWithAlignment.Add(time.Duration(tsdb.DefaultBlockDuration) * time.Millisecond)
|
2020-11-22 22:24:38 +00:00
|
|
|
if currentBlockEnd.After(importer.config.End) {
|
|
|
|
currentBlockEnd = importer.config.End
|
|
|
|
}
|
|
|
|
|
|
|
|
for i, r := range group.Rules() {
|
|
|
|
level.Info(importer.logger).Log("backfiller", fmt.Sprintf("processing rule %d, name: %s", i+1, r.Name()))
|
2020-11-26 16:30:06 +00:00
|
|
|
if err := importer.importRule(ctx, r.Query().String(), r.Name(), r.Labels(), stimeWithAlignment, currentBlockEnd); err != nil {
|
2020-11-22 22:24:38 +00:00
|
|
|
errs = append(errs, err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-11-26 16:30:06 +00:00
|
|
|
stimeWithAlignment = currentBlockEnd
|
2020-09-13 15:07:59 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return errs
|
|
|
|
}
|
|
|
|
|
2020-10-31 13:40:24 +00:00
|
|
|
// importRule imports the historical data for a single rule.
|
2020-11-26 16:30:06 +00:00
|
|
|
func (importer *ruleImporter) importRule(ctx context.Context, ruleExpr, ruleName string, ruleLabels labels.Labels, start, end time.Time) error {
|
2020-11-22 22:24:38 +00:00
|
|
|
val, warnings, err := importer.apiClient.QueryRange(ctx,
|
|
|
|
ruleExpr,
|
|
|
|
v1.Range{
|
|
|
|
Start: start,
|
|
|
|
End: end,
|
2020-11-26 16:30:06 +00:00
|
|
|
Step: importer.config.EvalInterval, // todo: did we check if the rule has an interval?
|
2020-11-22 22:24:38 +00:00
|
|
|
},
|
|
|
|
)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
if warnings != nil {
|
|
|
|
fmt.Fprint(os.Stderr, "warning api.QueryRange:", warnings)
|
|
|
|
}
|
2020-09-13 15:07:59 +00:00
|
|
|
|
2020-11-22 22:24:38 +00:00
|
|
|
var matrix model.Matrix
|
|
|
|
switch val.Type() {
|
|
|
|
case model.ValMatrix:
|
|
|
|
matrix = val.(model.Matrix)
|
|
|
|
for _, sample := range matrix {
|
2020-09-13 15:07:59 +00:00
|
|
|
|
2020-11-26 16:30:06 +00:00
|
|
|
currentLabels := make(labels.Labels, 0, len(sample.Metric)+len(ruleLabels)+1)
|
2020-11-22 22:24:38 +00:00
|
|
|
currentLabels = append(currentLabels, labels.Label{
|
|
|
|
Name: labels.MetricName,
|
|
|
|
Value: ruleName,
|
|
|
|
})
|
|
|
|
for _, ruleLabel := range ruleLabels {
|
|
|
|
currentLabels = append(currentLabels, ruleLabel)
|
|
|
|
}
|
|
|
|
for k, v := range sample.Metric {
|
|
|
|
currentLabels = append(currentLabels, labels.Label{
|
|
|
|
Name: string(k),
|
|
|
|
Value: string(v),
|
|
|
|
})
|
|
|
|
}
|
|
|
|
for _, value := range sample.Values {
|
2020-11-26 16:30:06 +00:00
|
|
|
if err := importer.appender.add(ctx, currentLabels, value.Timestamp.Unix(), float64(value.Value)); err != nil {
|
2020-11-22 22:24:38 +00:00
|
|
|
return err
|
2020-09-13 15:07:59 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2020-11-22 22:24:38 +00:00
|
|
|
default:
|
2020-11-26 16:30:06 +00:00
|
|
|
return errors.New(fmt.Sprintf("rule result is wrong type %s", val.Type().String()))
|
2020-09-13 15:07:59 +00:00
|
|
|
}
|
2020-11-22 22:24:38 +00:00
|
|
|
return nil
|
2020-09-13 15:07:59 +00:00
|
|
|
}
|
2020-11-26 16:30:06 +00:00
|
|
|
|
|
|
|
// multipleAppender keeps track of how many series have been added to the current appender.
|
|
|
|
// If the max samples have been added, then all series are flushed to disk and commited and a new
|
|
|
|
// appender is created.
|
|
|
|
type multipleAppender struct {
|
|
|
|
maxSamplesInMemory int
|
|
|
|
currentSampleCount int
|
|
|
|
writer *tsdb.BlockWriter
|
|
|
|
appender storage.Appender
|
|
|
|
}
|
|
|
|
|
|
|
|
func newMultipleAppender(ctx context.Context, maxSamplesInMemory int, blockWriter *tsdb.BlockWriter) *multipleAppender {
|
|
|
|
return &multipleAppender{
|
|
|
|
maxSamplesInMemory: maxSamplesInMemory,
|
|
|
|
writer: blockWriter,
|
|
|
|
appender: blockWriter.Appender(ctx),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (m *multipleAppender) add(ctx context.Context, l labels.Labels, t int64, v float64) error {
|
|
|
|
if _, err := m.appender.Add(l, t, v); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
m.currentSampleCount++
|
|
|
|
if m.currentSampleCount > m.maxSamplesInMemory {
|
|
|
|
return m.flushAndCommit(ctx)
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (m *multipleAppender) flushAndCommit(ctx context.Context) error {
|
|
|
|
if _, err := m.writer.Flush(ctx); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
if err := m.appender.Commit(); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
m.appender = m.writer.Appender(ctx)
|
|
|
|
m.currentSampleCount = 0
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (m *multipleAppender) close() error {
|
|
|
|
return m.writer.Close()
|
|
|
|
}
|