github.com/muhammadn/cortex@v1.9.1-0.20220510110439-46bb7000d03d/pkg/configs/legacy_promql/engine_test.go (about) 1 // Copyright 2016 The Prometheus Authors 2 // Licensed under the Apache License, Version 2.0 (the "License"); 3 // you may not use this file except in compliance with the License. 4 // You may obtain a copy of the License at 5 // 6 // http://www.apache.org/licenses/LICENSE-2.0 7 // 8 // Unless required by applicable law or agreed to in writing, software 9 // distributed under the License is distributed on an "AS IS" BASIS, 10 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 // See the License for the specific language governing permissions and 12 // limitations under the License. 13 14 package promql 15 16 import ( 17 "context" 18 "fmt" 19 "reflect" 20 "testing" 21 "time" 22 23 "github.com/go-kit/log" 24 "github.com/prometheus/prometheus/pkg/labels" 25 "github.com/prometheus/prometheus/storage" 26 ) 27 28 func TestQueryConcurrency(t *testing.T) { 29 concurrentQueries := 10 30 31 engine := NewEngine(nil, nil, concurrentQueries, 10*time.Second) 32 ctx, cancelCtx := context.WithCancel(context.Background()) 33 defer cancelCtx() 34 35 block := make(chan struct{}) 36 processing := make(chan struct{}) 37 38 f := func(context.Context) error { 39 processing <- struct{}{} 40 <-block 41 return nil 42 } 43 44 for i := 0; i < concurrentQueries; i++ { 45 q := engine.newTestQuery(f) 46 go q.Exec(ctx) 47 select { 48 case <-processing: 49 // Expected. 50 case <-time.After(20 * time.Millisecond): 51 t.Fatalf("Query within concurrency threshold not being executed") 52 } 53 } 54 55 q := engine.newTestQuery(f) 56 go q.Exec(ctx) 57 58 select { 59 case <-processing: 60 t.Fatalf("Query above concurrency threshold being executed") 61 case <-time.After(20 * time.Millisecond): 62 // Expected. 63 } 64 65 // Terminate a running query. 66 block <- struct{}{} 67 68 select { 69 case <-processing: 70 // Expected. 71 case <-time.After(20 * time.Millisecond): 72 t.Fatalf("Query within concurrency threshold not being executed") 73 } 74 75 // Terminate remaining queries. 76 for i := 0; i < concurrentQueries; i++ { 77 block <- struct{}{} 78 } 79 } 80 81 func TestQueryCancel(t *testing.T) { 82 engine := NewEngine(nil, nil, 10, 10*time.Second) 83 ctx, cancelCtx := context.WithCancel(context.Background()) 84 defer cancelCtx() 85 86 // Cancel a running query before it completes. 87 block := make(chan struct{}) 88 processing := make(chan struct{}) 89 90 query1 := engine.newTestQuery(func(ctx context.Context) error { 91 processing <- struct{}{} 92 <-block 93 return contextDone(ctx, "test statement execution") 94 }) 95 96 var res *Result 97 98 go func() { 99 res = query1.Exec(ctx) 100 processing <- struct{}{} 101 }() 102 103 <-processing 104 query1.Cancel() 105 block <- struct{}{} 106 <-processing 107 108 if res.Err == nil { 109 t.Fatalf("expected cancellation error for query1 but got none") 110 } 111 if ee := ErrQueryCanceled("test statement execution"); res.Err != ee { 112 t.Fatalf("expected error %q, got %q", ee, res.Err) 113 } 114 115 // Canceling a query before starting it must have no effect. 116 query2 := engine.newTestQuery(func(ctx context.Context) error { 117 return contextDone(ctx, "test statement execution") 118 }) 119 120 query2.Cancel() 121 res = query2.Exec(ctx) 122 if res.Err != nil { 123 t.Fatalf("unexpected error on executing query2: %s", res.Err) 124 } 125 } 126 127 // errQuerier implements storage.Querier which always returns error. 128 type errQuerier struct { 129 err error 130 } 131 132 func (q *errQuerier) Select(bool, *storage.SelectHints, ...*labels.Matcher) storage.SeriesSet { 133 return storage.ErrSeriesSet(q.err) 134 } 135 func (q *errQuerier) LabelValues(name string, matchers ...*labels.Matcher) ([]string, storage.Warnings, error) { 136 return nil, nil, q.err 137 } 138 func (q *errQuerier) LabelNames(matchers ...*labels.Matcher) ([]string, storage.Warnings, error) { 139 return nil, nil, q.err 140 } 141 func (q *errQuerier) Close() error { return q.err } 142 143 func TestQueryError(t *testing.T) { 144 engine := NewEngine(nil, nil, 10, 10*time.Second) 145 errStorage := ErrStorage(fmt.Errorf("storage error")) 146 queryable := storage.QueryableFunc(func(ctx context.Context, mint, maxt int64) (storage.Querier, error) { 147 return &errQuerier{err: errStorage}, nil 148 }) 149 ctx, cancelCtx := context.WithCancel(context.Background()) 150 defer cancelCtx() 151 152 vectorQuery, err := engine.NewInstantQuery(queryable, "foo", time.Unix(1, 0)) 153 if err != nil { 154 t.Fatalf("unexpected error creating query: %q", err) 155 } 156 res := vectorQuery.Exec(ctx) 157 if res.Err == nil { 158 t.Fatalf("expected error on failed select but got none") 159 } 160 if res.Err != errStorage { 161 t.Fatalf("expected error %q, got %q", errStorage, res.Err) 162 } 163 164 matrixQuery, err := engine.NewInstantQuery(queryable, "foo[1m]", time.Unix(1, 0)) 165 if err != nil { 166 t.Fatalf("unexpected error creating query: %q", err) 167 } 168 res = matrixQuery.Exec(ctx) 169 if res.Err == nil { 170 t.Fatalf("expected error on failed select but got none") 171 } 172 if res.Err != errStorage { 173 t.Fatalf("expected error %q, got %q", errStorage, res.Err) 174 } 175 } 176 177 func TestEngineShutdown(t *testing.T) { 178 engine := NewEngine(nil, nil, 10, 10*time.Second) 179 ctx, cancelCtx := context.WithCancel(context.Background()) 180 181 block := make(chan struct{}) 182 processing := make(chan struct{}) 183 184 // Shutdown engine on first handler execution. Should handler execution ever become 185 // concurrent this test has to be adjusted accordingly. 186 f := func(ctx context.Context) error { 187 processing <- struct{}{} 188 <-block 189 return contextDone(ctx, "test statement execution") 190 } 191 query1 := engine.newTestQuery(f) 192 193 // Stopping the engine must cancel the base context. While executing queries is 194 // still possible, their context is canceled from the beginning and execution should 195 // terminate immediately. 196 197 var res *Result 198 go func() { 199 res = query1.Exec(ctx) 200 processing <- struct{}{} 201 }() 202 203 <-processing 204 cancelCtx() 205 block <- struct{}{} 206 <-processing 207 208 if res.Err == nil { 209 t.Fatalf("expected error on shutdown during query but got none") 210 } 211 if ee := ErrQueryCanceled("test statement execution"); res.Err != ee { 212 t.Fatalf("expected error %q, got %q", ee, res.Err) 213 } 214 215 query2 := engine.newTestQuery(func(context.Context) error { 216 t.Fatalf("reached query execution unexpectedly") 217 return nil 218 }) 219 220 // The second query is started after the engine shut down. It must 221 // be canceled immediately. 222 res2 := query2.Exec(ctx) 223 if res2.Err == nil { 224 t.Fatalf("expected error on querying with canceled context but got none") 225 } 226 if _, ok := res2.Err.(ErrQueryCanceled); !ok { 227 t.Fatalf("expected cancellation error, got %q", res2.Err) 228 } 229 } 230 231 func TestEngineEvalStmtTimestamps(t *testing.T) { 232 test, err := NewTest(t, ` 233 load 10s 234 metric 1 2 235 `) 236 if err != nil { 237 t.Fatalf("unexpected error creating test: %q", err) 238 } 239 defer test.Close() 240 241 err = test.Run() 242 if err != nil { 243 t.Fatalf("unexpected error initializing test: %q", err) 244 } 245 246 cases := []struct { 247 Query string 248 Result Value 249 Start time.Time 250 End time.Time 251 Interval time.Duration 252 }{ 253 // Instant queries. 254 { 255 Query: "1", 256 Result: Scalar{V: 1, T: 1000}, 257 Start: time.Unix(1, 0), 258 }, 259 { 260 Query: "metric", 261 Result: Vector{ 262 Sample{Point: Point{V: 1, T: 1000}, 263 Metric: labels.FromStrings("__name__", "metric")}, 264 }, 265 Start: time.Unix(1, 0), 266 }, 267 { 268 Query: "metric[20s]", 269 Result: Matrix{Series{ 270 Points: []Point{{V: 1, T: 0}, {V: 2, T: 10000}}, 271 Metric: labels.FromStrings("__name__", "metric")}, 272 }, 273 Start: time.Unix(10, 0), 274 }, 275 // Range queries. 276 { 277 Query: "1", 278 Result: Matrix{Series{ 279 Points: []Point{{V: 1, T: 0}, {V: 1, T: 1000}, {V: 1, T: 2000}}, 280 Metric: labels.FromStrings()}, 281 }, 282 Start: time.Unix(0, 0), 283 End: time.Unix(2, 0), 284 Interval: time.Second, 285 }, 286 { 287 Query: "metric", 288 Result: Matrix{Series{ 289 Points: []Point{{V: 1, T: 0}, {V: 1, T: 1000}, {V: 1, T: 2000}}, 290 Metric: labels.FromStrings("__name__", "metric")}, 291 }, 292 Start: time.Unix(0, 0), 293 End: time.Unix(2, 0), 294 Interval: time.Second, 295 }, 296 { 297 Query: "metric", 298 Result: Matrix{Series{ 299 Points: []Point{{V: 1, T: 0}, {V: 1, T: 5000}, {V: 2, T: 10000}}, 300 Metric: labels.FromStrings("__name__", "metric")}, 301 }, 302 Start: time.Unix(0, 0), 303 End: time.Unix(10, 0), 304 Interval: 5 * time.Second, 305 }, 306 } 307 308 for _, c := range cases { 309 var err error 310 var qry Query 311 if c.Interval == 0 { 312 qry, err = test.QueryEngine().NewInstantQuery(test.Queryable(), c.Query, c.Start) 313 } else { 314 qry, err = test.QueryEngine().NewRangeQuery(test.Queryable(), c.Query, c.Start, c.End, c.Interval) 315 } 316 if err != nil { 317 t.Fatalf("unexpected error creating query: %q", err) 318 } 319 res := qry.Exec(test.Context()) 320 if res.Err != nil { 321 t.Fatalf("unexpected error running query: %q", res.Err) 322 } 323 if !reflect.DeepEqual(res.Value, c.Result) { 324 t.Fatalf("unexpected result for query %q: got %q wanted %q", c.Query, res.Value.String(), c.Result.String()) 325 } 326 } 327 328 } 329 330 func TestRecoverEvaluatorRuntime(t *testing.T) { 331 ev := &evaluator{logger: log.NewNopLogger()} 332 333 var err error 334 defer ev.recover(&err) 335 336 // Cause a runtime panic. 337 var a []int 338 a[123] = 1 339 340 if err.Error() != "unexpected error" { 341 t.Fatalf("wrong error message: %q, expected %q", err, "unexpected error") 342 } 343 } 344 345 func TestRecoverEvaluatorError(t *testing.T) { 346 ev := &evaluator{logger: log.NewNopLogger()} 347 var err error 348 349 e := fmt.Errorf("custom error") 350 351 defer func() { 352 if err.Error() != e.Error() { 353 t.Fatalf("wrong error message: %q, expected %q", err, e) 354 } 355 }() 356 defer ev.recover(&err) 357 358 panic(e) 359 }