From 6e0f18b13a98247c1a479e1de8d2bd74cceedaff Mon Sep 17 00:00:00 2001 From: Kai Hayashi Date: Wed, 1 Sep 2021 07:44:47 +0000 Subject: [PATCH] CloudWatch: Update Period `auto` to take retention into account (#37424) * now to get it to build * modified names, added the 455 day case, and added a comment * removed wireguard override i used for my local * updated the docs --- docs/sources/datasources/cloudwatch.md | 2 +- pkg/tsdb/cloudwatch/request_parser.go | 15 +++++++++++- pkg/tsdb/cloudwatch/request_parser_test.go | 27 ++++++++++++++++++++++ 3 files changed, 42 insertions(+), 2 deletions(-) diff --git a/docs/sources/datasources/cloudwatch.md b/docs/sources/datasources/cloudwatch.md index 5283220d342..caf851b34c9 100644 --- a/docs/sources/datasources/cloudwatch.md +++ b/docs/sources/datasources/cloudwatch.md @@ -180,7 +180,7 @@ Please note that in the case you use the expression field to reference another q A period is the length of time associated with a specific Amazon CloudWatch statistic. Periods are defined in numbers of seconds, and valid values for period are 1, 5, 10, 30, or any multiple of 60. -If the period field is left blank or set to `auto`, then it calculates automatically based on the time range. The formula used is `time range in seconds / 2000`, and then it snaps to the next higher value in an array of predefined periods `[60, 300, 900, 3600, 21600, 86400]`. By clicking `Show Query Preview` in the query editor, you can see what period Grafana used. +If the period field is left blank or set to `auto`, then it calculates automatically based on the time range and [cloudwatch's retention policy](https://aws.amazon.com/about-aws/whats-new/2016/11/cloudwatch-extends-metrics-retention-and-new-user-interface/). The formula used is `time range in seconds / 2000`, and then it snaps to the next higher value in an array of predefined periods `[60, 300, 900, 3600, 21600, 86400]` after removing periods based on retention. By clicking `Show Query Preview` in the query editor, you can see what period Grafana used. ### Deep linking from Grafana panels to the CloudWatch console diff --git a/pkg/tsdb/cloudwatch/request_parser.go b/pkg/tsdb/cloudwatch/request_parser.go index d27410b653c..e842bb96ad2 100644 --- a/pkg/tsdb/cloudwatch/request_parser.go +++ b/pkg/tsdb/cloudwatch/request_parser.go @@ -69,7 +69,7 @@ func parseRequestQuery(model *simplejson.Json, refId string, startTime time.Time var period int if strings.ToLower(p) == "auto" || p == "" { deltaInSeconds := endTime.Sub(startTime).Seconds() - periods := []int{60, 300, 900, 3600, 21600, 86400} + periods := getRetainedPeriods(time.Since(startTime)) datapoints := int(math.Ceil(deltaInSeconds / 2000)) period = periods[len(periods)-1] for _, value := range periods { @@ -123,6 +123,19 @@ func parseRequestQuery(model *simplejson.Json, refId string, startTime time.Time }, nil } +func getRetainedPeriods(timeSince time.Duration) []int { + // See https://aws.amazon.com/about-aws/whats-new/2016/11/cloudwatch-extends-metrics-retention-and-new-user-interface/ + if timeSince > time.Duration(455)*24*time.Hour { + return []int{21600, 86400} + } else if timeSince > time.Duration(63)*24*time.Hour { + return []int{3600, 21600, 86400} + } else if timeSince > time.Duration(15)*24*time.Hour { + return []int{300, 900, 3600, 21600, 86400} + } else { + return []int{60, 300, 900, 3600, 21600, 86400} + } +} + func parseStatistics(model *simplejson.Json) []string { var statistics []string for _, s := range model.Get("statistics").MustArray() { diff --git a/pkg/tsdb/cloudwatch/request_parser_test.go b/pkg/tsdb/cloudwatch/request_parser_test.go index 3f1be83be95..9f01fcdd5df 100644 --- a/pkg/tsdb/cloudwatch/request_parser_test.go +++ b/pkg/tsdb/cloudwatch/request_parser_test.go @@ -208,5 +208,32 @@ func TestRequestParser(t *testing.T) { require.NoError(t, err) assert.Equal(t, 86400, res.Period) }) + + t.Run("Time range is 2 days, but 16 days ago", func(t *testing.T) { + query.Set("period", "auto") + to := time.Now().AddDate(0, 0, -14) + from := to.AddDate(0, 0, -2) + res, err := parseRequestQuery(query, "ref1", from, to) + require.NoError(t, err) + assert.Equal(t, 300, res.Period) + }) + + t.Run("Time range is 2 days, but 90 days ago", func(t *testing.T) { + query.Set("period", "auto") + to := time.Now().AddDate(0, 0, -88) + from := to.AddDate(0, 0, -2) + res, err := parseRequestQuery(query, "ref1", from, to) + require.NoError(t, err) + assert.Equal(t, 3600, res.Period) + }) + + t.Run("Time range is 2 days, but 456 days ago", func(t *testing.T) { + query.Set("period", "auto") + to := time.Now().AddDate(0, 0, -454) + from := to.AddDate(0, 0, -2) + res, err := parseRequestQuery(query, "ref1", from, to) + require.NoError(t, err) + assert.Equal(t, 21600, res.Period) + }) }) }