github.com/yankunsam/loki/v2@v2.6.3-0.20220817130409-389df5235c27/pkg/querier/querier_test.go (about) 1 package querier 2 3 import ( 4 "context" 5 "errors" 6 "io" 7 "net/http" 8 "testing" 9 "time" 10 11 "github.com/grafana/dskit/flagext" 12 "github.com/grafana/dskit/ring" 13 ring_client "github.com/grafana/dskit/ring/client" 14 "github.com/prometheus/common/model" 15 "github.com/stretchr/testify/assert" 16 "github.com/stretchr/testify/mock" 17 "github.com/stretchr/testify/require" 18 "github.com/weaveworks/common/httpgrpc" 19 "github.com/weaveworks/common/user" 20 21 "github.com/grafana/loki/pkg/ingester/client" 22 "github.com/grafana/loki/pkg/logproto" 23 "github.com/grafana/loki/pkg/logql" 24 "github.com/grafana/loki/pkg/storage" 25 "github.com/grafana/loki/pkg/storage/stores/indexshipper/compactor/deletion" 26 "github.com/grafana/loki/pkg/validation" 27 ) 28 29 const ( 30 // Custom query timeout used in tests 31 queryTimeout = 12 * time.Second 32 ) 33 34 func TestQuerier_Label_QueryTimeoutConfigFlag(t *testing.T) { 35 startTime := time.Now().Add(-1 * time.Minute) 36 endTime := time.Now() 37 38 request := logproto.LabelRequest{ 39 Name: "test", 40 Values: true, 41 Start: &startTime, 42 End: &endTime, 43 } 44 45 ingesterClient := newQuerierClientMock() 46 ingesterClient.On("Label", mock.Anything, &request, mock.Anything).Return(mockLabelResponse([]string{}), nil) 47 48 store := newStoreMock() 49 store.On("LabelValuesForMetricName", mock.Anything, "test", model.TimeFromUnixNano(startTime.UnixNano()), model.TimeFromUnixNano(endTime.UnixNano()), "logs", "test").Return([]string{"foo", "bar"}, nil) 50 51 limits, err := validation.NewOverrides(defaultLimitsTestConfig(), nil) 52 require.NoError(t, err) 53 54 q, err := newQuerier( 55 mockQuerierConfig(), 56 mockIngesterClientConfig(), 57 newIngesterClientMockFactory(ingesterClient), 58 mockReadRingWithOneActiveIngester(), 59 &mockDeleteGettter{}, 60 store, limits) 61 require.NoError(t, err) 62 63 ctx := user.InjectOrgID(context.Background(), "test") 64 _, err = q.Label(ctx, &request) 65 require.NoError(t, err) 66 67 calls := ingesterClient.GetMockedCallsByMethod("Label") 68 assert.Equal(t, 1, len(calls)) 69 deadline, ok := calls[0].Arguments.Get(0).(context.Context).Deadline() 70 assert.True(t, ok) 71 assert.WithinDuration(t, deadline, time.Now().Add(queryTimeout), 1*time.Second) 72 73 calls = store.GetMockedCallsByMethod("LabelValuesForMetricName") 74 assert.Equal(t, 1, len(calls)) 75 deadline, ok = calls[0].Arguments.Get(0).(context.Context).Deadline() 76 assert.True(t, ok) 77 assert.WithinDuration(t, deadline, time.Now().Add(queryTimeout), 1*time.Second) 78 79 store.AssertExpectations(t) 80 } 81 82 func TestQuerier_Tail_QueryTimeoutConfigFlag(t *testing.T) { 83 request := logproto.TailRequest{ 84 Query: "{type=\"test\"}", 85 DelayFor: 0, 86 Limit: 10, 87 Start: time.Now(), 88 } 89 90 store := newStoreMock() 91 store.On("SelectLogs", mock.Anything, mock.Anything).Return(mockStreamIterator(1, 2), nil) 92 93 queryClient := newQueryClientMock() 94 queryClient.On("Recv").Return(mockQueryResponse([]logproto.Stream{mockStream(1, 2)}), nil) 95 96 tailClient := newTailClientMock() 97 tailClient.On("Recv").Return(mockTailResponse(mockStream(1, 2)), nil) 98 99 ingesterClient := newQuerierClientMock() 100 ingesterClient.On("Query", mock.Anything, mock.Anything, mock.Anything).Return(queryClient, nil) 101 ingesterClient.On("Tail", mock.Anything, &request, mock.Anything).Return(tailClient, nil) 102 ingesterClient.On("TailersCount", mock.Anything, mock.Anything, mock.Anything).Return(&logproto.TailersCountResponse{}, nil) 103 104 limits, err := validation.NewOverrides(defaultLimitsTestConfig(), nil) 105 require.NoError(t, err) 106 107 q, err := newQuerier( 108 mockQuerierConfig(), 109 mockIngesterClientConfig(), 110 newIngesterClientMockFactory(ingesterClient), 111 mockReadRingWithOneActiveIngester(), 112 &mockDeleteGettter{}, 113 store, limits) 114 require.NoError(t, err) 115 116 ctx := user.InjectOrgID(context.Background(), "test") 117 _, err = q.Tail(ctx, &request) 118 require.NoError(t, err) 119 120 calls := ingesterClient.GetMockedCallsByMethod("Query") 121 assert.Equal(t, 1, len(calls)) 122 deadline, ok := calls[0].Arguments.Get(0).(context.Context).Deadline() 123 assert.True(t, ok) 124 assert.WithinDuration(t, deadline, time.Now().Add(queryTimeout), 1*time.Second) 125 126 calls = ingesterClient.GetMockedCallsByMethod("Tail") 127 assert.Equal(t, 1, len(calls)) 128 _, ok = calls[0].Arguments.Get(0).(context.Context).Deadline() 129 assert.False(t, ok) 130 131 calls = store.GetMockedCallsByMethod("SelectLogs") 132 assert.Equal(t, 1, len(calls)) 133 deadline, ok = calls[0].Arguments.Get(0).(context.Context).Deadline() 134 assert.True(t, ok) 135 assert.WithinDuration(t, deadline, time.Now().Add(queryTimeout), 1*time.Second) 136 137 store.AssertExpectations(t) 138 } 139 140 func mockQuerierConfig() Config { 141 return Config{ 142 TailMaxDuration: 1 * time.Minute, 143 QueryTimeout: queryTimeout, 144 } 145 } 146 147 func mockQueryResponse(streams []logproto.Stream) *logproto.QueryResponse { 148 return &logproto.QueryResponse{ 149 Streams: streams, 150 } 151 } 152 153 func mockLabelResponse(values []string) *logproto.LabelResponse { 154 return &logproto.LabelResponse{ 155 Values: values, 156 } 157 } 158 159 func defaultLimitsTestConfig() validation.Limits { 160 limits := validation.Limits{} 161 flagext.DefaultValues(&limits) 162 return limits 163 } 164 165 func TestQuerier_validateQueryRequest(t *testing.T) { 166 request := logproto.QueryRequest{ 167 Selector: "{type=\"test\", fail=\"yes\"} |= \"foo\"", 168 Limit: 10, 169 Start: time.Now().Add(-1 * time.Minute), 170 End: time.Now(), 171 Direction: logproto.FORWARD, 172 } 173 174 store := newStoreMock() 175 store.On("SelectLogs", mock.Anything, mock.Anything).Return(mockStreamIterator(1, 2), nil) 176 177 queryClient := newQueryClientMock() 178 queryClient.On("Recv").Return(mockQueryResponse([]logproto.Stream{mockStream(1, 2)}), nil) 179 180 ingesterClient := newQuerierClientMock() 181 ingesterClient.On("Query", mock.Anything, &request, mock.Anything).Return(queryClient, nil) 182 183 defaultLimits := defaultLimitsTestConfig() 184 defaultLimits.MaxStreamsMatchersPerQuery = 1 185 defaultLimits.MaxQueryLength = model.Duration(2 * time.Minute) 186 187 limits, err := validation.NewOverrides(defaultLimits, nil) 188 require.NoError(t, err) 189 190 q, err := newQuerier( 191 mockQuerierConfig(), 192 mockIngesterClientConfig(), 193 newIngesterClientMockFactory(ingesterClient), 194 mockReadRingWithOneActiveIngester(), 195 &mockDeleteGettter{}, 196 store, limits) 197 require.NoError(t, err) 198 199 ctx := user.InjectOrgID(context.Background(), "test") 200 201 _, err = q.SelectLogs(ctx, logql.SelectLogParams{QueryRequest: &request}) 202 require.Equal(t, httpgrpc.Errorf(http.StatusBadRequest, "max streams matchers per query exceeded, matchers-count > limit (2 > 1)"), err) 203 204 request.Selector = "{type=\"test\"}" 205 _, err = q.SelectLogs(ctx, logql.SelectLogParams{QueryRequest: &request}) 206 require.NoError(t, err) 207 208 request.Start = request.End.Add(-3 * time.Minute) 209 _, err = q.SelectLogs(ctx, logql.SelectLogParams{QueryRequest: &request}) 210 require.Equal(t, httpgrpc.Errorf(http.StatusBadRequest, "the query time range exceeds the limit (query length: 3m0s, limit: 2m0s)"), err) 211 } 212 213 func TestQuerier_SeriesAPI(t *testing.T) { 214 mkReq := func(groups []string) *logproto.SeriesRequest { 215 return &logproto.SeriesRequest{ 216 Start: time.Unix(0, 0), 217 End: time.Unix(10, 0), 218 Groups: groups, 219 } 220 } 221 222 mockSeriesResponse := func(series []map[string]string) *logproto.SeriesResponse { 223 resp := &logproto.SeriesResponse{} 224 for _, s := range series { 225 resp.Series = append(resp.Series, logproto.SeriesIdentifier{ 226 Labels: s, 227 }) 228 } 229 return resp 230 } 231 232 for _, tc := range []struct { 233 desc string 234 req *logproto.SeriesRequest 235 setup func(*storeMock, *queryClientMock, *querierClientMock, validation.Limits, *logproto.SeriesRequest) 236 run func(*testing.T, *SingleTenantQuerier, *logproto.SeriesRequest) 237 }{ 238 { 239 "ingester error", 240 mkReq([]string{`{a="1"}`}), 241 func(store *storeMock, querier *queryClientMock, ingester *querierClientMock, limits validation.Limits, req *logproto.SeriesRequest) { 242 ingester.On("Series", mock.Anything, req, mock.Anything).Return(nil, errors.New("tst-err")) 243 244 store.On("Series", mock.Anything, mock.Anything).Return(nil, nil) 245 }, 246 func(t *testing.T, q *SingleTenantQuerier, req *logproto.SeriesRequest) { 247 ctx := user.InjectOrgID(context.Background(), "test") 248 _, err := q.Series(ctx, req) 249 require.Error(t, err) 250 }, 251 }, 252 { 253 "store error", 254 mkReq([]string{`{a="1"}`}), 255 func(store *storeMock, querier *queryClientMock, ingester *querierClientMock, limits validation.Limits, req *logproto.SeriesRequest) { 256 ingester.On("Series", mock.Anything, req, mock.Anything).Return(mockSeriesResponse([]map[string]string{ 257 {"a": "1"}, 258 }), nil) 259 260 store.On("Series", mock.Anything, mock.Anything).Return(nil, context.DeadlineExceeded) 261 }, 262 func(t *testing.T, q *SingleTenantQuerier, req *logproto.SeriesRequest) { 263 ctx := user.InjectOrgID(context.Background(), "test") 264 _, err := q.Series(ctx, req) 265 require.Error(t, err) 266 }, 267 }, 268 { 269 "no matches", 270 mkReq([]string{`{a="1"}`}), 271 func(store *storeMock, querier *queryClientMock, ingester *querierClientMock, limits validation.Limits, req *logproto.SeriesRequest) { 272 ingester.On("Series", mock.Anything, req, mock.Anything).Return(mockSeriesResponse(nil), nil) 273 store.On("Series", mock.Anything, mock.Anything).Return(nil, nil) 274 }, 275 func(t *testing.T, q *SingleTenantQuerier, req *logproto.SeriesRequest) { 276 ctx := user.InjectOrgID(context.Background(), "test") 277 resp, err := q.Series(ctx, req) 278 require.Nil(t, err) 279 require.Equal(t, &logproto.SeriesResponse{Series: make([]logproto.SeriesIdentifier, 0)}, resp) 280 }, 281 }, 282 { 283 "returns series", 284 mkReq([]string{`{a="1"}`}), 285 func(store *storeMock, querier *queryClientMock, ingester *querierClientMock, limits validation.Limits, req *logproto.SeriesRequest) { 286 ingester.On("Series", mock.Anything, req, mock.Anything).Return(mockSeriesResponse([]map[string]string{ 287 {"a": "1", "b": "2"}, 288 {"a": "1", "b": "3"}, 289 }), nil) 290 291 store.On("Series", mock.Anything, mock.Anything).Return([]logproto.SeriesIdentifier{ 292 {Labels: map[string]string{"a": "1", "b": "4"}}, 293 {Labels: map[string]string{"a": "1", "b": "5"}}, 294 }, nil) 295 }, 296 func(t *testing.T, q *SingleTenantQuerier, req *logproto.SeriesRequest) { 297 ctx := user.InjectOrgID(context.Background(), "test") 298 resp, err := q.Series(ctx, req) 299 require.Nil(t, err) 300 require.ElementsMatch(t, []logproto.SeriesIdentifier{ 301 {Labels: map[string]string{"a": "1", "b": "2"}}, 302 {Labels: map[string]string{"a": "1", "b": "3"}}, 303 {Labels: map[string]string{"a": "1", "b": "4"}}, 304 {Labels: map[string]string{"a": "1", "b": "5"}}, 305 }, resp.GetSeries()) 306 }, 307 }, 308 { 309 "dedupes", 310 mkReq([]string{`{a="1"}`}), 311 func(store *storeMock, querier *queryClientMock, ingester *querierClientMock, limits validation.Limits, req *logproto.SeriesRequest) { 312 ingester.On("Series", mock.Anything, req, mock.Anything).Return(mockSeriesResponse([]map[string]string{ 313 {"a": "1", "b": "2"}, 314 }), nil) 315 316 store.On("Series", mock.Anything, mock.Anything).Return([]logproto.SeriesIdentifier{ 317 {Labels: map[string]string{"a": "1", "b": "2"}}, 318 {Labels: map[string]string{"a": "1", "b": "3"}}, 319 }, nil) 320 }, 321 func(t *testing.T, q *SingleTenantQuerier, req *logproto.SeriesRequest) { 322 ctx := user.InjectOrgID(context.Background(), "test") 323 resp, err := q.Series(ctx, req) 324 require.Nil(t, err) 325 require.ElementsMatch(t, []logproto.SeriesIdentifier{ 326 {Labels: map[string]string{"a": "1", "b": "2"}}, 327 {Labels: map[string]string{"a": "1", "b": "3"}}, 328 }, resp.GetSeries()) 329 }, 330 }, 331 } { 332 t.Run(tc.desc, func(t *testing.T) { 333 store := newStoreMock() 334 queryClient := newQueryClientMock() 335 ingesterClient := newQuerierClientMock() 336 defaultLimits := defaultLimitsTestConfig() 337 if tc.setup != nil { 338 tc.setup(store, queryClient, ingesterClient, defaultLimits, tc.req) 339 } 340 341 limits, err := validation.NewOverrides(defaultLimits, nil) 342 require.NoError(t, err) 343 344 q, err := newQuerier( 345 mockQuerierConfig(), 346 mockIngesterClientConfig(), 347 newIngesterClientMockFactory(ingesterClient), 348 mockReadRingWithOneActiveIngester(), 349 &mockDeleteGettter{}, 350 store, limits) 351 require.NoError(t, err) 352 353 tc.run(t, q, tc.req) 354 }) 355 } 356 } 357 358 func TestQuerier_IngesterMaxQueryLookback(t *testing.T) { 359 limits, err := validation.NewOverrides(defaultLimitsTestConfig(), nil) 360 require.NoError(t, err) 361 362 for _, tc := range []struct { 363 desc string 364 lookback time.Duration 365 end time.Time 366 skipIngesters bool 367 }{ 368 { 369 desc: "0 value always queries ingesters", 370 lookback: 0, 371 end: time.Now().Add(time.Hour), 372 skipIngesters: false, 373 }, 374 { 375 desc: "query ingester", 376 lookback: time.Hour, 377 end: time.Now(), 378 skipIngesters: false, 379 }, 380 { 381 desc: "skip ingester", 382 lookback: time.Hour, 383 end: time.Now().Add(-2 * time.Hour), 384 skipIngesters: true, 385 }, 386 } { 387 t.Run(tc.desc, func(t *testing.T) { 388 req := logproto.QueryRequest{ 389 Selector: `{app="foo"}`, 390 Limit: 1000, 391 Start: tc.end.Add(-6 * time.Hour), 392 End: tc.end, 393 Direction: logproto.FORWARD, 394 } 395 396 queryClient := newQueryClientMock() 397 ingesterClient := newQuerierClientMock() 398 399 if !tc.skipIngesters { 400 ingesterClient.On("Query", mock.Anything, mock.Anything, mock.Anything).Return(queryClient, nil) 401 queryClient.On("Recv").Return(mockQueryResponse([]logproto.Stream{mockStream(1, 1)}), nil).Once() 402 queryClient.On("Recv").Return(nil, io.EOF).Once() 403 } 404 405 store := newStoreMock() 406 store.On("SelectLogs", mock.Anything, mock.Anything).Return(mockStreamIterator(0, 1), nil) 407 408 conf := mockQuerierConfig() 409 conf.QueryIngestersWithin = tc.lookback 410 q, err := newQuerier( 411 conf, 412 mockIngesterClientConfig(), 413 newIngesterClientMockFactory(ingesterClient), 414 mockReadRingWithOneActiveIngester(), 415 &mockDeleteGettter{}, 416 store, limits) 417 require.NoError(t, err) 418 419 ctx := user.InjectOrgID(context.Background(), "test") 420 421 res, err := q.SelectLogs(ctx, logql.SelectLogParams{QueryRequest: &req}) 422 require.Nil(t, err) 423 424 // since streams are loaded lazily, force iterators to exhaust 425 for res.Next() { 426 } 427 queryClient.AssertExpectations(t) 428 ingesterClient.AssertExpectations(t) 429 store.AssertExpectations(t) 430 }) 431 } 432 } 433 434 func TestQuerier_concurrentTailLimits(t *testing.T) { 435 request := logproto.TailRequest{ 436 Query: "{type=\"test\"}", 437 DelayFor: 0, 438 Limit: 10, 439 Start: time.Now(), 440 } 441 442 t.Parallel() 443 444 tests := map[string]struct { 445 ringIngesters []ring.InstanceDesc 446 expectedError error 447 tailersCount uint32 448 }{ 449 "empty ring": { 450 ringIngesters: []ring.InstanceDesc{}, 451 expectedError: httpgrpc.Errorf(http.StatusInternalServerError, "no active ingester found"), 452 }, 453 "ring containing one pending ingester": { 454 ringIngesters: []ring.InstanceDesc{mockInstanceDesc("1.1.1.1", ring.PENDING)}, 455 expectedError: httpgrpc.Errorf(http.StatusInternalServerError, "no active ingester found"), 456 }, 457 "ring containing one active ingester and 0 active tailers": { 458 ringIngesters: []ring.InstanceDesc{mockInstanceDesc("1.1.1.1", ring.ACTIVE)}, 459 }, 460 "ring containing one active ingester and 1 active tailer": { 461 ringIngesters: []ring.InstanceDesc{mockInstanceDesc("1.1.1.1", ring.ACTIVE)}, 462 tailersCount: 1, 463 }, 464 "ring containing one pending and active ingester with 1 active tailer": { 465 ringIngesters: []ring.InstanceDesc{mockInstanceDesc("1.1.1.1", ring.PENDING), mockInstanceDesc("2.2.2.2", ring.ACTIVE)}, 466 tailersCount: 1, 467 }, 468 "ring containing one active ingester and max active tailers": { 469 ringIngesters: []ring.InstanceDesc{mockInstanceDesc("1.1.1.1", ring.ACTIVE)}, 470 expectedError: httpgrpc.Errorf(http.StatusBadRequest, 471 "max concurrent tail requests limit exceeded, count > limit (%d > %d)", 6, 5), 472 tailersCount: 5, 473 }, 474 } 475 476 for testName, testData := range tests { 477 testData := testData 478 479 t.Run(testName, func(t *testing.T) { 480 // For this test's purpose, whenever a new ingester client needs to 481 // be created, the factory will always return the same mock instance 482 store := newStoreMock() 483 store.On("SelectLogs", mock.Anything, mock.Anything).Return(mockStreamIterator(1, 2), nil) 484 485 queryClient := newQueryClientMock() 486 queryClient.On("Recv").Return(mockQueryResponse([]logproto.Stream{mockStream(1, 2)}), nil) 487 488 tailClient := newTailClientMock() 489 tailClient.On("Recv").Return(mockTailResponse(mockStream(1, 2)), nil) 490 491 ingesterClient := newQuerierClientMock() 492 ingesterClient.On("Query", mock.Anything, mock.Anything, mock.Anything).Return(queryClient, nil) 493 ingesterClient.On("Tail", mock.Anything, &request, mock.Anything).Return(tailClient, nil) 494 ingesterClient.On("TailersCount", mock.Anything, mock.Anything, mock.Anything).Return(&logproto.TailersCountResponse{Count: testData.tailersCount}, nil) 495 496 defaultLimits := defaultLimitsTestConfig() 497 defaultLimits.MaxConcurrentTailRequests = 5 498 499 limits, err := validation.NewOverrides(defaultLimits, nil) 500 require.NoError(t, err) 501 502 q, err := newQuerier( 503 mockQuerierConfig(), 504 mockIngesterClientConfig(), 505 newIngesterClientMockFactory(ingesterClient), 506 newReadRingMock(testData.ringIngesters), 507 &mockDeleteGettter{}, 508 store, limits) 509 require.NoError(t, err) 510 511 ctx := user.InjectOrgID(context.Background(), "test") 512 _, err = q.Tail(ctx, &request) 513 assert.Equal(t, testData.expectedError, err) 514 }) 515 } 516 } 517 518 func TestQuerier_buildQueryIntervals(t *testing.T) { 519 // For simplicity it is always assumed that ingesterQueryStoreMaxLookback and queryIngestersWithin both would be set upto 11 hours so 520 // overlappingQuery has range of last 11 hours while nonOverlappingQuery has range older than last 11 hours. 521 // We would test the cases below with both the queries. 522 overlappingQuery := interval{ 523 start: time.Now().Add(-6 * time.Hour), 524 end: time.Now(), 525 } 526 527 nonOverlappingQuery := interval{ 528 start: time.Now().Add(-24 * time.Hour), 529 end: time.Now().Add(-12 * time.Hour), 530 } 531 532 type response struct { 533 ingesterQueryInterval *interval 534 storeQueryInterval *interval 535 } 536 537 compareResponse := func(t *testing.T, expectedResponse, actualResponse response) { 538 if expectedResponse.ingesterQueryInterval == nil { 539 require.Nil(t, actualResponse.ingesterQueryInterval) 540 } else { 541 require.InDelta(t, expectedResponse.ingesterQueryInterval.start.Unix(), actualResponse.ingesterQueryInterval.start.Unix(), 1) 542 require.InDelta(t, expectedResponse.ingesterQueryInterval.end.Unix(), actualResponse.ingesterQueryInterval.end.Unix(), 1) 543 } 544 545 if expectedResponse.storeQueryInterval == nil { 546 require.Nil(t, actualResponse.storeQueryInterval) 547 } else { 548 require.InDelta(t, expectedResponse.storeQueryInterval.start.Unix(), actualResponse.storeQueryInterval.start.Unix(), 1) 549 require.InDelta(t, expectedResponse.storeQueryInterval.end.Unix(), actualResponse.storeQueryInterval.end.Unix(), 1) 550 } 551 } 552 553 for _, tc := range []struct { 554 name string 555 ingesterQueryStoreMaxLookback time.Duration 556 queryIngestersWithin time.Duration 557 overlappingQueryExpectedResponse response 558 nonOverlappingQueryExpectedResponse response 559 }{ 560 { 561 name: "default values, query ingesters and store for whole duration", 562 overlappingQueryExpectedResponse: response{ // query both store and ingesters 563 ingesterQueryInterval: &overlappingQuery, 564 storeQueryInterval: &overlappingQuery, 565 }, 566 nonOverlappingQueryExpectedResponse: response{ // query both store and ingesters 567 ingesterQueryInterval: &nonOverlappingQuery, 568 storeQueryInterval: &nonOverlappingQuery, 569 }, 570 }, 571 { 572 name: "ingesterQueryStoreMaxLookback set to 1h", 573 ingesterQueryStoreMaxLookback: time.Hour, 574 overlappingQueryExpectedResponse: response{ // query ingesters for last 1h and store until last 1h. 575 ingesterQueryInterval: &interval{ 576 start: time.Now().Add(-time.Hour), 577 end: overlappingQuery.end, 578 }, 579 storeQueryInterval: &interval{ 580 start: overlappingQuery.start, 581 end: time.Now().Add(-time.Hour), 582 }, 583 }, 584 nonOverlappingQueryExpectedResponse: response{ // query just the store 585 storeQueryInterval: &nonOverlappingQuery, 586 }, 587 }, 588 { 589 name: "ingesterQueryStoreMaxLookback set to 10h", 590 ingesterQueryStoreMaxLookback: 10 * time.Hour, 591 overlappingQueryExpectedResponse: response{ // query just the ingesters. 592 ingesterQueryInterval: &overlappingQuery, 593 }, 594 nonOverlappingQueryExpectedResponse: response{ // query just the store 595 storeQueryInterval: &nonOverlappingQuery, 596 }, 597 }, 598 { 599 name: "ingesterQueryStoreMaxLookback set to 1h and queryIngestersWithin set to 2h, ingesterQueryStoreMaxLookback takes precedence", 600 ingesterQueryStoreMaxLookback: time.Hour, 601 queryIngestersWithin: 2 * time.Hour, 602 overlappingQueryExpectedResponse: response{ // query ingesters for last 1h and store until last 1h. 603 ingesterQueryInterval: &interval{ 604 start: time.Now().Add(-time.Hour), 605 end: overlappingQuery.end, 606 }, 607 storeQueryInterval: &interval{ 608 start: overlappingQuery.start, 609 end: time.Now().Add(-time.Hour), 610 }, 611 }, 612 nonOverlappingQueryExpectedResponse: response{ // query just the store 613 storeQueryInterval: &nonOverlappingQuery, 614 }, 615 }, 616 { 617 name: "ingesterQueryStoreMaxLookback set to 2h and queryIngestersWithin set to 1h, ingesterQueryStoreMaxLookback takes precedence", 618 ingesterQueryStoreMaxLookback: 2 * time.Hour, 619 queryIngestersWithin: time.Hour, 620 overlappingQueryExpectedResponse: response{ // query ingesters for last 2h and store until last 2h. 621 ingesterQueryInterval: &interval{ 622 start: time.Now().Add(-2 * time.Hour), 623 end: overlappingQuery.end, 624 }, 625 storeQueryInterval: &interval{ 626 start: overlappingQuery.start, 627 end: time.Now().Add(-2 * time.Hour), 628 }, 629 }, 630 nonOverlappingQueryExpectedResponse: response{ // query just the store 631 storeQueryInterval: &nonOverlappingQuery, 632 }, 633 }, 634 { 635 name: "ingesterQueryStoreMaxLookback set to -1, query just ingesters", 636 ingesterQueryStoreMaxLookback: -1, 637 overlappingQueryExpectedResponse: response{ 638 ingesterQueryInterval: &overlappingQuery, 639 }, 640 nonOverlappingQueryExpectedResponse: response{ 641 ingesterQueryInterval: &nonOverlappingQuery, 642 }, 643 }, 644 { 645 name: "queryIngestersWithin set to 1h", 646 queryIngestersWithin: time.Hour, 647 overlappingQueryExpectedResponse: response{ // query both store and ingesters since query overlaps queryIngestersWithin 648 ingesterQueryInterval: &overlappingQuery, 649 storeQueryInterval: &overlappingQuery, 650 }, 651 nonOverlappingQueryExpectedResponse: response{ // query just the store since query doesn't overlap queryIngestersWithin 652 storeQueryInterval: &nonOverlappingQuery, 653 }, 654 }, 655 { 656 name: "queryIngestersWithin set to 10h", 657 queryIngestersWithin: 10 * time.Hour, 658 overlappingQueryExpectedResponse: response{ // query both store and ingesters since query overlaps queryIngestersWithin 659 ingesterQueryInterval: &overlappingQuery, 660 storeQueryInterval: &overlappingQuery, 661 }, 662 nonOverlappingQueryExpectedResponse: response{ // query just the store since query doesn't overlap queryIngestersWithin 663 storeQueryInterval: &nonOverlappingQuery, 664 }, 665 }, 666 } { 667 t.Run(tc.name, func(t *testing.T) { 668 querier := SingleTenantQuerier{cfg: Config{ 669 IngesterQueryStoreMaxLookback: tc.ingesterQueryStoreMaxLookback, 670 QueryIngestersWithin: tc.queryIngestersWithin, 671 }} 672 673 ingesterQueryInterval, storeQueryInterval := querier.buildQueryIntervals(overlappingQuery.start, overlappingQuery.end) 674 compareResponse(t, tc.overlappingQueryExpectedResponse, response{ 675 ingesterQueryInterval: ingesterQueryInterval, 676 storeQueryInterval: storeQueryInterval, 677 }) 678 679 ingesterQueryInterval, storeQueryInterval = querier.buildQueryIntervals(nonOverlappingQuery.start, nonOverlappingQuery.end) 680 compareResponse(t, tc.nonOverlappingQueryExpectedResponse, response{ 681 ingesterQueryInterval: ingesterQueryInterval, 682 storeQueryInterval: storeQueryInterval, 683 }) 684 }) 685 } 686 } 687 688 func TestQuerier_calculateIngesterMaxLookbackPeriod(t *testing.T) { 689 for _, tc := range []struct { 690 name string 691 ingesterQueryStoreMaxLookback time.Duration 692 queryIngestersWithin time.Duration 693 expected time.Duration 694 }{ 695 { 696 name: "defaults are set; infinite lookback period if no values are set", 697 expected: -1, 698 }, 699 { 700 name: "only setting ingesterQueryStoreMaxLookback", 701 ingesterQueryStoreMaxLookback: time.Hour, 702 expected: time.Hour, 703 }, 704 { 705 name: "setting both ingesterQueryStoreMaxLookback and queryIngestersWithin; ingesterQueryStoreMaxLookback takes precedence", 706 ingesterQueryStoreMaxLookback: time.Hour, 707 queryIngestersWithin: time.Minute, 708 expected: time.Hour, 709 }, 710 { 711 name: "only setting queryIngestersWithin", 712 queryIngestersWithin: time.Minute, 713 expected: time.Minute, 714 }, 715 } { 716 t.Run(tc.name, func(t *testing.T) { 717 querier := SingleTenantQuerier{cfg: Config{ 718 IngesterQueryStoreMaxLookback: tc.ingesterQueryStoreMaxLookback, 719 QueryIngestersWithin: tc.queryIngestersWithin, 720 }} 721 722 assert.Equal(t, tc.expected, querier.calculateIngesterMaxLookbackPeriod()) 723 }) 724 } 725 } 726 727 func TestQuerier_isWithinIngesterMaxLookbackPeriod(t *testing.T) { 728 overlappingQuery := interval{ 729 start: time.Now().Add(-6 * time.Hour), 730 end: time.Now(), 731 } 732 733 nonOverlappingQuery := interval{ 734 start: time.Now().Add(-24 * time.Hour), 735 end: time.Now().Add(-12 * time.Hour), 736 } 737 738 for _, tc := range []struct { 739 name string 740 ingesterQueryStoreMaxLookback time.Duration 741 queryIngestersWithin time.Duration 742 overlappingWithinRange bool 743 nonOverlappingWithinRange bool 744 }{ 745 { 746 name: "default values, query ingesters and store for whole duration", 747 overlappingWithinRange: true, 748 nonOverlappingWithinRange: true, 749 }, 750 { 751 name: "ingesterQueryStoreMaxLookback set to 1h", 752 ingesterQueryStoreMaxLookback: time.Hour, 753 overlappingWithinRange: true, 754 nonOverlappingWithinRange: false, 755 }, 756 { 757 name: "ingesterQueryStoreMaxLookback set to 10h", 758 ingesterQueryStoreMaxLookback: 10 * time.Hour, 759 overlappingWithinRange: true, 760 nonOverlappingWithinRange: false, 761 }, 762 { 763 name: "ingesterQueryStoreMaxLookback set to 1h and queryIngestersWithin set to 16h, ingesterQueryStoreMaxLookback takes precedence", 764 ingesterQueryStoreMaxLookback: time.Hour, 765 queryIngestersWithin: 16 * time.Hour, // if used, this would put the nonOverlapping query in range 766 overlappingWithinRange: true, 767 nonOverlappingWithinRange: false, 768 }, 769 { 770 name: "ingesterQueryStoreMaxLookback set to -1, query just ingesters", 771 ingesterQueryStoreMaxLookback: -1, 772 overlappingWithinRange: true, 773 nonOverlappingWithinRange: true, 774 }, 775 { 776 name: "queryIngestersWithin set to 1h", 777 queryIngestersWithin: time.Hour, 778 overlappingWithinRange: true, 779 nonOverlappingWithinRange: false, 780 }, 781 { 782 name: "queryIngestersWithin set to 10h", 783 queryIngestersWithin: 10 * time.Hour, 784 overlappingWithinRange: true, 785 nonOverlappingWithinRange: false, 786 }, 787 } { 788 t.Run(tc.name, func(t *testing.T) { 789 querier := SingleTenantQuerier{cfg: Config{ 790 IngesterQueryStoreMaxLookback: tc.ingesterQueryStoreMaxLookback, 791 QueryIngestersWithin: tc.queryIngestersWithin, 792 }} 793 794 lookbackPeriod := querier.calculateIngesterMaxLookbackPeriod() 795 assert.Equal(t, tc.overlappingWithinRange, querier.isWithinIngesterMaxLookbackPeriod(lookbackPeriod, overlappingQuery.end)) 796 assert.Equal(t, tc.nonOverlappingWithinRange, querier.isWithinIngesterMaxLookbackPeriod(lookbackPeriod, nonOverlappingQuery.end)) 797 }) 798 } 799 } 800 801 func TestQuerier_RequestingIngesters(t *testing.T) { 802 ctx := user.InjectOrgID(context.Background(), "test") 803 804 requestMapping := map[string]struct { 805 ingesterMethod string 806 storeMethod string 807 }{ 808 "SelectLogs": { 809 ingesterMethod: "Query", 810 storeMethod: "SelectLogs", 811 }, 812 "SelectSamples": { 813 ingesterMethod: "QuerySample", 814 storeMethod: "SelectSamples", 815 }, 816 "LabelValuesForMetricName": { 817 ingesterMethod: "Label", 818 storeMethod: "LabelValuesForMetricName", 819 }, 820 "LabelNamesForMetricName": { 821 ingesterMethod: "Label", 822 storeMethod: "LabelNamesForMetricName", 823 }, 824 "Series": { 825 ingesterMethod: "Series", 826 storeMethod: "Series", 827 }, 828 } 829 830 tests := []struct { 831 desc string 832 start, end time.Time 833 setIngesterQueryStoreMaxLookback bool 834 expectedCallsStore int 835 expectedCallsIngesters int 836 }{ 837 { 838 desc: "Data in storage and ingesters", 839 start: time.Now().Add(-time.Hour * 2), 840 end: time.Now(), 841 expectedCallsStore: 1, 842 expectedCallsIngesters: 1, 843 }, 844 { 845 desc: "Data in ingesters (IngesterQueryStoreMaxLookback not set)", 846 start: time.Now().Add(-time.Minute * 15), 847 end: time.Now(), 848 expectedCallsStore: 1, 849 expectedCallsIngesters: 1, 850 }, 851 { 852 desc: "Data only in storage", 853 start: time.Now().Add(-time.Hour * 2), 854 end: time.Now().Add(-time.Hour * 1), 855 expectedCallsStore: 1, 856 expectedCallsIngesters: 0, 857 }, 858 { 859 desc: "Data in ingesters (IngesterQueryStoreMaxLookback set)", 860 start: time.Now().Add(-time.Minute * 15), 861 end: time.Now(), 862 setIngesterQueryStoreMaxLookback: true, 863 expectedCallsStore: 0, 864 expectedCallsIngesters: 1, 865 }, 866 } 867 868 requests := []struct { 869 name string 870 do func(querier *SingleTenantQuerier, start, end time.Time) error 871 }{ 872 { 873 name: "SelectLogs", 874 do: func(querier *SingleTenantQuerier, start, end time.Time) error { 875 _, err := querier.SelectLogs(ctx, logql.SelectLogParams{ 876 QueryRequest: &logproto.QueryRequest{ 877 Selector: "{type=\"test\", fail=\"yes\"} |= \"foo\"", 878 Limit: 10, 879 Start: start, 880 End: end, 881 Direction: logproto.FORWARD, 882 }, 883 }) 884 885 return err 886 }, 887 }, 888 { 889 name: "SelectSamples", 890 do: func(querier *SingleTenantQuerier, start, end time.Time) error { 891 _, err := querier.SelectSamples(ctx, logql.SelectSampleParams{ 892 SampleQueryRequest: &logproto.SampleQueryRequest{ 893 Selector: "count_over_time({foo=\"bar\"}[5m])", 894 Start: start, 895 End: end, 896 }, 897 }) 898 return err 899 }, 900 }, 901 { 902 name: "LabelValuesForMetricName", 903 do: func(querier *SingleTenantQuerier, start, end time.Time) error { 904 _, err := querier.Label(ctx, &logproto.LabelRequest{ 905 Name: "type", 906 Values: true, 907 Start: &start, 908 End: &end, 909 }) 910 return err 911 }, 912 }, 913 { 914 name: "LabelNamesForMetricName", 915 do: func(querier *SingleTenantQuerier, start, end time.Time) error { 916 _, err := querier.Label(ctx, &logproto.LabelRequest{ 917 Values: false, 918 Start: &start, 919 End: &end, 920 }) 921 return err 922 }, 923 }, 924 { 925 name: "Series", 926 do: func(querier *SingleTenantQuerier, start, end time.Time) error { 927 _, err := querier.Series(ctx, &logproto.SeriesRequest{ 928 Start: start, 929 End: end, 930 }) 931 return err 932 }, 933 }, 934 } 935 936 for _, tc := range tests { 937 t.Run(tc.desc, func(t *testing.T) { 938 939 conf := mockQuerierConfig() 940 conf.QueryIngestersWithin = time.Minute * 30 941 if tc.setIngesterQueryStoreMaxLookback { 942 conf.IngesterQueryStoreMaxLookback = conf.QueryIngestersWithin 943 } 944 945 limits, err := validation.NewOverrides(defaultLimitsTestConfig(), nil) 946 require.NoError(t, err) 947 948 for _, request := range requests { 949 t.Run(request.name, func(t *testing.T) { 950 ingesterClient, store, querier, err := setupIngesterQuerierMocks(conf, limits) 951 require.NoError(t, err) 952 953 err = request.do(querier, tc.start, tc.end) 954 require.NoError(t, err) 955 956 callsIngesters := ingesterClient.GetMockedCallsByMethod(requestMapping[request.name].ingesterMethod) 957 assert.Equal(t, tc.expectedCallsIngesters, len(callsIngesters)) 958 959 callsStore := store.GetMockedCallsByMethod(requestMapping[request.name].storeMethod) 960 assert.Equal(t, tc.expectedCallsStore, len(callsStore)) 961 }) 962 } 963 }) 964 } 965 } 966 967 func setupIngesterQuerierMocks(conf Config, limits *validation.Overrides) (*querierClientMock, *storeMock, *SingleTenantQuerier, error) { 968 queryClient := newQueryClientMock() 969 queryClient.On("Recv").Return(mockQueryResponse([]logproto.Stream{mockStream(1, 1)}), nil) 970 971 querySampleClient := newQuerySampleClientMock() 972 querySampleClient.On("Recv").Return(mockQueryResponse([]logproto.Stream{mockStream(1, 1)}), nil) 973 974 ingesterClient := newQuerierClientMock() 975 ingesterClient.On("Query", mock.Anything, mock.Anything, mock.Anything).Return(queryClient, nil) 976 ingesterClient.On("QuerySample", mock.Anything, mock.Anything, mock.Anything).Return(querySampleClient, nil) 977 ingesterClient.On("Label", mock.Anything, mock.Anything, mock.Anything).Return(mockLabelResponse([]string{"bar"}), nil) 978 ingesterClient.On("Series", mock.Anything, mock.Anything, mock.Anything).Return(&logproto.SeriesResponse{ 979 Series: []logproto.SeriesIdentifier{ 980 { 981 Labels: map[string]string{"bar": "1"}, 982 }, 983 }, 984 }, nil) 985 986 store := newStoreMock() 987 store.On("SelectLogs", mock.Anything, mock.Anything).Return(mockStreamIterator(0, 1), nil) 988 store.On("SelectSamples", mock.Anything, mock.Anything).Return(mockSampleIterator(querySampleClient), nil) 989 store.On("LabelValuesForMetricName", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return([]string{"1", "2", "3"}, nil) 990 store.On("LabelNamesForMetricName", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return([]string{"foo"}, nil) 991 store.On("Series", mock.Anything, mock.Anything).Return([]logproto.SeriesIdentifier{ 992 {Labels: map[string]string{"foo": "1"}}, 993 }, nil) 994 995 querier, err := newQuerier( 996 conf, 997 mockIngesterClientConfig(), 998 newIngesterClientMockFactory(ingesterClient), 999 mockReadRingWithOneActiveIngester(), 1000 &mockDeleteGettter{}, 1001 store, limits) 1002 1003 if err != nil { 1004 return nil, nil, nil, err 1005 } 1006 1007 return ingesterClient, store, querier, nil 1008 } 1009 1010 type fakeTimeLimits struct { 1011 maxQueryLookback time.Duration 1012 maxQueryLength time.Duration 1013 } 1014 1015 func (f fakeTimeLimits) MaxQueryLookback(_ string) time.Duration { return f.maxQueryLookback } 1016 func (f fakeTimeLimits) MaxQueryLength(_ string) time.Duration { return f.maxQueryLength } 1017 1018 func Test_validateQueryTimeRangeLimits(t *testing.T) { 1019 now := time.Now() 1020 nowFunc = func() time.Time { return now } 1021 tests := []struct { 1022 name string 1023 limits timeRangeLimits 1024 from time.Time 1025 through time.Time 1026 wantFrom time.Time 1027 wantThrough time.Time 1028 wantErr bool 1029 }{ 1030 {"no change", fakeTimeLimits{1000 * time.Hour, 1000 * time.Hour}, now, now.Add(24 * time.Hour), now, now.Add(24 * time.Hour), false}, 1031 {"clamped to 24h", fakeTimeLimits{24 * time.Hour, 1000 * time.Hour}, now.Add(-48 * time.Hour), now, now.Add(-24 * time.Hour), now, false}, 1032 {"end before start", fakeTimeLimits{}, now, now.Add(-48 * time.Hour), time.Time{}, time.Time{}, true}, 1033 {"query too long", fakeTimeLimits{maxQueryLength: 24 * time.Hour}, now.Add(-48 * time.Hour), now, time.Time{}, time.Time{}, true}, 1034 } 1035 for _, tt := range tests { 1036 t.Run(tt.name, func(t *testing.T) { 1037 from, through, err := validateQueryTimeRangeLimits(context.Background(), "foo", tt.limits, tt.from, tt.through) 1038 if tt.wantErr { 1039 require.NotNil(t, err) 1040 } else { 1041 require.Nil(t, err) 1042 } 1043 require.Equal(t, tt.wantFrom, from, "wanted (%s) got (%s)", tt.wantFrom, from) 1044 require.Equal(t, tt.wantThrough, through) 1045 }) 1046 } 1047 } 1048 1049 func TestQuerier_SelectLogWithDeletes(t *testing.T) { 1050 store := newStoreMock() 1051 store.On("SelectLogs", mock.Anything, mock.Anything).Return(mockStreamIterator(1, 2), nil) 1052 1053 queryClient := newQueryClientMock() 1054 queryClient.On("Recv").Return(mockQueryResponse([]logproto.Stream{mockStream(1, 2)}), nil) 1055 1056 ingesterClient := newQuerierClientMock() 1057 ingesterClient.On("Query", mock.Anything, mock.Anything, mock.Anything).Return(queryClient, nil) 1058 1059 limits, err := validation.NewOverrides(defaultLimitsTestConfig(), nil) 1060 require.NoError(t, err) 1061 1062 delGetter := &mockDeleteGettter{ 1063 results: []deletion.DeleteRequest{ 1064 {Query: `0`, StartTime: 0, EndTime: 100}, 1065 {Query: `1`, StartTime: 200, EndTime: 400}, 1066 {Query: `2`, StartTime: 400, EndTime: 500}, 1067 {Query: `3`, StartTime: 500, EndTime: 700}, 1068 {Query: `4`, StartTime: 700, EndTime: 900}, 1069 }, 1070 } 1071 1072 q, err := newQuerier( 1073 mockQuerierConfig(), 1074 mockIngesterClientConfig(), 1075 newIngesterClientMockFactory(ingesterClient), 1076 mockReadRingWithOneActiveIngester(), 1077 delGetter, store, limits) 1078 require.NoError(t, err) 1079 1080 ctx := user.InjectOrgID(context.Background(), "test") 1081 1082 request := logproto.QueryRequest{ 1083 Selector: `{type="test"} |= "foo"`, 1084 Limit: 10, 1085 Start: time.Unix(0, 300000000), 1086 End: time.Unix(0, 600000000), 1087 Direction: logproto.FORWARD, 1088 } 1089 1090 _, err = q.SelectLogs(ctx, logql.SelectLogParams{QueryRequest: &request}) 1091 require.NoError(t, err) 1092 1093 expectedRequest := &logproto.QueryRequest{ 1094 Selector: request.Selector, 1095 Limit: request.Limit, 1096 Start: request.Start, 1097 End: request.End, 1098 Direction: request.Direction, 1099 Deletes: []*logproto.Delete{ 1100 {Selector: "1", Start: 200000000, End: 400000000}, 1101 {Selector: "2", Start: 400000000, End: 500000000}, 1102 {Selector: "3", Start: 500000000, End: 700000000}, 1103 }, 1104 } 1105 1106 require.Contains(t, store.Calls[0].Arguments, logql.SelectLogParams{QueryRequest: expectedRequest}) 1107 require.Contains(t, ingesterClient.Calls[0].Arguments, expectedRequest) 1108 require.Equal(t, "test", delGetter.user) 1109 } 1110 1111 func TestQuerier_SelectSamplesWithDeletes(t *testing.T) { 1112 queryClient := newQuerySampleClientMock() 1113 queryClient.On("Recv").Return(mockQueryResponse([]logproto.Stream{mockStream(1, 2)}), nil) 1114 1115 store := newStoreMock() 1116 store.On("SelectSamples", mock.Anything, mock.Anything).Return(mockSampleIterator(queryClient), nil) 1117 1118 ingesterClient := newQuerierClientMock() 1119 ingesterClient.On("QuerySample", mock.Anything, mock.Anything, mock.Anything).Return(queryClient, nil) 1120 1121 limits, err := validation.NewOverrides(defaultLimitsTestConfig(), nil) 1122 require.NoError(t, err) 1123 1124 delGetter := &mockDeleteGettter{ 1125 results: []deletion.DeleteRequest{ 1126 {Query: `0`, StartTime: 0, EndTime: 100}, 1127 {Query: `1`, StartTime: 200, EndTime: 400}, 1128 {Query: `2`, StartTime: 400, EndTime: 500}, 1129 {Query: `3`, StartTime: 500, EndTime: 700}, 1130 {Query: `4`, StartTime: 700, EndTime: 900}, 1131 }, 1132 } 1133 1134 q, err := newQuerier( 1135 mockQuerierConfig(), 1136 mockIngesterClientConfig(), 1137 newIngesterClientMockFactory(ingesterClient), 1138 mockReadRingWithOneActiveIngester(), 1139 delGetter, store, limits) 1140 require.NoError(t, err) 1141 1142 ctx := user.InjectOrgID(context.Background(), "test") 1143 1144 request := logproto.SampleQueryRequest{ 1145 Selector: `count_over_time({foo="bar"}[5m])`, 1146 Start: time.Unix(0, 300000000), 1147 End: time.Unix(0, 600000000), 1148 } 1149 1150 _, err = q.SelectSamples(ctx, logql.SelectSampleParams{SampleQueryRequest: &request}) 1151 require.NoError(t, err) 1152 1153 expectedRequest := logql.SelectSampleParams{ 1154 SampleQueryRequest: &logproto.SampleQueryRequest{ 1155 Selector: request.Selector, 1156 Start: request.Start, 1157 End: request.End, 1158 Deletes: []*logproto.Delete{ 1159 {Selector: "1", Start: 200000000, End: 400000000}, 1160 {Selector: "2", Start: 400000000, End: 500000000}, 1161 {Selector: "3", Start: 500000000, End: 700000000}, 1162 }, 1163 }, 1164 } 1165 1166 require.Contains(t, store.Calls[0].Arguments, expectedRequest) 1167 require.Contains(t, ingesterClient.Calls[0].Arguments, expectedRequest.SampleQueryRequest) 1168 require.Equal(t, "test", delGetter.user) 1169 } 1170 1171 func newQuerier(cfg Config, clientCfg client.Config, clientFactory ring_client.PoolFactory, ring ring.ReadRing, dg *mockDeleteGettter, store storage.Store, limits *validation.Overrides) (*SingleTenantQuerier, error) { 1172 iq, err := newIngesterQuerier(clientCfg, ring, cfg.ExtraQueryDelay, clientFactory) 1173 if err != nil { 1174 return nil, err 1175 } 1176 1177 return New(cfg, store, iq, limits, dg, nil) 1178 } 1179 1180 type mockDeleteGettter struct { 1181 user string 1182 results []deletion.DeleteRequest 1183 } 1184 1185 func (d *mockDeleteGettter) GetAllDeleteRequestsForUser(ctx context.Context, userID string) ([]deletion.DeleteRequest, error) { 1186 d.user = userID 1187 return d.results, nil 1188 }