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  }