diff --git a/integration/backward_compatibility_test.go b/integration/backward_compatibility_test.go index 1df6f1bdf3e..60cb13457e4 100644 --- a/integration/backward_compatibility_test.go +++ b/integration/backward_compatibility_test.go @@ -190,6 +190,8 @@ func TestCanSupportHoltWintersFunc(t *testing.T) { "-alertmanager.web.external-url": "http://localhost/alertmanager", // enable experimental promQL funcs "-querier.enable-promql-experimental-functions": "true", + // max query length + "-store.max-query-length": "30d", }, ) // make alert manager config dir diff --git a/pkg/cortex/cortex.go b/pkg/cortex/cortex.go index a00b5f34809..a39081cc51f 100644 --- a/pkg/cortex/cortex.go +++ b/pkg/cortex/cortex.go @@ -39,6 +39,7 @@ import ( "github.com/cortexproject/cortex/pkg/ingester" "github.com/cortexproject/cortex/pkg/ingester/client" "github.com/cortexproject/cortex/pkg/parquetconverter" + cortexparser "github.com/cortexproject/cortex/pkg/parser" "github.com/cortexproject/cortex/pkg/querier" "github.com/cortexproject/cortex/pkg/querier/tenantfederation" "github.com/cortexproject/cortex/pkg/querier/tripperware" @@ -551,7 +552,5 @@ func (t *Cortex) readyHandler(sm *services.Manager) http.HandlerFunc { } func (t *Cortex) setupPromQLFunctions() { - // The holt_winters function is renamed to double_exponential_smoothing and has been experimental since Prometheus v3. (https://github.com/prometheus/prometheus/pull/14930) - // The cortex supports holt_winters for users using this function. - querier.EnableExperimentalPromQLFunctions(t.Cfg.Querier.EnablePromQLExperimentalFunctions, true) + cortexparser.Setup(t.Cfg.Querier.EnablePromQLExperimentalFunctions, true) } diff --git a/pkg/parser/parser.go b/pkg/parser/parser.go index 07940c6aaaa..d37ff965b44 100644 --- a/pkg/parser/parser.go +++ b/pkg/parser/parser.go @@ -3,16 +3,41 @@ package parser import ( "maps" + "github.com/prometheus/prometheus/promql" promqlparser "github.com/prometheus/prometheus/promql/parser" "github.com/thanos-io/promql-engine/execution/parse" ) -var functions = buildFunctions() +var functions = buildFunctions(true) -func buildFunctions() map[string]*promqlparser.Function { +func Setup(enableExperimentalFunctions, enableHoltWinters bool) { + promqlparser.EnableExperimentalFunctions = enableExperimentalFunctions + buildFunctions(enableHoltWinters) +} + +func buildFunctions(enableHoltWinters bool) map[string]*promqlparser.Function { fns := make(map[string]*promqlparser.Function, len(promqlparser.Functions)) maps.Copy(fns, promqlparser.Functions) maps.Copy(fns, parse.XFunctions) + + // The holt_winters function was renamed to double_exponential_smoothing and marked experimental in Prometheus v3. + // Register holt_winters as an alias to maintain backward compatibility. + if enableHoltWinters { + if des, ok := fns["double_exponential_smoothing"]; ok { + holtWinters := *des + holtWinters.Experimental = false + holtWinters.Name = "holt_winters" + fns["holt_winters"] = &holtWinters + + // Also register in global Prometheus parser for engine execution + promqlparser.Functions["holt_winters"] = &holtWinters + promql.FunctionCalls["holt_winters"] = promql.FunctionCalls["double_exponential_smoothing"] + } + } else { + delete(promqlparser.Functions, "holt_winters") + delete(promql.FunctionCalls, "holt_winters") + } + return fns } diff --git a/pkg/querier/querier.go b/pkg/querier/querier.go index 720e304dfcb..8ebc66a16dc 100644 --- a/pkg/querier/querier.go +++ b/pkg/querier/querier.go @@ -17,7 +17,6 @@ import ( "github.com/prometheus/common/model" "github.com/prometheus/prometheus/model/labels" "github.com/prometheus/prometheus/promql" - "github.com/prometheus/prometheus/promql/parser" "github.com/prometheus/prometheus/storage" "github.com/prometheus/prometheus/util/annotations" "github.com/thanos-io/thanos/pkg/strutil" @@ -719,15 +718,3 @@ func validateQueryTimeRange(ctx context.Context, userID string, startMs, endMs i return int64(startTime), int64(endTime), nil } - -func EnableExperimentalPromQLFunctions(enablePromQLExperimentalFunctions, enableHoltWinters bool) { - parser.EnableExperimentalFunctions = enablePromQLExperimentalFunctions - - if enableHoltWinters { - holtWinters := *parser.Functions["double_exponential_smoothing"] - holtWinters.Experimental = false - holtWinters.Name = "holt_winters" - parser.Functions["holt_winters"] = &holtWinters - promql.FunctionCalls["holt_winters"] = promql.FunctionCalls["double_exponential_smoothing"] - } -} diff --git a/pkg/querier/querier_test.go b/pkg/querier/querier_test.go index ec08fee2ed2..4a13dae9aaf 100644 --- a/pkg/querier/querier_test.go +++ b/pkg/querier/querier_test.go @@ -34,6 +34,7 @@ import ( promchunk "github.com/cortexproject/cortex/pkg/chunk/encoding" "github.com/cortexproject/cortex/pkg/cortexpb" "github.com/cortexproject/cortex/pkg/ingester/client" + cortexparser "github.com/cortexproject/cortex/pkg/parser" "github.com/cortexproject/cortex/pkg/querier/batch" "github.com/cortexproject/cortex/pkg/querier/series" "github.com/cortexproject/cortex/pkg/util" @@ -1693,32 +1694,34 @@ func TestConfig_Validate(t *testing.T) { } func Test_EnableExperimentalPromQLFunctions(t *testing.T) { - EnableExperimentalPromQLFunctions(true, true) + cortexparser.Setup(true, true) + defer cortexparser.Setup(false, false) - // holt_winters function should exist - holtWintersFunc, ok := parser.Functions["holt_winters"] - require.True(t, ok) + // Verify experimental functions flag is set + require.True(t, parser.EnableExperimentalFunctions) // double_exponential_smoothing function should exist doubleExponentialSmoothingFunc, ok := parser.Functions["double_exponential_smoothing"] require.True(t, ok) + require.Equal(t, "double_exponential_smoothing", doubleExponentialSmoothingFunc.Name) + require.True(t, parser.Functions["double_exponential_smoothing"].Experimental) - // holt_winters should not experimental function. + // holt_winters should exist + holtWintersFunc, ok := parser.Functions["holt_winters"] + require.True(t, ok) + require.Equal(t, "holt_winters", holtWintersFunc.Name) require.False(t, holtWintersFunc.Experimental) - // holt_winters's Variadic, ReturnType, and ArgTypes are the same as the double_exponential_smoothing. + + // holt_winters's Variadic, ReturnType, and ArgTypes are the same as the double_exponential_smoothing require.Equal(t, doubleExponentialSmoothingFunc.Variadic, holtWintersFunc.Variadic) require.Equal(t, doubleExponentialSmoothingFunc.ReturnType, holtWintersFunc.ReturnType) require.Equal(t, doubleExponentialSmoothingFunc.ArgTypes, holtWintersFunc.ArgTypes) - // double_exponential_smoothing shouldn't be changed. - require.Equal(t, "double_exponential_smoothing", doubleExponentialSmoothingFunc.Name) - require.True(t, parser.Functions["double_exponential_smoothing"].Experimental) - - // holt_winters function calls should exist. + // holt_winters function calls should exist _, ok = promql.FunctionCalls["holt_winters"] require.True(t, ok) - // double_exponential_smoothing function calls should exist. + // double_exponential_smoothing function calls should exist _, ok = promql.FunctionCalls["double_exponential_smoothing"] require.True(t, ok) }