grafana/pkg/tsdb/cloudwatch/get_metric_query_batches.go
Nathan Vērzemnieks b5f26560c2
Cloudwatch: Remove core imports from infra/log (#81543)
* Cloudwatch: use logging from plugin-sdk-go instead of infra/log
* Add a link to the TODO issue for moving `instrumentContext`
2024-02-07 13:53:05 +01:00

92 lines
3.1 KiB
Go

package cloudwatch
import (
"regexp"
"github.com/grafana/grafana-plugin-sdk-go/backend/log"
"github.com/grafana/grafana/pkg/tsdb/cloudwatch/models"
)
// nonWordRegex is for spliting the expressions to just functions and ids
var nonWordRegex = regexp.MustCompile(`\W+`)
// getMetricQueryBatches separates queries into batches if necessary. Metric Insight queries cannot run together, and math expressions must be run
// with all the queries they reference.
func getMetricQueryBatches(queries []*models.CloudWatchQuery, logger log.Logger) [][]*models.CloudWatchQuery {
if !hasMultipleMetricInsights(queries) {
return [][]*models.CloudWatchQuery{queries}
}
logger.Debug("Separating queries into batches")
// set up list of math expression queries since below we loop over them to get what query IDs they reference
var mathQueries []*models.CloudWatchQuery
for _, query := range queries {
if query.GetGetMetricDataAPIMode() == models.GMDApiModeMathExpression {
mathQueries = append(mathQueries, query)
}
}
// put queries into a set in order to facilitate lookup below
idToQuery := make(map[string]*models.CloudWatchQuery, len(queries))
for _, q := range queries {
idToQuery[q.Id] = q
}
// gets query IDs which are referenced in math expressions
mathQueryIdToReferences := make(map[string][]*models.CloudWatchQuery)
// we will use this set of referenced queries to determine the root queries below
referencedQueries := make(map[string]bool)
for _, mathQuery := range mathQueries {
substrings := nonWordRegex.Split(mathQuery.Expression, -1)
for _, id := range substrings {
query, found := idToQuery[id]
if found {
mathQueryIdToReferences[mathQuery.Id] = append(mathQueryIdToReferences[mathQuery.Id], query)
referencedQueries[query.Id] = true
}
}
}
batches := [][]*models.CloudWatchQuery{}
for _, query := range queries {
// if a query is not referenced, then it is a "root" query
if _, ok := referencedQueries[query.Id]; !ok {
batches = append(batches, getConnectedQueries(query, mathQueryIdToReferences))
}
}
return batches
}
// getConnectedQueries does a breadth-first search to find all the query ids connected to the root id by references. The root id is also returned in the response.
func getConnectedQueries(root *models.CloudWatchQuery, queryReferences map[string][]*models.CloudWatchQuery) []*models.CloudWatchQuery {
visited := map[string]bool{root.Id: true}
queriesToReturn := []*models.CloudWatchQuery{}
queriesToVisit := []*models.CloudWatchQuery{root}
for i := 0; i < len(queriesToVisit); i++ {
currentQuery := queriesToVisit[i]
queriesToReturn = append(queriesToReturn, currentQuery)
for _, queryRef := range queryReferences[currentQuery.Id] {
if !visited[queryRef.Id] {
visited[queryRef.Id] = true
queriesToVisit = append(queriesToVisit, queryRef)
}
}
}
return queriesToReturn
}
func hasMultipleMetricInsights(queries []*models.CloudWatchQuery) bool {
count := 0
for _, query := range queries {
if query.GetGetMetricDataAPIMode() == models.GMDApiModeSQLExpression {
count++
}
if count > 1 {
return true
}
}
return false
}