Skip to content

Commit e3052e3

Browse files
committed
Register xfunctions in global parser for rule file loading
Signed-off-by: Paurush Garg <paurushg@amazon.com>
1 parent 74185ef commit e3052e3

3 files changed

Lines changed: 71 additions & 0 deletions

File tree

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
* [FEATURE] Querier: Add timeout classification to classify query timeouts as 4XX (user error) or 5XX (system error) based on phase timing. When enabled, queries that spend most of their time in PromQL evaluation return `422 Unprocessable Entity` instead of `503 Service Unavailable`. #7374
1313
* [FEATURE] Querier: Implement Resource Based Throttling in Querier. #7442
1414
* [FEATURE] Querier: Add resource-based query eviction that automatically cancels the heaviest running query when CPU or heap utilization exceeds configured thresholds. #7488
15+
* [BUGFIX] Ruler: Register xfunctions (xincrease, xrate, xdelta) in the global parser before loading rule files. #7621
1516
* [ENHANCEMENT] Upgrade prometheus alertmanager version to v0.32.1. #7462
1617
* [ENHANCEMENT] Tenant Federation: Avoid purging the regex resolver LRU cache on user-sync ticks when the set of known users has not changed. #7489
1718
* [ENHANCEMENT] Memberlist: Add `-memberlist.packet-read-timeout`, `-memberlist.max-packet-size`, and `-memberlist.max-concurrent-connections` flags to bound inbound gossip TCP connections, preventing slow-read, OOM, and connection-flood attacks on the gossip port. #7518

pkg/cortex/modules.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import (
55
"flag"
66
"fmt"
77
"log/slog"
8+
"maps"
89
"net/http"
910
"runtime"
1011
"runtime/debug"
@@ -18,9 +19,11 @@ import (
1819
"github.com/prometheus/client_golang/prometheus"
1920
"github.com/prometheus/common/model"
2021
"github.com/prometheus/prometheus/promql"
22+
"github.com/prometheus/prometheus/promql/parser"
2123
"github.com/prometheus/prometheus/rules"
2224
prom_storage "github.com/prometheus/prometheus/storage"
2325
"github.com/thanos-io/objstore"
26+
"github.com/thanos-io/promql-engine/execution/parse"
2427
"github.com/thanos-io/thanos/pkg/discovery/dns"
2528
"github.com/thanos-io/thanos/pkg/querysharding"
2629
httpgrpc_server "github.com/weaveworks/common/httpgrpc/server"
@@ -661,6 +664,11 @@ func (t *Cortex) initRulerStorage() (serv services.Service, err error) {
661664
return
662665
}
663666

667+
// Register xfunctions (xincrease, xrate, xdelta) in the global parser
668+
if t.Cfg.Querier.ThanosEngine.EnableXFunctions {
669+
maps.Copy(parser.Functions, parse.XFunctions)
670+
}
671+
664672
t.RulerStorage, err = ruler.NewRuleStore(context.Background(), t.Cfg.RulerStorage, t.OverridesConfig, rules.FileLoader{}, util_log.Logger, prometheus.DefaultRegisterer, t.Cfg.NameValidationScheme)
665673
return
666674
}

pkg/cortex/modules_test.go

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package cortex
22

33
import (
44
"context"
5+
"maps"
56
"net/http/httptest"
67
"os"
78
"reflect"
@@ -10,6 +11,7 @@ import (
1011
"testing"
1112

1213
"github.com/gorilla/mux"
14+
"github.com/prometheus/prometheus/promql/parser"
1315
prom_storage "github.com/prometheus/prometheus/storage"
1416
"github.com/stretchr/testify/assert"
1517
"github.com/stretchr/testify/require"
@@ -169,6 +171,66 @@ func (p *myPusher) Push(ctx context.Context, req *cortexpb.WriteRequest) (*corte
169171
return nil, nil
170172
}
171173

174+
func TestCortex_InitRulerStorage_RegistersXFunctions(t *testing.T) {
175+
tests := map[string]struct {
176+
enableXFunctions bool
177+
expectRegistered bool
178+
}{
179+
"should register xfunctions when EnableXFunctions is true": {
180+
enableXFunctions: true,
181+
expectRegistered: true,
182+
},
183+
"should not register xfunctions when EnableXFunctions is false": {
184+
enableXFunctions: false,
185+
expectRegistered: false,
186+
},
187+
}
188+
189+
for testName, testData := range tests {
190+
t.Run(testName, func(t *testing.T) {
191+
// Clean up global state after each test
192+
originalFunctions := make(map[string]*parser.Function, len(parser.Functions))
193+
maps.Copy(originalFunctions, parser.Functions)
194+
defer func() {
195+
// Restore original parser.Functions
196+
for k := range parser.Functions {
197+
if _, ok := originalFunctions[k]; !ok {
198+
delete(parser.Functions, k)
199+
}
200+
}
201+
}()
202+
203+
cfg := newDefaultConfig()
204+
cfg.Target = []string{"ruler"}
205+
cfg.RulerStorage.Backend = "local"
206+
cfg.RulerStorage.Local.Directory = os.TempDir()
207+
cfg.Querier.ThanosEngine.EnableXFunctions = testData.enableXFunctions
208+
209+
cortex := &Cortex{
210+
Server: &server.Server{},
211+
Cfg: *cfg,
212+
}
213+
214+
_, err := cortex.initRulerStorage()
215+
require.NoError(t, err)
216+
217+
_, hasXincrease := parser.Functions["xincrease"]
218+
_, hasXrate := parser.Functions["xrate"]
219+
_, hasXdelta := parser.Functions["xdelta"]
220+
221+
if testData.expectRegistered {
222+
assert.True(t, hasXincrease, "xincrease should be registered")
223+
assert.True(t, hasXrate, "xrate should be registered")
224+
assert.True(t, hasXdelta, "xdelta should be registered")
225+
} else {
226+
assert.False(t, hasXincrease, "xincrease should not be registered")
227+
assert.False(t, hasXrate, "xrate should not be registered")
228+
assert.False(t, hasXdelta, "xdelta should not be registered")
229+
}
230+
})
231+
}
232+
}
233+
172234
type myQueryable struct{}
173235

174236
func (q *myQueryable) Querier(mint, maxt int64) (prom_storage.Querier, error) {

0 commit comments

Comments
 (0)