From 333f90d8acc408576ec21a1f943c7d3a0d9e5a93 Mon Sep 17 00:00:00 2001 From: Srikanth Chekuri Date: Wed, 29 Jan 2025 14:47:47 +0530 Subject: [PATCH] fix: window size for small time ranges (#6964) --- ee/query-service/anomaly/params.go | 9 ++++----- ee/query-service/anomaly/seasonal.go | 25 +++++++++++++++---------- pkg/query-service/rules/db.go | 2 +- 3 files changed, 20 insertions(+), 16 deletions(-) diff --git a/ee/query-service/anomaly/params.go b/ee/query-service/anomaly/params.go index 8340a2673a..b0107493d7 100644 --- a/ee/query-service/anomaly/params.go +++ b/ee/query-service/anomaly/params.go @@ -59,7 +59,7 @@ type anomalyQueryParams struct { // The results obtained from this query are used to compare with predicted values // and to detect anomalies CurrentPeriodQuery *v3.QueryRangeParamsV3 - // PastPeriodQuery is the query range params for past seasonal period + // PastPeriodQuery is the query range params for past period of seasonality // Example: For weekly seasonality, (now-1w-5m, now-1w) // : For daily seasonality, (now-1d-5m, now-1d) // : For hourly seasonality, (now-1h-5m, now-1h) @@ -74,7 +74,6 @@ type anomalyQueryParams struct { // : For daily seasonality, this is the query range params for the (now-2d-5m, now-1d) // : For hourly seasonality, this is the query range params for the (now-2h-5m, now-1h) PastSeasonQuery *v3.QueryRangeParamsV3 - // Past2SeasonQuery is the query range params for past 2 seasonal period to the current season // Example: For weekly seasonality, this is the query range params for the (now-3w-5m, now-2w) // : For daily seasonality, this is the query range params for the (now-3d-5m, now-2d) @@ -144,13 +143,13 @@ func prepareAnomalyQueryParams(req *v3.QueryRangeParamsV3, seasonality Seasonali switch seasonality { case SeasonalityWeekly: currentGrowthPeriodStart = start - oneWeekOffset - currentGrowthPeriodEnd = end + currentGrowthPeriodEnd = start case SeasonalityDaily: currentGrowthPeriodStart = start - oneDayOffset - currentGrowthPeriodEnd = end + currentGrowthPeriodEnd = start case SeasonalityHourly: currentGrowthPeriodStart = start - oneHourOffset - currentGrowthPeriodEnd = end + currentGrowthPeriodEnd = start } currentGrowthQuery := &v3.QueryRangeParamsV3{ diff --git a/ee/query-service/anomaly/seasonal.go b/ee/query-service/anomaly/seasonal.go index 9b5f33d3df..c77acc0a8e 100644 --- a/ee/query-service/anomaly/seasonal.go +++ b/ee/query-service/anomaly/seasonal.go @@ -194,10 +194,11 @@ func (p *BaseSeasonalProvider) getMovingAvg(series *v3.Series, movingAvgWindowSi } var sum float64 points := series.Points[startIdx:] - for i := 0; i < movingAvgWindowSize && i < len(points); i++ { + windowSize := int(math.Min(float64(movingAvgWindowSize), float64(len(points)))) + for i := 0; i < windowSize; i++ { sum += points[i].Value } - avg := sum / float64(movingAvgWindowSize) + avg := sum / float64(windowSize) return avg } @@ -226,21 +227,25 @@ func (p *BaseSeasonalProvider) getPredictedSeries( // plus the average of the current season series // minus the mean of the past season series, past2 season series and past3 season series for idx, curr := range series.Points { - predictedValue := - p.getMovingAvg(prevSeries, movingAvgWindowSize, idx) + - p.getAvg(currentSeasonSeries) - - p.getMean(p.getAvg(pastSeasonSeries), p.getAvg(past2SeasonSeries), p.getAvg(past3SeasonSeries)) + movingAvg := p.getMovingAvg(prevSeries, movingAvgWindowSize, idx) + avg := p.getAvg(currentSeasonSeries) + mean := p.getMean(p.getAvg(pastSeasonSeries), p.getAvg(past2SeasonSeries), p.getAvg(past3SeasonSeries)) + predictedValue := movingAvg + avg - mean if predictedValue < 0 { + // this should not happen (except when the data has extreme outliers) + // we will use the moving avg of the previous period series in this case + zap.L().Warn("predictedValue is less than 0", zap.Float64("predictedValue", predictedValue), zap.Any("labels", series.Labels)) predictedValue = p.getMovingAvg(prevSeries, movingAvgWindowSize, idx) } - zap.L().Info("predictedSeries", - zap.Float64("movingAvg", p.getMovingAvg(prevSeries, movingAvgWindowSize, idx)), - zap.Float64("avg", p.getAvg(currentSeasonSeries)), - zap.Float64("mean", p.getMean(p.getAvg(pastSeasonSeries), p.getAvg(past2SeasonSeries), p.getAvg(past3SeasonSeries))), + zap.L().Debug("predictedSeries", + zap.Float64("movingAvg", movingAvg), + zap.Float64("avg", avg), + zap.Float64("mean", mean), zap.Any("labels", series.Labels), zap.Float64("predictedValue", predictedValue), + zap.Float64("curr", curr.Value), ) predictedSeries.Points = append(predictedSeries.Points, v3.Point{ Timestamp: curr.Timestamp, diff --git a/pkg/query-service/rules/db.go b/pkg/query-service/rules/db.go index cc7ef81b8a..343023dd88 100644 --- a/pkg/query-service/rules/db.go +++ b/pkg/query-service/rules/db.go @@ -616,7 +616,7 @@ func (r *ruleDB) GetAlertsInfo(ctx context.Context) (*model.AlertsInfo, error) { } } alertsInfo.TotalAlerts = alertsInfo.TotalAlerts + 1 - if rule.PostableRule.Disabled == false { + if !rule.PostableRule.Disabled { alertsInfo.TotalActiveAlerts = alertsInfo.TotalActiveAlerts + 1 } }