@@ -163,7 +163,7 @@ func TestRuler_ListRules(t *testing.T) {
163
163
store .setMissingRuleGroups (tc .missingRules )
164
164
165
165
r := prepareRuler (t , cfg , store , withStart ())
166
- a := NewAPI (r , r .directStore , log .NewNopLogger ())
166
+ a := NewAPI (r , r .store , log .NewNopLogger ())
167
167
168
168
router := mux .NewRouter ()
169
169
router .Path ("/prometheus/config/v1/rules" ).Methods ("GET" ).HandlerFunc (a .ListRules )
@@ -936,7 +936,7 @@ func TestRuler_PrometheusRules(t *testing.T) {
936
936
return len (rls .Groups )
937
937
})
938
938
939
- a := NewAPI (r , r .directStore , log .NewNopLogger ())
939
+ a := NewAPI (r , r .store , log .NewNopLogger ())
940
940
941
941
req := requestFor (t , http .MethodGet , "https://localhost:8080/prometheus/api/v1/rules" + tc .queryParams , nil , userID )
942
942
w := httptest .NewRecorder ()
@@ -993,7 +993,7 @@ func TestRuler_PrometheusAlerts(t *testing.T) {
993
993
return len (rls .Groups )
994
994
})
995
995
996
- a := NewAPI (r , r .directStore , log .NewNopLogger ())
996
+ a := NewAPI (r , r .store , log .NewNopLogger ())
997
997
998
998
req := requestFor (t , http .MethodGet , "https://localhost:8080/prometheus/api/v1/alerts" , nil , "user1" )
999
999
w := httptest .NewRecorder ()
@@ -1172,7 +1172,7 @@ rules:
1172
1172
1173
1173
reg := prometheus .NewPedanticRegistry ()
1174
1174
r := prepareRuler (t , rulerCfg , newMockRuleStore (make (map [string ]rulespb.RuleGroupList )), withStart (), withRulerAddrAutomaticMapping (), withPrometheusRegisterer (reg ))
1175
- a := NewAPI (r , r .directStore , log .NewNopLogger ())
1175
+ a := NewAPI (r , r .store , log .NewNopLogger ())
1176
1176
1177
1177
router := mux .NewRouter ()
1178
1178
router .Path ("/prometheus/config/v1/rules/{namespace}" ).Methods ("POST" ).HandlerFunc (a .CreateRuleGroup )
@@ -1207,6 +1207,92 @@ rules:
1207
1207
}
1208
1208
}
1209
1209
1210
+ func TestAPI_CreateRuleGroupWithCaching (t * testing.T ) {
1211
+ // Configure the ruler to only sync the rules based on notifications upon API changes.
1212
+ cfg := defaultRulerConfig (t )
1213
+ cfg .PollInterval = time .Hour
1214
+ cfg .OutboundSyncQueuePollInterval = 100 * time .Millisecond
1215
+ cfg .InboundSyncQueuePollInterval = 100 * time .Millisecond
1216
+
1217
+ const successResponse = `{"status":"success","data":null,"errorType":"","error":""}`
1218
+
1219
+ ruleGroupVersion1 := `name: group1
1220
+ interval: 15s
1221
+ rules:
1222
+ - record: up_rule
1223
+ expr: up
1224
+ - alert: up_alert
1225
+ expr: up < 1
1226
+ `
1227
+ ruleGroupVersion2 := `name: group1
1228
+ interval: 15s
1229
+ rules:
1230
+ - record: up_rule
1231
+ expr: up
1232
+ - alert: up_alert
1233
+ expr: up <= 1
1234
+ `
1235
+
1236
+ mockCache , store := newInMemoryRuleStore (t )
1237
+
1238
+ reg := prometheus .NewPedanticRegistry ()
1239
+ // Set rule group limits since this performs a list call to count the current number of rule groups
1240
+ // and we're testing if the API layer is correctly telling the rule store not to serve cached results.
1241
+ r := prepareRuler (t , cfg , store , withStart (), withRulerAddrAutomaticMapping (), withPrometheusRegisterer (reg ), withLimits (validation .MockOverrides (func (defaults * validation.Limits , _ map [string ]* validation.Limits ) {
1242
+ defaults .RulerMaxRuleGroupsPerTenant = 2
1243
+ defaults .RulerMaxRulesPerRuleGroup = 2
1244
+ })))
1245
+ a := NewAPI (r , r .store , log .NewNopLogger ())
1246
+
1247
+ router := mux .NewRouter ()
1248
+ router .Path ("/prometheus/config/v1/rules/{namespace}/{groupName}" ).Methods (http .MethodGet ).HandlerFunc (a .GetRuleGroup )
1249
+ router .Path ("/prometheus/config/v1/rules/{namespace}" ).Methods (http .MethodPost ).HandlerFunc (a .CreateRuleGroup )
1250
+
1251
+ // Pre-condition check: the ruler should have run the initial rules sync.
1252
+ verifySyncRulesMetric (t , reg , 1 , 0 )
1253
+
1254
+ // Store the initial version of the rule group
1255
+ req := requestFor (t , http .MethodPost , "https://localhost:8080/prometheus/config/v1/rules/namespace1" , strings .NewReader (ruleGroupVersion1 ), "user1" )
1256
+ w := httptest .NewRecorder ()
1257
+ router .ServeHTTP (w , req )
1258
+ assert .Equal (t , http .StatusAccepted , w .Code )
1259
+ assert .Equal (t , successResponse , w .Body .String ())
1260
+ // Invalidation of exists and content
1261
+ assert .Equal (t , 2 , mockCache .CountDeleteCalls ())
1262
+
1263
+ verifySyncRulesMetric (t , reg , 1 , 1 )
1264
+
1265
+ // Fetch it back and ensure the content is what we expect even though content can be cached
1266
+ req = requestFor (t , http .MethodGet , "https://localhost:8080/prometheus/config/v1/rules/namespace1/group1" , nil , "user1" )
1267
+ w = httptest .NewRecorder ()
1268
+ router .ServeHTTP (w , req )
1269
+ assert .Equal (t , http .StatusOK , w .Code )
1270
+ assert .Equal (t , ruleGroupVersion1 , w .Body .String ())
1271
+ // Iter from initial sync, get, iter from sync
1272
+ assert .Equal (t , 3 , mockCache .CountFetchCalls ())
1273
+
1274
+ // Store a new version of the group that is slightly different
1275
+ req = requestFor (t , http .MethodPost , "https://localhost:8080/prometheus/config/v1/rules/namespace1" , strings .NewReader (ruleGroupVersion2 ), "user1" )
1276
+ w = httptest .NewRecorder ()
1277
+ router .ServeHTTP (w , req )
1278
+ assert .Equal (t , http .StatusAccepted , w .Code )
1279
+ assert .Equal (t , successResponse , w .Body .String ())
1280
+ // Invalidating exists and content again
1281
+ assert .Equal (t , 4 , mockCache .CountDeleteCalls ())
1282
+
1283
+ verifySyncRulesMetric (t , reg , 1 , 2 )
1284
+
1285
+ // Fetch it back and ensure content is updated to the new version meaning the cache was invalidated
1286
+ req = requestFor (t , http .MethodGet , "https://localhost:8080/prometheus/config/v1/rules/namespace1/group1" , nil , "user1" )
1287
+ w = httptest .NewRecorder ()
1288
+ router .ServeHTTP (w , req )
1289
+ assert .Equal (t , http .StatusOK , w .Code )
1290
+ assert .Equal (t , ruleGroupVersion2 , w .Body .String ())
1291
+ // Iter from initial sync, get, iter from sync, another get, iter from sync
1292
+ assert .Equal (t , 5 , mockCache .CountFetchCalls ())
1293
+
1294
+ }
1295
+
1210
1296
func TestAPI_DeleteNamespace (t * testing.T ) {
1211
1297
// Configure the ruler to only sync the rules based on notifications upon API changes.
1212
1298
cfg := defaultRulerConfig (t )
@@ -1237,7 +1323,7 @@ func TestAPI_DeleteNamespace(t *testing.T) {
1237
1323
1238
1324
reg := prometheus .NewPedanticRegistry ()
1239
1325
r := prepareRuler (t , cfg , newMockRuleStore (mockRulesNamespaces ), withStart (), withRulerAddrAutomaticMapping (), withPrometheusRegisterer (reg ))
1240
- a := NewAPI (r , r .directStore , log .NewNopLogger ())
1326
+ a := NewAPI (r , r .store , log .NewNopLogger ())
1241
1327
1242
1328
router := mux .NewRouter ()
1243
1329
router .Path ("/prometheus/config/v1/rules/{namespace}" ).Methods (http .MethodDelete ).HandlerFunc (a .DeleteNamespace )
@@ -1294,7 +1380,7 @@ func TestAPI_DeleteRuleGroup(t *testing.T) {
1294
1380
1295
1381
reg := prometheus .NewPedanticRegistry ()
1296
1382
r := prepareRuler (t , cfg , newMockRuleStore (mockRulesNamespaces ), withStart (), withRulerAddrAutomaticMapping (), withPrometheusRegisterer (reg ))
1297
- a := NewAPI (r , r .directStore , log .NewNopLogger ())
1383
+ a := NewAPI (r , r .store , log .NewNopLogger ())
1298
1384
1299
1385
router := mux .NewRouter ()
1300
1386
router .Path ("/prometheus/config/v1/rules/{namespace}/{groupName}" ).Methods (http .MethodDelete ).HandlerFunc (a .DeleteRuleGroup )
@@ -1336,7 +1422,7 @@ func TestRuler_LimitsPerGroup(t *testing.T) {
1336
1422
defaults .RulerMaxRulesPerRuleGroup = 1
1337
1423
})))
1338
1424
1339
- a := NewAPI (r , r .directStore , log .NewNopLogger ())
1425
+ a := NewAPI (r , r .store , log .NewNopLogger ())
1340
1426
1341
1427
tc := []struct {
1342
1428
name string
@@ -1389,7 +1475,7 @@ func TestRuler_RulerGroupLimits(t *testing.T) {
1389
1475
defaults .RulerMaxRulesPerRuleGroup = 1
1390
1476
})))
1391
1477
1392
- a := NewAPI (r , r .directStore , log .NewNopLogger ())
1478
+ a := NewAPI (r , r .store , log .NewNopLogger ())
1393
1479
1394
1480
tc := []struct {
1395
1481
name string
@@ -1449,7 +1535,7 @@ func TestRuler_RulerGroupLimitsDisabled(t *testing.T) {
1449
1535
defaults .RulerMaxRulesPerRuleGroup = 0
1450
1536
})))
1451
1537
1452
- a := NewAPI (r , r .directStore , log .NewNopLogger ())
1538
+ a := NewAPI (r , r .store , log .NewNopLogger ())
1453
1539
1454
1540
tc := []struct {
1455
1541
name string
@@ -1551,7 +1637,7 @@ func TestAPIRoutesCorrectlyHandleInvalidOrgID(t *testing.T) {
1551
1637
1552
1638
r := prepareRuler (t , cfg , newMockRuleStore (map [string ]rulespb.RuleGroupList {}), withStart ())
1553
1639
1554
- a := NewAPI (r , r .directStore , log .NewNopLogger ())
1640
+ a := NewAPI (r , r .store , log .NewNopLogger ())
1555
1641
1556
1642
router := mux .NewRouter ()
1557
1643
router .Path ("/api/v1/rules" ).Methods (http .MethodGet ).HandlerFunc (a .PrometheusRules )
0 commit comments