grafana/pkg/tsdb/cloudwatch/time_series_query.go
Shabeeb Khalid 8f96d23eee
CloudWatch: Use context in aws ListMetricsPages (#76938)
Use context in aws ListMetricsPages

Use ListMetricsPagesWithContext and pass context in related sub calls
2023-10-23 16:17:06 +02:00

135 lines
3.6 KiB
Go

package cloudwatch
import (
"context"
"fmt"
"github.com/grafana/grafana-plugin-sdk-go/backend"
"golang.org/x/sync/errgroup"
"github.com/grafana/grafana/pkg/infra/log"
"github.com/grafana/grafana/pkg/services/featuremgmt"
"github.com/grafana/grafana/pkg/tsdb/cloudwatch/models"
)
type responseWrapper struct {
DataResponse *backend.DataResponse
RefId string
}
func (e *cloudWatchExecutor) executeTimeSeriesQuery(ctx context.Context, logger log.Logger, req *backend.QueryDataRequest) (*backend.QueryDataResponse, error) {
logger.Debug("Executing time series query")
resp := backend.NewQueryDataResponse()
if len(req.Queries) == 0 {
return nil, fmt.Errorf("request contains no queries")
}
// startTime and endTime are always the same for all queries
startTime := req.Queries[0].TimeRange.From
endTime := req.Queries[0].TimeRange.To
if !startTime.Before(endTime) {
return nil, fmt.Errorf("invalid time range: start time must be before end time")
}
instance, err := e.getInstance(ctx, req.PluginContext)
if err != nil {
return nil, err
}
requestQueries, err := models.ParseMetricDataQueries(req.Queries, startTime, endTime, instance.Settings.Region, logger,
e.features.IsEnabled(featuremgmt.FlagCloudWatchCrossAccountQuerying))
if err != nil {
return nil, err
}
if len(requestQueries) == 0 {
return backend.NewQueryDataResponse(), nil
}
requestQueriesByRegion := make(map[string][]*models.CloudWatchQuery)
for _, query := range requestQueries {
if _, exist := requestQueriesByRegion[query.Region]; !exist {
requestQueriesByRegion[query.Region] = []*models.CloudWatchQuery{}
}
requestQueriesByRegion[query.Region] = append(requestQueriesByRegion[query.Region], query)
}
resultChan := make(chan *responseWrapper, len(req.Queries))
eg, ectx := errgroup.WithContext(ctx)
for r, regionQueries := range requestQueriesByRegion {
region := r
batches := [][]*models.CloudWatchQuery{regionQueries}
if e.features.IsEnabled(featuremgmt.FlagCloudWatchBatchQueries) {
batches = getMetricQueryBatches(regionQueries, logger)
}
for _, batch := range batches {
requestQueries := batch
eg.Go(func() error {
defer func() {
if err := recover(); err != nil {
logger.Error("Execute Get Metric Data Query Panic", "error", err, "stack", log.Stack(1))
if theErr, ok := err.(error); ok {
resultChan <- &responseWrapper{
DataResponse: &backend.DataResponse{
Error: theErr,
},
}
}
}
}()
client, err := e.getCWClient(ctx, req.PluginContext, region)
if err != nil {
return err
}
metricDataInput, err := e.buildMetricDataInput(logger, startTime, endTime, requestQueries)
if err != nil {
return err
}
mdo, err := e.executeRequest(ectx, client, metricDataInput)
if err != nil {
return err
}
if e.features.IsEnabled(featuremgmt.FlagCloudWatchWildCardDimensionValues) {
requestQueries, err = e.getDimensionValuesForWildcards(ctx, req.PluginContext, region, client, requestQueries, instance.tagValueCache, logger)
if err != nil {
return err
}
}
res, err := e.parseResponse(startTime, endTime, mdo, requestQueries)
if err != nil {
return err
}
for _, responseWrapper := range res {
resultChan <- responseWrapper
}
return nil
})
}
}
if err := eg.Wait(); err != nil {
dataResponse := backend.DataResponse{
Error: fmt.Errorf("metric request error: %q", err),
}
resultChan <- &responseWrapper{
DataResponse: &dataResponse,
}
}
close(resultChan)
for result := range resultChan {
resp.Responses[result.RefId] = *result.DataResponse
}
return resp, nil
}