func prepareInstantQuery(node Node, timestamp clientmodel.Timestamp, storage local.Storage, queryStats *stats.TimerGroup) (local.Preloader, error) { analyzeTimer := queryStats.GetTimer(stats.QueryAnalysisTime).Start() analyzer := NewQueryAnalyzer(storage) Walk(analyzer, node) analyzeTimer.Stop() // TODO: Preloading should time out after a given duration. preloadTimer := queryStats.GetTimer(stats.PreloadTime).Start() p := storage.NewPreloader() for fp, rangeDuration := range analyzer.FullRanges { if err := p.PreloadRange(fp, timestamp.Add(-rangeDuration), timestamp, *stalenessDelta); err != nil { p.Close() return nil, err } } for fp := range analyzer.IntervalRanges { if err := p.PreloadRange(fp, timestamp, timestamp, *stalenessDelta); err != nil { p.Close() return nil, err } } preloadTimer.Stop() ii := &iteratorInitializer{ storage: storage, } Walk(ii, node) return p, nil }
// makeView materializes a View according to a ViewRequestBuilder, subject to a // timeout. func (t *TieredStorage) makeView(builder metric.ViewRequestBuilder, deadline time.Duration, queryStats *stats.TimerGroup) (metric.View, error) { t.mu.RLock() defer t.mu.RUnlock() if t.state != tieredStorageServing { return nil, fmt.Errorf("storage is not serving") } // The result channel needs a one-element buffer in case we have timed // out in makeView, but the view rendering still completes afterwards // and writes to the channel. result := make(chan metric.View, 1) // The abort channel needs a one-element buffer in case the view // rendering has already exited and doesn't consume from the channel // anymore. abortChan := make(chan bool, 1) errChan := make(chan error) queryStats.GetTimer(stats.ViewQueueTime).Start() t.ViewQueue <- viewJob{ builder: builder, output: result, abort: abortChan, err: errChan, stats: queryStats, } select { case view := <-result: return view, nil case err := <-errChan: return nil, err case <-time.After(deadline): abortChan <- true return nil, fmt.Errorf("fetching query data timed out after %s", deadline) } }
// EvalToString evaluates the given node into a string of the given format. func EvalToString(node Node, timestamp clientmodel.Timestamp, format OutputFormat, storage local.Storage, queryStats *stats.TimerGroup) string { prepareTimer := queryStats.GetTimer(stats.TotalQueryPreparationTime).Start() closer, err := prepareInstantQuery(node, timestamp, storage, queryStats) prepareTimer.Stop() if err != nil { panic(err) } defer closer.Close() evalTimer := queryStats.GetTimer(stats.InnerEvalTime).Start() switch node.Type() { case ScalarType: scalar := node.(ScalarNode).Eval(timestamp) evalTimer.Stop() switch format { case Text: return fmt.Sprintf("scalar: %v @[%v]", scalar, timestamp) case JSON: return TypedValueToJSON(scalar, "scalar") } case VectorType: vector := node.(VectorNode).Eval(timestamp) evalTimer.Stop() switch format { case Text: return vector.String() case JSON: return TypedValueToJSON(vector, "vector") } case MatrixType: matrix := node.(MatrixNode).Eval(timestamp) evalTimer.Stop() switch format { case Text: return matrix.String() case JSON: return TypedValueToJSON(matrix, "matrix") } case StringType: str := node.(StringNode).Eval(timestamp) evalTimer.Stop() switch format { case Text: return str case JSON: return TypedValueToJSON(str, "string") } } panic("Switch didn't cover all node types") }
// EvalToString evaluates the given node into a string of the given format. func EvalToString(node Node, timestamp clientmodel.Timestamp, format OutputFormat, storage metric.PreloadingPersistence, queryStats *stats.TimerGroup) string { viewTimer := queryStats.GetTimer(stats.TotalViewBuildingTime).Start() viewAdapter, err := viewAdapterForInstantQuery(node, timestamp, storage, queryStats) viewTimer.Stop() if err != nil { panic(err) } evalTimer := queryStats.GetTimer(stats.InnerEvalTime).Start() switch node.Type() { case SCALAR: scalar := node.(ScalarNode).Eval(timestamp, viewAdapter) evalTimer.Stop() switch format { case TEXT: return fmt.Sprintf("scalar: %v @[%v]", scalar, timestamp) case JSON: return TypedValueToJSON(scalar, "scalar") } case VECTOR: vector := node.(VectorNode).Eval(timestamp, viewAdapter) evalTimer.Stop() switch format { case TEXT: return vector.String() case JSON: return TypedValueToJSON(vector, "vector") } case MATRIX: matrix := node.(MatrixNode).Eval(timestamp, viewAdapter) evalTimer.Stop() switch format { case TEXT: return matrix.String() case JSON: return TypedValueToJSON(matrix, "matrix") } case STRING: str := node.(StringNode).Eval(timestamp, viewAdapter) evalTimer.Stop() switch format { case TEXT: return str case JSON: return TypedValueToJSON(str, "string") } } panic("Switch didn't cover all node types") }
func prepareRangeQuery(node Node, start clientmodel.Timestamp, end clientmodel.Timestamp, interval time.Duration, storage local.Storage, queryStats *stats.TimerGroup) (local.Preloader, error) { analyzeTimer := queryStats.GetTimer(stats.QueryAnalysisTime).Start() analyzer := NewQueryAnalyzer(storage) Walk(analyzer, node) analyzeTimer.Stop() // TODO: Preloading should time out after a given duration. preloadTimer := queryStats.GetTimer(stats.PreloadTime).Start() p := storage.NewPreloader() for fp, rangeDuration := range analyzer.FullRanges { if err := p.PreloadRange(fp, start.Add(-rangeDuration), end, *stalenessDelta); err != nil { p.Close() return nil, err } /* if interval < rangeDuration { if err := p.GetMetricRange(fp, end, end.Sub(start)+rangeDuration); err != nil { p.Close() return nil, err } } else { if err := p.GetMetricRangeAtInterval(fp, start, end, interval, rangeDuration); err != nil { p.Close() return nil, err } } */ } for fp := range analyzer.IntervalRanges { if err := p.PreloadRange(fp, start, end, *stalenessDelta); err != nil { p.Close() return nil, err } } preloadTimer.Stop() ii := &iteratorInitializer{ storage: storage, } Walk(ii, node) return p, nil }
func viewAdapterForRangeQuery(node Node, start clientmodel.Timestamp, end clientmodel.Timestamp, interval time.Duration, storage metric.PreloadingPersistence, queryStats *stats.TimerGroup) (*viewAdapter, error) { analyzeTimer := queryStats.GetTimer(stats.QueryAnalysisTime).Start() analyzer := NewQueryAnalyzer(storage) analyzer.AnalyzeQueries(node) analyzeTimer.Stop() requestBuildTimer := queryStats.GetTimer(stats.ViewRequestBuildTime).Start() viewBuilder := storage.NewViewRequestBuilder() for fingerprint, rangeDuration := range analyzer.FullRanges { if interval < rangeDuration { viewBuilder.GetMetricRange(&fingerprint, start.Add(-rangeDuration), end) } else { viewBuilder.GetMetricRangeAtInterval(&fingerprint, start.Add(-rangeDuration), end, interval, rangeDuration) } } for fingerprint := range analyzer.IntervalRanges { viewBuilder.GetMetricAtInterval(&fingerprint, start, end, interval) } requestBuildTimer.Stop() buildTimer := queryStats.GetTimer(stats.InnerViewBuildingTime).Start() view, err := viewBuilder.Execute(time.Duration(60)*time.Second, queryStats) buildTimer.Stop() if err != nil { return nil, err } return NewViewAdapter(view, storage, queryStats), nil }
// EvalToVector evaluates the given node into a Vector. Matrices aren't supported. func EvalToVector(node Node, timestamp clientmodel.Timestamp, storage local.Storage, queryStats *stats.TimerGroup) (Vector, error) { prepareTimer := queryStats.GetTimer(stats.TotalQueryPreparationTime).Start() closer, err := prepareInstantQuery(node, timestamp, storage, queryStats) prepareTimer.Stop() if err != nil { panic(err) } defer closer.Close() evalTimer := queryStats.GetTimer(stats.InnerEvalTime).Start() switch node.Type() { case ScalarType: scalar := node.(ScalarNode).Eval(timestamp) evalTimer.Stop() return Vector{&Sample{Value: scalar}}, nil case VectorType: vector := node.(VectorNode).Eval(timestamp) evalTimer.Stop() return vector, nil case MatrixType: return nil, errors.New("matrices not supported by EvalToVector") case StringType: str := node.(StringNode).Eval(timestamp) evalTimer.Stop() return Vector{ &Sample{ Metric: clientmodel.COWMetric{ Metric: clientmodel.Metric{ "__value__": clientmodel.LabelValue(str), }, Copied: true, }, }, }, nil } panic("Switch didn't cover all node types") }
// EvalVectorRange evaluates a VectorNode with a range query. func EvalVectorRange(node VectorNode, start clientmodel.Timestamp, end clientmodel.Timestamp, interval time.Duration, storage local.Storage, queryStats *stats.TimerGroup) (Matrix, error) { // Explicitly initialize to an empty matrix since a nil Matrix encodes to // null in JSON. matrix := Matrix{} prepareTimer := queryStats.GetTimer(stats.TotalQueryPreparationTime).Start() closer, err := prepareRangeQuery(node, start, end, interval, storage, queryStats) prepareTimer.Stop() if err != nil { return nil, err } defer closer.Close() // TODO implement watchdog timer for long-running queries. evalTimer := queryStats.GetTimer(stats.InnerEvalTime).Start() sampleStreams := map[uint64]*SampleStream{} for t := start; !t.After(end); t = t.Add(interval) { vector := node.Eval(t) for _, sample := range vector { samplePair := metric.SamplePair{ Value: sample.Value, Timestamp: sample.Timestamp, } groupingKey := labelsToKey(sample.Metric.Metric) if sampleStreams[groupingKey] == nil { sampleStreams[groupingKey] = &SampleStream{ Metric: sample.Metric, Values: metric.Values{samplePair}, } } else { sampleStreams[groupingKey].Values = append(sampleStreams[groupingKey].Values, samplePair) } } } evalTimer.Stop() appendTimer := queryStats.GetTimer(stats.ResultAppendTime).Start() for _, sampleStream := range sampleStreams { matrix = append(matrix, *sampleStream) } appendTimer.Stop() return matrix, nil }
// EvalVectorRange evaluates a VectorNode with a range query. func EvalVectorRange(node VectorNode, start clientmodel.Timestamp, end clientmodel.Timestamp, interval time.Duration, storage metric.PreloadingPersistence, queryStats *stats.TimerGroup) (Matrix, error) { // Explicitly initialize to an empty matrix since a nil Matrix encodes to // null in JSON. matrix := Matrix{} viewTimer := queryStats.GetTimer(stats.TotalViewBuildingTime).Start() viewAdapter, err := viewAdapterForRangeQuery(node, start, end, interval, storage, queryStats) viewTimer.Stop() if err != nil { return nil, err } // TODO implement watchdog timer for long-running queries. evalTimer := queryStats.GetTimer(stats.InnerEvalTime).Start() sampleSets := map[uint64]*metric.SampleSet{} for t := start; t.Before(end); t = t.Add(interval) { vector := node.Eval(t, viewAdapter) for _, sample := range vector { samplePair := metric.SamplePair{ Value: sample.Value, Timestamp: sample.Timestamp, } groupingKey := labelsToKey(sample.Metric) if sampleSets[groupingKey] == nil { sampleSets[groupingKey] = &metric.SampleSet{ Metric: sample.Metric, Values: metric.Values{samplePair}, } } else { sampleSets[groupingKey].Values = append(sampleSets[groupingKey].Values, samplePair) } } } evalTimer.Stop() appendTimer := queryStats.GetTimer(stats.ResultAppendTime).Start() for _, sampleSet := range sampleSets { matrix = append(matrix, *sampleSet) } appendTimer.Stop() return matrix, nil }