// Copyright 2013 Prometheus Team // 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 ast import ( "github.com/prometheus/prometheus/model" "github.com/prometheus/prometheus/storage/metric" "log" "time" ) type FullRangeMap map[model.Fingerprint]time.Duration type IntervalRangeMap map[model.Fingerprint]bool type QueryAnalyzer struct { // Values collected by query analysis. // // Full ranges always implicitly span a time range of: // - start: query interval start - duration // - end: query interval end // // 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. IntervalRanges IntervalRangeMap // The underlying storage to which the query will be applied. Needed for // extracting timeseries fingerprint information during query analysis. storage *metric.TieredStorage } func NewQueryAnalyzer(storage *metric.TieredStorage) *QueryAnalyzer { return &QueryAnalyzer{ FullRanges: FullRangeMap{}, IntervalRanges: IntervalRangeMap{}, storage: storage, } } func (analyzer *QueryAnalyzer) Visit(node Node) { switch n := node.(type) { case *VectorLiteral: fingerprints, err := analyzer.storage.GetFingerprintsForLabelSet(n.labels) if err != nil { log.Printf("Error getting fingerprints for labelset %v: %v", n.labels, err) return } n.fingerprints = fingerprints for _, fingerprint := range fingerprints { if !analyzer.IntervalRanges[*fingerprint] { analyzer.IntervalRanges[*fingerprint] = true } } case *MatrixLiteral: fingerprints, err := analyzer.storage.GetFingerprintsForLabelSet(n.labels) if err != nil { log.Printf("Error getting fingerprints for labelset %v: %v", n.labels, err) return } n.fingerprints = fingerprints for _, fingerprint := range fingerprints { interval := n.interval // If an interval has already been recorded for this fingerprint, merge // it with the current interval. if oldInterval, ok := analyzer.FullRanges[*fingerprint]; ok { if oldInterval > interval { interval = oldInterval } } analyzer.FullRanges[*fingerprint] = interval } } } func (analyzer *QueryAnalyzer) AnalyzeQueries(node Node) { Walk(analyzer, node) // Find and dedupe overlaps between full and stepped ranges. Full ranges // always contain more points *and* span more time than stepped ranges, so // throw away stepped ranges for fingerprints which have full ranges. for fingerprint := range analyzer.FullRanges { if analyzer.IntervalRanges[fingerprint] { delete(analyzer.IntervalRanges, fingerprint) } } } func viewAdapterForInstantQuery(node Node, timestamp time.Time, storage *metric.TieredStorage) (*viewAdapter, error) { analyzer := NewQueryAnalyzer(storage) analyzer.AnalyzeQueries(node) viewBuilder := metric.NewViewRequestBuilder() for fingerprint, rangeDuration := range analyzer.FullRanges { viewBuilder.GetMetricRange(fingerprint, timestamp.Add(-rangeDuration), timestamp) } for fingerprint := range analyzer.IntervalRanges { viewBuilder.GetMetricAtTime(fingerprint, timestamp) } view, err := analyzer.storage.MakeView(viewBuilder, time.Duration(60)*time.Second) if err != nil { return nil, err } return NewViewAdapter(view, storage), nil } func viewAdapterForRangeQuery(node Node, start time.Time, end time.Time, interval time.Duration, storage *metric.TieredStorage) (*viewAdapter, error) { analyzer := NewQueryAnalyzer(storage) analyzer.AnalyzeQueries(node) viewBuilder := metric.NewViewRequestBuilder() for fingerprint, rangeDuration := range analyzer.FullRanges { // TODO: we should support GetMetricRangeAtInterval() or similar ops in the view builder. for t := start; t.Before(end); t = t.Add(interval) { viewBuilder.GetMetricRange(fingerprint, t.Add(-rangeDuration), t) } } for fingerprint := range analyzer.IntervalRanges { viewBuilder.GetMetricAtInterval(fingerprint, start, end, interval) } view, err := analyzer.storage.MakeView(viewBuilder, time.Duration(60)*time.Second) if err != nil { return nil, err } return NewViewAdapter(view, storage), nil }