github.com/thanos-io/thanos@v0.32.5/pkg/store/proxy_test.go (about) 1 // Copyright (c) The Thanos Authors. 2 // Licensed under the Apache License 2.0. 3 4 package store 5 6 import ( 7 "context" 8 "fmt" 9 10 "math" 11 "math/rand" 12 "os" 13 "path/filepath" 14 "sync" 15 "testing" 16 "time" 17 18 "github.com/cespare/xxhash/v2" 19 "github.com/go-kit/log" 20 "github.com/gogo/protobuf/proto" 21 "github.com/gogo/protobuf/types" 22 "github.com/pkg/errors" 23 "github.com/prometheus/prometheus/model/labels" 24 "github.com/prometheus/prometheus/model/timestamp" 25 "github.com/prometheus/prometheus/tsdb" 26 "github.com/prometheus/prometheus/tsdb/chunkenc" 27 "google.golang.org/grpc" 28 "google.golang.org/grpc/codes" 29 "google.golang.org/grpc/status" 30 31 "github.com/efficientgo/core/testutil" 32 33 "github.com/thanos-io/thanos/pkg/component" 34 "github.com/thanos-io/thanos/pkg/info/infopb" 35 "github.com/thanos-io/thanos/pkg/store/labelpb" 36 "github.com/thanos-io/thanos/pkg/store/storepb" 37 storetestutil "github.com/thanos-io/thanos/pkg/store/storepb/testutil" 38 "github.com/thanos-io/thanos/pkg/testutil/custom" 39 ) 40 41 type mockedSeriesServer struct { 42 storepb.Store_SeriesServer 43 ctx context.Context 44 45 send func(*storepb.SeriesResponse) error 46 } 47 48 func (s *mockedSeriesServer) Send(r *storepb.SeriesResponse) error { 49 return s.send(r) 50 } 51 func (s *mockedSeriesServer) Context() context.Context { return s.ctx } 52 53 type mockedStartTimeDB struct { 54 *tsdb.DBReadOnly 55 startTime int64 56 } 57 58 func (db *mockedStartTimeDB) StartTime() (int64, error) { return db.startTime, nil } 59 60 func TestProxyStore_Info(t *testing.T) { 61 defer custom.TolerantVerifyLeak(t) 62 63 ctx, cancel := context.WithCancel(context.Background()) 64 defer cancel() 65 66 q := NewProxyStore(nil, 67 nil, 68 func() []Client { return nil }, 69 component.Query, 70 nil, 0*time.Second, RetrievalStrategy(EagerRetrieval), 71 ) 72 73 resp, err := q.Info(ctx, &storepb.InfoRequest{}) 74 testutil.Ok(t, err) 75 testutil.Equals(t, []labelpb.ZLabelSet(nil), resp.LabelSets) 76 testutil.Equals(t, storepb.StoreType_QUERY, resp.StoreType) 77 testutil.Equals(t, int64(0), resp.MinTime) 78 testutil.Equals(t, int64(0), resp.MaxTime) 79 } 80 81 func TestProxyStore_TSDBInfos(t *testing.T) { 82 stores := []Client{ 83 &storetestutil.TestClient{ 84 StoreTSDBInfos: nil, 85 }, 86 &storetestutil.TestClient{ 87 StoreTSDBInfos: []infopb.TSDBInfo{ 88 infopb.NewTSDBInfo(0, 10, []labelpb.ZLabel{{Name: "lbl", Value: "val1"}}), 89 }, 90 }, 91 &storetestutil.TestClient{ 92 StoreTSDBInfos: []infopb.TSDBInfo{ 93 infopb.NewTSDBInfo(0, 20, []labelpb.ZLabel{{Name: "lbl", Value: "val2"}}), 94 }, 95 }, 96 } 97 q := NewProxyStore(nil, nil, 98 func() []Client { return stores }, 99 component.Query, nil, 0*time.Second, EagerRetrieval, 100 ) 101 102 expected := []infopb.TSDBInfo{ 103 infopb.NewTSDBInfo(0, 10, []labelpb.ZLabel{{Name: "lbl", Value: "val1"}}), 104 infopb.NewTSDBInfo(0, 20, []labelpb.ZLabel{{Name: "lbl", Value: "val2"}}), 105 } 106 testutil.Equals(t, expected, q.TSDBInfos()) 107 } 108 109 func TestProxyStore_Series(t *testing.T) { 110 defer custom.TolerantVerifyLeak(t) 111 112 for _, tc := range []struct { 113 title string 114 storeAPIs []Client 115 selectorLabels labels.Labels 116 117 req *storepb.SeriesRequest 118 storeDebugMatchers [][]*labels.Matcher 119 120 expectedSeries []rawSeries 121 expectedErr error 122 expectedWarningsLen int 123 }{ 124 { 125 title: "no storeAPI available", 126 req: &storepb.SeriesRequest{ 127 MinTime: 1, 128 MaxTime: 300, 129 Matchers: []storepb.LabelMatcher{{Name: "a", Value: "a", Type: storepb.LabelMatcher_EQ}}, 130 }, 131 expectedWarningsLen: 0, // No store matched for this query. 132 }, 133 { 134 title: "no storeAPI available for 301-302 time range", 135 storeAPIs: []Client{ 136 &storetestutil.TestClient{ 137 StoreClient: &mockedStoreAPI{ 138 RespSeries: []*storepb.SeriesResponse{ 139 storeSeriesResponse(t, labels.FromStrings("a", "a"), []sample{{0, 0}, {2, 1}, {3, 2}}), 140 }, 141 }, 142 MinTime: 1, 143 MaxTime: 300, 144 }, 145 }, 146 req: &storepb.SeriesRequest{ 147 MinTime: 301, 148 MaxTime: 400, 149 Matchers: []storepb.LabelMatcher{{Name: "a", Value: "a", Type: storepb.LabelMatcher_EQ}}, 150 }, 151 expectedWarningsLen: 0, // No store matched for this query. 152 }, 153 { 154 title: "storeAPI available for time range; no series for ext=2 external label matcher", 155 storeAPIs: []Client{ 156 &storetestutil.TestClient{ 157 StoreClient: &mockedStoreAPI{ 158 RespSeries: []*storepb.SeriesResponse{ 159 storeSeriesResponse(t, labels.FromStrings("a", "a"), []sample{{0, 0}, {2, 1}, {3, 2}}), 160 }, 161 }, 162 MinTime: 1, 163 MaxTime: 300, 164 ExtLset: []labels.Labels{labels.FromStrings("ext", "1")}, 165 }, 166 }, 167 req: &storepb.SeriesRequest{ 168 MinTime: 1, 169 MaxTime: 300, 170 Matchers: []storepb.LabelMatcher{{Name: "ext", Value: "2", Type: storepb.LabelMatcher_EQ}}, 171 }, 172 expectedWarningsLen: 0, // No store matched for this query. 173 }, 174 { 175 title: "storeAPI available for time range; available series for ext=1 external label matcher", 176 storeAPIs: []Client{ 177 &storetestutil.TestClient{ 178 StoreClient: &mockedStoreAPI{ 179 RespSeries: []*storepb.SeriesResponse{ 180 storeSeriesResponse(t, labels.FromStrings("a", "a"), []sample{{0, 0}, {2, 1}, {3, 2}}), 181 }, 182 }, 183 MinTime: 1, 184 MaxTime: 300, 185 ExtLset: []labels.Labels{labels.FromStrings("ext", "1")}, 186 }, 187 }, 188 req: &storepb.SeriesRequest{ 189 MinTime: 1, 190 MaxTime: 300, 191 Matchers: []storepb.LabelMatcher{{Name: "ext", Value: "1", Type: storepb.LabelMatcher_EQ}}, 192 }, 193 expectedSeries: []rawSeries{ 194 { 195 lset: labels.FromStrings("a", "a"), 196 chunks: [][]sample{{{0, 0}, {2, 1}, {3, 2}}}, 197 }, 198 }, 199 }, 200 { 201 title: "storeAPI available for time range; available series for any external label matcher", 202 storeAPIs: []Client{ 203 &storetestutil.TestClient{ 204 StoreClient: &mockedStoreAPI{ 205 RespSeries: []*storepb.SeriesResponse{ 206 storeSeriesResponse(t, labels.FromStrings("a", "a"), []sample{{4, 3}}, []sample{{0, 0}, {2, 1}, {3, 2}}), 207 }, 208 }, 209 MinTime: 1, 210 MaxTime: 300, 211 }, 212 }, 213 req: &storepb.SeriesRequest{ 214 MinTime: 1, 215 MaxTime: 300, 216 Matchers: []storepb.LabelMatcher{{Name: "ext", Value: "1", Type: storepb.LabelMatcher_EQ}}, 217 }, 218 expectedSeries: []rawSeries{ 219 { 220 lset: labels.FromStrings("a", "a"), 221 chunks: [][]sample{{{0, 0}, {2, 1}, {3, 2}}, {{4, 3}}}, 222 }, 223 }, 224 }, 225 { 226 title: "storeAPI available for time range; available series for any external label matcher, but selector blocks", 227 storeAPIs: []Client{ 228 &storetestutil.TestClient{ 229 StoreClient: &mockedStoreAPI{ 230 RespSeries: []*storepb.SeriesResponse{ 231 storeSeriesResponse(t, labels.FromStrings("a", "a"), []sample{{0, 0}, {2, 1}, {3, 2}}), 232 }, 233 }, 234 MinTime: 1, 235 MaxTime: 300, 236 }, 237 }, 238 selectorLabels: labels.FromStrings("ext", "2"), 239 req: &storepb.SeriesRequest{ 240 MinTime: 1, 241 MaxTime: 300, 242 Matchers: []storepb.LabelMatcher{{Name: "ext", Value: "1", Type: storepb.LabelMatcher_EQ}}, 243 }, 244 }, 245 { 246 title: "no validation if storeAPI follow matching contract", 247 storeAPIs: []Client{ 248 &storetestutil.TestClient{ 249 StoreClient: &mockedStoreAPI{ 250 RespSeries: []*storepb.SeriesResponse{ 251 storeSeriesResponse(t, labels.FromStrings("a", "a"), []sample{{0, 0}, {2, 1}, {3, 2}}), 252 }, 253 }, 254 MinTime: 1, 255 MaxTime: 300, 256 }, 257 }, 258 req: &storepb.SeriesRequest{ 259 MinTime: 1, 260 MaxTime: 300, 261 Matchers: []storepb.LabelMatcher{{Name: "a", Value: "b", Type: storepb.LabelMatcher_EQ}}, 262 }, 263 expectedSeries: []rawSeries{ 264 { 265 // We did not ask for a=a, but we trust StoreAPI will match correctly, so proxy does check any of this. 266 lset: labels.FromStrings("a", "a"), 267 chunks: [][]sample{{{0, 0}, {2, 1}, {3, 2}}}, 268 }, 269 }, 270 }, 271 { 272 title: "complex scenario with storeAPIs warnings", 273 storeAPIs: []Client{ 274 &storetestutil.TestClient{ 275 StoreClient: &mockedStoreAPI{ 276 RespSeries: []*storepb.SeriesResponse{ 277 storeSeriesResponse(t, labels.FromStrings("a", "a"), []sample{{0, 0}, {2, 1}, {3, 2}}, []sample{{4, 3}}), 278 storeSeriesResponse(t, labels.FromStrings("a", "a"), []sample{{5, 4}}), // Continuations of the same series. 279 storepb.NewWarnSeriesResponse(errors.New("warning")), 280 storeSeriesResponse(t, labels.FromStrings("a", "b"), []sample{{2, 2}, {3, 3}, {4, 4}}), 281 }, 282 }, 283 MinTime: 1, 284 MaxTime: 300, 285 }, 286 &storetestutil.TestClient{ 287 StoreClient: &mockedStoreAPI{ 288 RespSeries: []*storepb.SeriesResponse{ 289 storeSeriesResponse(t, labels.FromStrings("a", "b"), []sample{{1, 1}, {2, 2}, {3, 3}}), 290 }, 291 }, 292 MinTime: 1, 293 MaxTime: 300, 294 }, 295 &storetestutil.TestClient{ 296 StoreClient: &mockedStoreAPI{ 297 RespSeries: []*storepb.SeriesResponse{ 298 storepb.NewWarnSeriesResponse(errors.New("warning")), 299 }, 300 }, 301 MinTime: 1, 302 MaxTime: 300, 303 }, 304 &storetestutil.TestClient{ 305 StoreClient: &mockedStoreAPI{ 306 RespSeries: []*storepb.SeriesResponse{ 307 storeSeriesResponse(t, labels.FromStrings("a", "c"), []sample{{100, 1}, {300, 3}, {400, 4}}), 308 }, 309 }, 310 MinTime: 1, 311 MaxTime: 300, 312 }, 313 &storetestutil.TestClient{ 314 StoreClient: &mockedStoreAPI{ 315 RespSeries: []*storepb.SeriesResponse{ 316 storeSeriesResponse(t, labels.FromStrings("a", "outside"), []sample{{1, 1}}), 317 }, 318 }, 319 // Outside range for store itself. 320 MinTime: 301, 321 MaxTime: 302, 322 }, 323 }, 324 req: &storepb.SeriesRequest{ 325 MinTime: 1, 326 MaxTime: 300, 327 Matchers: []storepb.LabelMatcher{{Name: "ext", Value: "1", Type: storepb.LabelMatcher_EQ}}, 328 }, 329 expectedSeries: []rawSeries{ 330 { 331 lset: labels.FromStrings("a", "a"), 332 chunks: [][]sample{{{0, 0}, {2, 1}, {3, 2}}, {{4, 3}}, {{5, 4}}}, 333 }, 334 { 335 lset: labels.FromStrings("a", "b"), 336 chunks: [][]sample{{{1, 1}, {2, 2}, {3, 3}}, {{2, 2}, {3, 3}, {4, 4}}}, 337 }, 338 { 339 lset: labels.FromStrings("a", "c"), 340 chunks: [][]sample{{{100, 1}, {300, 3}, {400, 4}}}, 341 }, 342 }, 343 expectedWarningsLen: 2, 344 }, 345 { 346 title: "storeAPI available for time range; available two duplicated series for ext=1 external label matcher from 2 storeAPIs", 347 storeAPIs: []Client{ 348 &storetestutil.TestClient{ 349 StoreClient: &mockedStoreAPI{ 350 RespSeries: []*storepb.SeriesResponse{ 351 storeSeriesResponse(t, labels.FromStrings("a", "a"), []sample{{0, 0}, {2, 1}, {3, 2}}), 352 }, 353 }, 354 MinTime: 1, 355 MaxTime: 300, 356 ExtLset: []labels.Labels{labels.FromStrings("ext", "1")}, 357 }, 358 &storetestutil.TestClient{ 359 StoreClient: &mockedStoreAPI{ 360 RespSeries: []*storepb.SeriesResponse{ 361 storeSeriesResponse(t, labels.FromStrings("a", "a"), []sample{{1, 4}, {2, 5}, {3, 6}}), 362 }, 363 }, 364 MinTime: 1, 365 MaxTime: 300, 366 ExtLset: []labels.Labels{labels.FromStrings("ext", "1")}, 367 }, 368 }, 369 req: &storepb.SeriesRequest{ 370 MinTime: 1, 371 MaxTime: 300, 372 Matchers: []storepb.LabelMatcher{{Name: "ext", Value: "1", Type: storepb.LabelMatcher_EQ}}, 373 }, 374 expectedSeries: []rawSeries{ 375 { 376 lset: labels.FromStrings("a", "a"), 377 chunks: [][]sample{{{0, 0}, {2, 1}, {3, 2}}, {{1, 4}, {2, 5}, {3, 6}}}, 378 }, 379 }, 380 }, 381 { 382 title: "storeAPI available for time range; available a few duplicated series for ext=1 external label matcher, mixed storeAPIs", 383 storeAPIs: []Client{ 384 &storetestutil.TestClient{ 385 StoreClient: &mockedStoreAPI{ 386 RespSeries: []*storepb.SeriesResponse{ 387 storeSeriesResponse(t, labels.FromStrings("a", "1", "w", "1"), []sample{{5, 5}, {7, 7}}), 388 storeSeriesResponse(t, labels.FromStrings("a", "1", "w", "1"), []sample{{0, 0}, {2, 1}, {3, 2}}), 389 storeSeriesResponse(t, labels.FromStrings("a", "1", "w", "1"), []sample{{5, 5}, {6, 6}, {7, 7}}), 390 storeSeriesResponse(t, labels.FromStrings("a", "1", "x", "1"), []sample{{2, 2}, {3, 3}, {4, 4}}, []sample{{1, 1}, {2, 2}, {3, 3}}), 391 storeSeriesResponse(t, labels.FromStrings("a", "1", "x", "1"), []sample{{100, 1}, {300, 3}, {400, 4}}), 392 }, 393 }, 394 MinTime: 1, 395 MaxTime: 300, 396 ExtLset: []labels.Labels{labels.FromStrings("ext", "1")}, 397 }, 398 &storetestutil.TestClient{ 399 StoreClient: &mockedStoreAPI{ 400 RespSeries: []*storepb.SeriesResponse{ 401 storeSeriesResponse(t, labels.FromStrings("a", "1", "w", "1"), []sample{{2, 1}}), 402 storeSeriesResponse(t, labels.FromStrings("a", "1", "w", "1"), []sample{{5, 5}, {6, 6}, {7, 7}}), 403 storeSeriesResponse(t, labels.FromStrings("a", "1", "x", "2"), []sample{{10, 10}, {30, 30}, {40, 40}}), 404 }, 405 }, 406 MinTime: 1, 407 MaxTime: 300, 408 ExtLset: []labels.Labels{labels.FromStrings("ext", "1")}, 409 }, 410 }, 411 req: &storepb.SeriesRequest{ 412 MinTime: 1, 413 MaxTime: 300, 414 Matchers: []storepb.LabelMatcher{{Name: "ext", Value: "1", Type: storepb.LabelMatcher_EQ}}, 415 }, 416 expectedSeries: []rawSeries{ 417 { 418 lset: labels.FromStrings("a", "1", "w", "1"), 419 chunks: [][]sample{{{0, 0}, {2, 1}, {3, 2}}, {{2, 1}}, {{5, 5}, {6, 6}, {7, 7}}, {{5, 5}, {7, 7}}}, 420 }, 421 { 422 lset: labels.FromStrings("a", "1", "x", "1"), 423 chunks: [][]sample{{{1, 1}, {2, 2}, {3, 3}}, {{2, 2}, {3, 3}, {4, 4}}, {{100, 1}, {300, 3}, {400, 4}}}, 424 }, 425 { 426 lset: labels.FromStrings("a", "1", "x", "2"), 427 chunks: [][]sample{{{10, 10}, {30, 30}, {40, 40}}}, 428 }, 429 }, 430 }, 431 { 432 title: "same external labels are validated during upload and on querier storeset, proxy does not care", 433 storeAPIs: []Client{ 434 &storetestutil.TestClient{ 435 StoreClient: &mockedStoreAPI{ 436 RespSeries: []*storepb.SeriesResponse{ 437 storeSeriesResponse(t, labels.FromStrings("a", "b"), []sample{{1, 1}, {2, 2}, {3, 3}}), 438 }, 439 }, 440 ExtLset: []labels.Labels{labels.FromStrings("ext", "1")}, 441 MinTime: 1, 442 MaxTime: 300, 443 }, 444 &storetestutil.TestClient{ 445 StoreClient: &mockedStoreAPI{ 446 RespSeries: []*storepb.SeriesResponse{ 447 storeSeriesResponse(t, labels.FromStrings("a", "b"), []sample{{1, 11}, {2, 22}, {3, 33}}), 448 }, 449 }, 450 ExtLset: []labels.Labels{labels.FromStrings("ext", "1")}, 451 MinTime: 1, 452 MaxTime: 300, 453 }, 454 }, 455 req: &storepb.SeriesRequest{ 456 MinTime: 1, 457 MaxTime: 300, 458 Matchers: []storepb.LabelMatcher{{Name: "ext", Value: "1", Type: storepb.LabelMatcher_EQ}}, 459 }, 460 expectedSeries: []rawSeries{ 461 { 462 lset: labels.FromStrings("a", "b"), 463 chunks: [][]sample{{{1, 11}, {2, 22}, {3, 33}}, {{1, 1}, {2, 2}, {3, 3}}}, 464 }, 465 }, 466 }, 467 { 468 title: "partial response enabled", 469 storeAPIs: []Client{ 470 &storetestutil.TestClient{ 471 StoreClient: &mockedStoreAPI{ 472 RespSeries: []*storepb.SeriesResponse{ 473 storepb.NewWarnSeriesResponse(errors.New("warning")), 474 storeSeriesResponse(t, labels.FromStrings("a", "b"), []sample{{1, 1}, {2, 2}, {3, 3}}), 475 }, 476 }, 477 ExtLset: []labels.Labels{labels.FromStrings("ext", "1")}, 478 MinTime: 1, 479 MaxTime: 300, 480 }, 481 &storetestutil.TestClient{ 482 StoreClient: &mockedStoreAPI{ 483 RespError: errors.New("error!"), 484 }, 485 ExtLset: []labels.Labels{labels.FromStrings("ext", "1")}, 486 MinTime: 1, 487 MaxTime: 300, 488 }, 489 }, 490 req: &storepb.SeriesRequest{ 491 MinTime: 1, 492 MaxTime: 300, 493 Matchers: []storepb.LabelMatcher{{Name: "ext", Value: "1", Type: storepb.LabelMatcher_EQ}}, 494 }, 495 expectedSeries: []rawSeries{ 496 { 497 lset: labels.FromStrings("a", "b"), 498 chunks: [][]sample{{{1, 1}, {2, 2}, {3, 3}}}, 499 }, 500 }, 501 expectedWarningsLen: 2, 502 }, 503 { 504 title: "partial response disabled", 505 storeAPIs: []Client{ 506 &storetestutil.TestClient{ 507 StoreClient: &mockedStoreAPI{ 508 RespSeries: []*storepb.SeriesResponse{ 509 storepb.NewWarnSeriesResponse(errors.New("warning")), 510 storeSeriesResponse(t, labels.FromStrings("a", "b"), []sample{{1, 1}, {2, 2}, {3, 3}}), 511 }, 512 }, 513 ExtLset: []labels.Labels{labels.FromStrings("ext", "1")}, 514 MinTime: 1, 515 MaxTime: 300, 516 }, 517 &storetestutil.TestClient{ 518 StoreClient: &mockedStoreAPI{ 519 RespError: errors.New("error!"), 520 }, 521 ExtLset: []labels.Labels{labels.FromStrings("ext", "1")}, 522 MinTime: 1, 523 MaxTime: 300, 524 }, 525 }, 526 req: &storepb.SeriesRequest{ 527 MinTime: 1, 528 MaxTime: 300, 529 Matchers: []storepb.LabelMatcher{{Name: "ext", Value: "1", Type: storepb.LabelMatcher_EQ}}, 530 PartialResponseDisabled: true, 531 PartialResponseStrategy: storepb.PartialResponseStrategy_ABORT, 532 }, 533 expectedErr: errors.New("fetch series for {ext=\"1\"} : error!"), 534 }, 535 { 536 title: "storeAPI available for time range; available series for ext=1 external label matcher; allowed by store debug matcher", 537 storeAPIs: []Client{ 538 &storetestutil.TestClient{ 539 StoreClient: &mockedStoreAPI{ 540 RespSeries: []*storepb.SeriesResponse{ 541 storeSeriesResponse(t, labels.FromStrings("a", "a"), []sample{{0, 0}, {2, 1}, {3, 2}}), 542 }, 543 }, 544 MinTime: 1, 545 MaxTime: 300, 546 ExtLset: []labels.Labels{labels.FromStrings("ext", "1")}, 547 Name: "testaddr", 548 }, 549 }, 550 req: &storepb.SeriesRequest{ 551 MinTime: 1, 552 MaxTime: 300, 553 Matchers: []storepb.LabelMatcher{{Name: "ext", Value: "1", Type: storepb.LabelMatcher_EQ}}, 554 }, 555 storeDebugMatchers: [][]*labels.Matcher{{labels.MustNewMatcher(labels.MatchEqual, "__address__", "testaddr")}}, 556 557 expectedSeries: []rawSeries{ 558 { 559 lset: labels.FromStrings("a", "a"), 560 chunks: [][]sample{{{0, 0}, {2, 1}, {3, 2}}}, 561 }, 562 }, 563 }, 564 { 565 title: "storeAPI available for time range; available series for ext=1 external label matcher; blocked by store debug matcher.", 566 storeAPIs: []Client{ 567 &storetestutil.TestClient{ 568 StoreClient: &mockedStoreAPI{ 569 RespSeries: []*storepb.SeriesResponse{ 570 storeSeriesResponse(t, labels.FromStrings("a", "a"), []sample{{0, 0}, {2, 1}, {3, 2}}), 571 }, 572 }, 573 MinTime: 1, 574 MaxTime: 300, 575 ExtLset: []labels.Labels{labels.FromStrings("ext", "1")}, 576 Name: "testaddr", 577 }, 578 }, 579 req: &storepb.SeriesRequest{ 580 MinTime: 1, 581 MaxTime: 300, 582 Matchers: []storepb.LabelMatcher{{Name: "ext", Value: "1", Type: storepb.LabelMatcher_EQ}}, 583 }, 584 storeDebugMatchers: [][]*labels.Matcher{{labels.MustNewMatcher(labels.MatchEqual, "__address__", "foo")}}, 585 expectedWarningsLen: 0, // No stores match. 586 }, 587 { 588 title: "sharded series response", 589 storeAPIs: []Client{ 590 &storetestutil.TestClient{ 591 StoreClient: &mockedStoreAPI{ 592 RespSeries: []*storepb.SeriesResponse{ 593 storeSeriesResponse(t, labels.FromStrings("a", "a"), []sample{{0, 0}, {2, 1}, {3, 2}}), 594 storeSeriesResponse(t, labels.FromStrings("a", "b"), []sample{{0, 0}, {2, 1}, {3, 2}}), 595 storeSeriesResponse(t, labels.FromStrings("a", "c"), []sample{{0, 0}, {2, 1}, {3, 2}}), 596 }, 597 }, 598 MinTime: 1, 599 MaxTime: 300, 600 ExtLset: []labels.Labels{labels.FromStrings("ext", "1")}, 601 }, 602 }, 603 req: &storepb.SeriesRequest{ 604 MinTime: 1, 605 MaxTime: 300, 606 Matchers: []storepb.LabelMatcher{{Name: "ext", Value: "1", Type: storepb.LabelMatcher_EQ}}, 607 ShardInfo: &storepb.ShardInfo{ 608 ShardIndex: 0, 609 TotalShards: 2, 610 By: true, 611 Labels: []string{"a"}, 612 }, 613 }, 614 expectedSeries: []rawSeries{ 615 { 616 lset: labels.FromStrings("a", "a"), 617 chunks: [][]sample{{{0, 0}, {2, 1}, {3, 2}}}, 618 }, 619 { 620 lset: labels.FromStrings("a", "b"), 621 chunks: [][]sample{{{0, 0}, {2, 1}, {3, 2}}}, 622 }, 623 }, 624 }, 625 } { 626 t.Run(tc.title, func(t *testing.T) { 627 for _, replicaLabelSupport := range []bool{false, true} { 628 t.Run(fmt.Sprintf("replica_support=%v", replicaLabelSupport), func(t *testing.T) { 629 for _, s := range tc.storeAPIs { 630 cl := s.(*storetestutil.TestClient) 631 cl.WithoutReplicaLabelsEnabled = replicaLabelSupport 632 } 633 for _, strategy := range []RetrievalStrategy{EagerRetrieval, LazyRetrieval} { 634 t.Run(string(strategy), func(t *testing.T) { 635 q := NewProxyStore(nil, 636 nil, 637 func() []Client { return tc.storeAPIs }, 638 component.Query, 639 tc.selectorLabels, 640 5*time.Second, strategy, 641 ) 642 643 ctx := context.Background() 644 if len(tc.storeDebugMatchers) > 0 { 645 ctx = context.WithValue(ctx, StoreMatcherKey, tc.storeDebugMatchers) 646 } 647 648 s := newStoreSeriesServer(ctx) 649 err := q.Series(tc.req, s) 650 if tc.expectedErr != nil { 651 testutil.NotOk(t, err) 652 testutil.Equals(t, tc.expectedErr.Error(), err.Error()) 653 return 654 } 655 testutil.Ok(t, err) 656 657 seriesEquals(t, tc.expectedSeries, s.SeriesSet) 658 testutil.Equals(t, tc.expectedWarningsLen, len(s.Warnings), "got %v warnings", s.Warnings) 659 }) 660 } 661 }) 662 } 663 }) 664 } 665 } 666 667 func TestProxyStore_SeriesSlowStores(t *testing.T) { 668 enable := os.Getenv("THANOS_ENABLE_STORE_READ_TIMEOUT_TESTS") 669 if enable == "" { 670 t.Skip("enable THANOS_ENABLE_STORE_READ_TIMEOUT_TESTS to run store-read-timeout tests") 671 } 672 673 defer custom.TolerantVerifyLeak(t) 674 675 for _, tc := range []struct { 676 title string 677 storeAPIs []Client 678 selectorLabels labels.Labels 679 680 req *storepb.SeriesRequest 681 682 expectedSeries []rawSeries 683 expectedErr error 684 expectedWarningsLen int 685 }{ 686 { 687 title: "partial response disabled; 1st errors out after some delay; 2nd store is fast", 688 storeAPIs: []Client{ 689 &storetestutil.TestClient{ 690 StoreClient: &mockedStoreAPI{ 691 RespSeries: []*storepb.SeriesResponse{ 692 storepb.NewWarnSeriesResponse(errors.New("warning")), 693 storeSeriesResponse(t, labels.FromStrings("a", "b"), []sample{{1, 1}, {2, 2}, {3, 3}}), 694 }, 695 RespDuration: 2 * time.Second, 696 SlowSeriesIndex: 1, 697 injectedError: errors.New("test"), 698 injectedErrorIndex: 1, 699 }, 700 ExtLset: []labels.Labels{labels.FromStrings("ext", "1")}, 701 MinTime: 1, 702 MaxTime: 300, 703 }, 704 &storetestutil.TestClient{ 705 StoreClient: &mockedStoreAPI{ 706 RespSeries: []*storepb.SeriesResponse{ 707 storepb.NewWarnSeriesResponse(errors.New("warning")), 708 storeSeriesResponse(t, labels.FromStrings("b", "a"), []sample{{4, 1}, {5, 2}, {6, 3}}), 709 storeSeriesResponse(t, labels.FromStrings("b", "a"), []sample{{4, 1}, {5, 2}, {6, 3}}), 710 storeSeriesResponse(t, labels.FromStrings("b", "a"), []sample{{4, 1}, {5, 2}, {6, 3}}), 711 storeSeriesResponse(t, labels.FromStrings("b", "a"), []sample{{4, 1}, {5, 2}, {6, 3}}), 712 storeSeriesResponse(t, labels.FromStrings("b", "a"), []sample{{4, 1}, {5, 2}, {6, 3}}), 713 714 storeSeriesResponse(t, labels.FromStrings("b", "a"), []sample{{4, 1}, {5, 2}, {6, 3}}), 715 storeSeriesResponse(t, labels.FromStrings("b", "a"), []sample{{4, 1}, {5, 2}, {6, 3}}), 716 storeSeriesResponse(t, labels.FromStrings("b", "a"), []sample{{4, 1}, {5, 2}, {6, 3}}), 717 storeSeriesResponse(t, labels.FromStrings("b", "a"), []sample{{4, 1}, {5, 2}, {6, 3}}), 718 storeSeriesResponse(t, labels.FromStrings("b", "a"), []sample{{4, 1}, {5, 2}, {6, 3}}), 719 720 storeSeriesResponse(t, labels.FromStrings("b", "a"), []sample{{4, 1}, {5, 2}, {6, 3}}), 721 storeSeriesResponse(t, labels.FromStrings("b", "a"), []sample{{4, 1}, {5, 2}, {6, 3}}), 722 storeSeriesResponse(t, labels.FromStrings("b", "a"), []sample{{4, 1}, {5, 2}, {6, 3}}), 723 storeSeriesResponse(t, labels.FromStrings("b", "a"), []sample{{4, 1}, {5, 2}, {6, 3}}), 724 storeSeriesResponse(t, labels.FromStrings("b", "a"), []sample{{4, 1}, {5, 2}, {6, 3}}), 725 }, 726 }, 727 ExtLset: []labels.Labels{labels.FromStrings("ext", "1")}, 728 MinTime: 1, 729 MaxTime: 300, 730 }, 731 }, 732 req: &storepb.SeriesRequest{ 733 MinTime: 1, 734 MaxTime: 300, 735 Matchers: []storepb.LabelMatcher{{Name: "ext", Value: "1", Type: storepb.LabelMatcher_EQ}}, 736 PartialResponseDisabled: true, 737 PartialResponseStrategy: storepb.PartialResponseStrategy_ABORT, 738 }, 739 expectedErr: errors.New(`rpc error: code = Aborted desc = warning`), 740 }, 741 { 742 title: "partial response disabled; 1st store is slow, 2nd store is fast;", 743 storeAPIs: []Client{ 744 &storetestutil.TestClient{ 745 StoreClient: &mockedStoreAPI{ 746 RespSeries: []*storepb.SeriesResponse{ 747 storepb.NewWarnSeriesResponse(errors.New("warning")), 748 storeSeriesResponse(t, labels.FromStrings("a", "b"), []sample{{1, 1}, {2, 2}, {3, 3}}), 749 }, 750 RespDuration: 10 * time.Second, 751 }, 752 ExtLset: []labels.Labels{labels.FromStrings("ext", "1")}, 753 MinTime: 1, 754 MaxTime: 300, 755 }, 756 &storetestutil.TestClient{ 757 StoreClient: &mockedStoreAPI{ 758 RespSeries: []*storepb.SeriesResponse{ 759 storepb.NewWarnSeriesResponse(errors.New("warning")), 760 storeSeriesResponse(t, labels.FromStrings("a", "b"), []sample{{1, 1}, {2, 2}, {3, 3}}), 761 }, 762 }, 763 ExtLset: []labels.Labels{labels.FromStrings("ext", "1")}, 764 MinTime: 1, 765 MaxTime: 300, 766 }, 767 }, 768 req: &storepb.SeriesRequest{ 769 MinTime: 1, 770 MaxTime: 300, 771 Matchers: []storepb.LabelMatcher{{Name: "ext", Value: "1", Type: storepb.LabelMatcher_EQ}}, 772 PartialResponseDisabled: true, 773 PartialResponseStrategy: storepb.PartialResponseStrategy_ABORT, 774 }, 775 expectedErr: errors.New("rpc error: code = Aborted desc = failed to receive any data in 4s from test: context canceled"), 776 }, 777 { 778 title: "partial response disabled; 1st store is fast, 2nd store is slow;", 779 storeAPIs: []Client{ 780 &storetestutil.TestClient{ 781 StoreClient: &mockedStoreAPI{ 782 RespSeries: []*storepb.SeriesResponse{ 783 storepb.NewWarnSeriesResponse(errors.New("warning")), 784 storeSeriesResponse(t, labels.FromStrings("a", "b"), []sample{{1, 1}, {2, 2}, {3, 3}}), 785 }, 786 }, 787 ExtLset: []labels.Labels{labels.FromStrings("ext", "1")}, 788 MinTime: 1, 789 MaxTime: 300, 790 }, 791 &storetestutil.TestClient{ 792 StoreClient: &mockedStoreAPI{ 793 RespSeries: []*storepb.SeriesResponse{ 794 storepb.NewWarnSeriesResponse(errors.New("warning")), 795 storeSeriesResponse(t, labels.FromStrings("a", "b"), []sample{{1, 1}, {2, 2}, {3, 3}}), 796 }, 797 RespDuration: 10 * time.Second, 798 }, 799 ExtLset: []labels.Labels{labels.FromStrings("ext", "1")}, 800 MinTime: 1, 801 MaxTime: 300, 802 }, 803 }, 804 req: &storepb.SeriesRequest{ 805 MinTime: 1, 806 MaxTime: 300, 807 Matchers: []storepb.LabelMatcher{{Name: "ext", Value: "1", Type: storepb.LabelMatcher_EQ}}, 808 PartialResponseDisabled: true, 809 PartialResponseStrategy: storepb.PartialResponseStrategy_ABORT, 810 }, 811 expectedErr: errors.New("rpc error: code = Aborted desc = warning"), 812 }, 813 { 814 title: "partial response disabled; 1st store is slow on 2nd series, 2nd store is fast;", 815 storeAPIs: []Client{ 816 &storetestutil.TestClient{ 817 StoreClient: &mockedStoreAPI{ 818 RespSeries: []*storepb.SeriesResponse{ 819 storepb.NewWarnSeriesResponse(errors.New("warning")), 820 storeSeriesResponse(t, labels.FromStrings("a", "b"), []sample{{1, 1}, {2, 2}, {3, 3}}), 821 storeSeriesResponse(t, labels.FromStrings("a", "b"), []sample{{3, 1}, {4, 2}, {5, 3}}), 822 storeSeriesResponse(t, labels.FromStrings("a", "b"), []sample{{6, 1}, {7, 2}, {8, 3}}), 823 }, 824 RespDuration: 10 * time.Second, 825 SlowSeriesIndex: 2, 826 }, 827 ExtLset: []labels.Labels{labels.FromStrings("ext", "1")}, 828 MinTime: 1, 829 MaxTime: 300, 830 }, 831 &storetestutil.TestClient{ 832 StoreClient: &mockedStoreAPI{ 833 RespSeries: []*storepb.SeriesResponse{ 834 storepb.NewWarnSeriesResponse(errors.New("warning")), 835 storeSeriesResponse(t, labels.FromStrings("a", "b"), []sample{{1, 1}, {2, 2}, {3, 3}}), 836 }, 837 }, 838 ExtLset: []labels.Labels{labels.FromStrings("ext", "1")}, 839 MinTime: 1, 840 MaxTime: 300, 841 }, 842 }, 843 req: &storepb.SeriesRequest{ 844 MinTime: 1, 845 MaxTime: 300, 846 Matchers: []storepb.LabelMatcher{{Name: "ext", Value: "1", Type: storepb.LabelMatcher_EQ}}, 847 PartialResponseDisabled: true, 848 PartialResponseStrategy: storepb.PartialResponseStrategy_ABORT, 849 }, 850 expectedErr: errors.New("rpc error: code = Aborted desc = warning"), 851 }, 852 { 853 title: "partial response disabled; 1st store is fast to respond, 2nd store is slow on 2nd series;", 854 storeAPIs: []Client{ 855 &storetestutil.TestClient{ 856 StoreClient: &mockedStoreAPI{ 857 RespSeries: []*storepb.SeriesResponse{ 858 storepb.NewWarnSeriesResponse(errors.New("warning")), 859 storeSeriesResponse(t, labels.FromStrings("a", "b"), []sample{{1, 1}, {2, 2}, {3, 3}}), 860 storeSeriesResponse(t, labels.FromStrings("a", "b"), []sample{{3, 1}, {4, 2}, {5, 3}}), 861 storeSeriesResponse(t, labels.FromStrings("a", "b"), []sample{{6, 1}, {7, 2}, {8, 3}}), 862 }, 863 }, 864 ExtLset: []labels.Labels{labels.FromStrings("ext", "1")}, 865 MinTime: 1, 866 MaxTime: 300, 867 }, 868 &storetestutil.TestClient{ 869 StoreClient: &mockedStoreAPI{ 870 RespSeries: []*storepb.SeriesResponse{ 871 storepb.NewWarnSeriesResponse(errors.New("warning")), 872 storeSeriesResponse(t, labels.FromStrings("a", "b"), []sample{{1, 1}, {2, 2}, {3, 3}}), 873 }, 874 RespDuration: 10 * time.Second, 875 SlowSeriesIndex: 2, 876 }, 877 ExtLset: []labels.Labels{labels.FromStrings("ext", "1")}, 878 MinTime: 1, 879 MaxTime: 300, 880 }, 881 }, 882 req: &storepb.SeriesRequest{ 883 MinTime: 1, 884 MaxTime: 300, 885 Matchers: []storepb.LabelMatcher{{Name: "ext", Value: "1", Type: storepb.LabelMatcher_EQ}}, 886 PartialResponseDisabled: true, 887 PartialResponseStrategy: storepb.PartialResponseStrategy_ABORT, 888 }, 889 expectedErr: errors.New("rpc error: code = Aborted desc = warning"), 890 }, 891 { 892 title: "partial response enabled; 1st store is slow to respond, 2nd store is fast;", 893 storeAPIs: []Client{ 894 &storetestutil.TestClient{ 895 StoreClient: &mockedStoreAPI{ 896 RespSeries: []*storepb.SeriesResponse{ 897 storepb.NewWarnSeriesResponse(errors.New("warning")), 898 storeSeriesResponse(t, labels.FromStrings("a", "b"), []sample{{1, 1}, {2, 2}, {3, 3}}), 899 }, 900 RespDuration: 10 * time.Second, 901 }, 902 ExtLset: []labels.Labels{labels.FromStrings("ext", "1")}, 903 MinTime: 1, 904 MaxTime: 300, 905 }, 906 &storetestutil.TestClient{ 907 StoreClient: &mockedStoreAPI{ 908 RespSeries: []*storepb.SeriesResponse{ 909 storepb.NewWarnSeriesResponse(errors.New("warning")), 910 storeSeriesResponse(t, labels.FromStrings("b", "c"), []sample{{1, 1}, {2, 2}, {3, 3}}), 911 }, 912 }, 913 ExtLset: []labels.Labels{labels.FromStrings("ext", "1")}, 914 MinTime: 1, 915 MaxTime: 300, 916 }, 917 }, 918 req: &storepb.SeriesRequest{ 919 MinTime: 1, 920 MaxTime: 300, 921 Matchers: []storepb.LabelMatcher{{Name: "ext", Value: "1", Type: storepb.LabelMatcher_EQ}}, 922 }, 923 expectedSeries: []rawSeries{ 924 { 925 lset: labels.FromStrings("b", "c"), 926 chunks: [][]sample{{{1, 1}, {2, 2}, {3, 3}}}, 927 }, 928 }, 929 expectedWarningsLen: 2, 930 }, 931 { 932 title: "partial response enabled; 1st store is fast, 2nd store is slow;", 933 storeAPIs: []Client{ 934 &storetestutil.TestClient{ 935 StoreClient: &mockedStoreAPI{ 936 RespSeries: []*storepb.SeriesResponse{ 937 storepb.NewWarnSeriesResponse(errors.New("warning")), 938 storeSeriesResponse(t, labels.FromStrings("a", "b"), []sample{{1, 1}, {2, 2}, {3, 3}}), 939 }, 940 }, 941 ExtLset: []labels.Labels{labels.FromStrings("ext", "1")}, 942 MinTime: 1, 943 MaxTime: 300, 944 }, 945 &storetestutil.TestClient{ 946 StoreClient: &mockedStoreAPI{ 947 RespSeries: []*storepb.SeriesResponse{ 948 storepb.NewWarnSeriesResponse(errors.New("warning")), 949 storeSeriesResponse(t, labels.FromStrings("b", "c"), []sample{{1, 1}, {2, 2}, {3, 3}}), 950 }, 951 RespDuration: 10 * time.Second, 952 }, 953 ExtLset: []labels.Labels{labels.FromStrings("ext", "1")}, 954 MinTime: 1, 955 MaxTime: 300, 956 }, 957 }, 958 req: &storepb.SeriesRequest{ 959 MinTime: 1, 960 MaxTime: 300, 961 Matchers: []storepb.LabelMatcher{{Name: "ext", Value: "1", Type: storepb.LabelMatcher_EQ}}, 962 }, 963 expectedSeries: []rawSeries{ 964 { 965 lset: labels.FromStrings("a", "b"), 966 chunks: [][]sample{{{1, 1}, {2, 2}, {3, 3}}}, 967 }, 968 }, 969 expectedWarningsLen: 2, 970 }, 971 { 972 title: "partial response enabled; 1st store is fast, 2-3 is slow, 4th is fast;", 973 storeAPIs: []Client{ 974 &storetestutil.TestClient{ 975 StoreClient: &mockedStoreAPI{ 976 RespSeries: []*storepb.SeriesResponse{ 977 storepb.NewWarnSeriesResponse(errors.New("warning")), 978 storeSeriesResponse(t, labels.FromStrings("a", "b"), []sample{{1, 1}, {2, 2}, {3, 3}}), 979 }, 980 }, 981 ExtLset: []labels.Labels{labels.FromStrings("ext", "1")}, 982 MinTime: 1, 983 MaxTime: 300, 984 }, 985 &storetestutil.TestClient{ 986 StoreClient: &mockedStoreAPI{ 987 RespSeries: []*storepb.SeriesResponse{ 988 storepb.NewWarnSeriesResponse(errors.New("warning")), 989 storeSeriesResponse(t, labels.FromStrings("b", "c"), []sample{{1, 1}, {2, 2}, {3, 3}}), 990 }, 991 RespDuration: 10 * time.Second, 992 }, 993 ExtLset: []labels.Labels{labels.FromStrings("ext", "1")}, 994 MinTime: 1, 995 MaxTime: 300, 996 }, 997 &storetestutil.TestClient{ 998 StoreClient: &mockedStoreAPI{ 999 RespSeries: []*storepb.SeriesResponse{ 1000 storepb.NewWarnSeriesResponse(errors.New("warning")), 1001 storeSeriesResponse(t, labels.FromStrings("c", "d"), []sample{{1, 1}, {2, 2}, {3, 3}}), 1002 }, 1003 RespDuration: 10 * time.Second, 1004 }, 1005 ExtLset: []labels.Labels{labels.FromStrings("ext", "1")}, 1006 MinTime: 1, 1007 MaxTime: 300, 1008 }, 1009 &storetestutil.TestClient{ 1010 StoreClient: &mockedStoreAPI{ 1011 RespSeries: []*storepb.SeriesResponse{ 1012 storepb.NewWarnSeriesResponse(errors.New("warning")), 1013 storeSeriesResponse(t, labels.FromStrings("d", "f"), []sample{{1, 1}, {2, 2}, {3, 3}}), 1014 }, 1015 }, 1016 ExtLset: []labels.Labels{labels.FromStrings("ext", "1")}, 1017 MinTime: 1, 1018 MaxTime: 300, 1019 }, 1020 }, 1021 req: &storepb.SeriesRequest{ 1022 MinTime: 1, 1023 MaxTime: 300, 1024 Matchers: []storepb.LabelMatcher{{Name: "ext", Value: "1", Type: storepb.LabelMatcher_EQ}}, 1025 }, 1026 expectedSeries: []rawSeries{ 1027 { 1028 lset: labels.FromStrings("a", "b"), 1029 chunks: [][]sample{{{1, 1}, {2, 2}, {3, 3}}}, 1030 }, 1031 { 1032 lset: labels.FromStrings("d", "f"), 1033 chunks: [][]sample{{{1, 1}, {2, 2}, {3, 3}}}, 1034 }, 1035 }, 1036 expectedWarningsLen: 4, 1037 }, 1038 { 1039 title: "partial response enabled; 1st store is slow on 2nd series, 2nd store is fast", 1040 storeAPIs: []Client{ 1041 &storetestutil.TestClient{ 1042 StoreClient: &mockedStoreAPI{ 1043 RespSeries: []*storepb.SeriesResponse{ 1044 storepb.NewWarnSeriesResponse(errors.New("warning")), 1045 storeSeriesResponse(t, labels.FromStrings("a", "b"), []sample{{1, 1}, {2, 2}, {3, 3}}), 1046 storeSeriesResponse(t, labels.FromStrings("a", "b"), []sample{{4, 1}, {5, 2}, {6, 3}}), 1047 storeSeriesResponse(t, labels.FromStrings("a", "b"), []sample{{7, 1}, {8, 2}, {9, 3}}), 1048 }, 1049 RespDuration: 10 * time.Second, 1050 SlowSeriesIndex: 2, 1051 }, 1052 ExtLset: []labels.Labels{labels.FromStrings("ext", "1")}, 1053 MinTime: 1, 1054 MaxTime: 300, 1055 }, 1056 &storetestutil.TestClient{ 1057 StoreClient: &mockedStoreAPI{ 1058 RespSeries: []*storepb.SeriesResponse{ 1059 storepb.NewWarnSeriesResponse(errors.New("warning")), 1060 storeSeriesResponse(t, labels.FromStrings("b", "c"), []sample{{1, 1}, {2, 2}, {3, 3}}), 1061 }, 1062 }, 1063 ExtLset: []labels.Labels{labels.FromStrings("ext", "1")}, 1064 MinTime: 1, 1065 MaxTime: 300, 1066 }, 1067 }, 1068 req: &storepb.SeriesRequest{ 1069 MinTime: 1, 1070 MaxTime: 300, 1071 Matchers: []storepb.LabelMatcher{{Name: "ext", Value: "1", Type: storepb.LabelMatcher_EQ}}, 1072 }, 1073 expectedSeries: []rawSeries{ 1074 { 1075 lset: labels.FromStrings("a", "b"), 1076 chunks: [][]sample{{{1, 1}, {2, 2}, {3, 3}}}, 1077 }, 1078 { 1079 lset: labels.FromStrings("b", "c"), 1080 chunks: [][]sample{{{1, 1}, {2, 2}, {3, 3}}}, 1081 }, 1082 }, 1083 expectedWarningsLen: 3, 1084 }, 1085 { 1086 title: "partial response disabled; all stores respond 3s", 1087 storeAPIs: []Client{ 1088 &storetestutil.TestClient{ 1089 StoreClient: &mockedStoreAPI{ 1090 RespSeries: []*storepb.SeriesResponse{ 1091 storeSeriesResponse(t, labels.FromStrings("a", "b"), []sample{{1, 1}, {2, 2}, {3, 3}}), 1092 storeSeriesResponse(t, labels.FromStrings("a", "b"), []sample{{4, 1}, {5, 2}, {6, 3}}), 1093 storeSeriesResponse(t, labels.FromStrings("a", "b"), []sample{{7, 1}, {8, 2}, {9, 3}}), 1094 }, 1095 RespDuration: 3 * time.Second, 1096 }, 1097 ExtLset: []labels.Labels{labels.FromStrings("ext", "1")}, 1098 MinTime: 1, 1099 MaxTime: 300, 1100 }, 1101 }, 1102 req: &storepb.SeriesRequest{ 1103 MinTime: 1, 1104 MaxTime: 300, 1105 Matchers: []storepb.LabelMatcher{{Name: "ext", Value: "1", Type: storepb.LabelMatcher_EQ}}, 1106 PartialResponseDisabled: true, 1107 PartialResponseStrategy: storepb.PartialResponseStrategy_ABORT, 1108 }, 1109 expectedSeries: []rawSeries{ 1110 { 1111 lset: labels.FromStrings("a", "b"), 1112 chunks: [][]sample{{{1, 1}, {2, 2}, {3, 3}}}, 1113 }, 1114 }, 1115 expectedErr: errors.New("rpc error: code = Aborted desc = receive series from test: context deadline exceeded"), 1116 }, 1117 { 1118 title: "partial response enabled; all stores respond 3s", 1119 storeAPIs: []Client{ 1120 &storetestutil.TestClient{ 1121 StoreClient: &mockedStoreAPI{ 1122 RespSeries: []*storepb.SeriesResponse{ 1123 storeSeriesResponse(t, labels.FromStrings("a", "b"), []sample{{1, 1}, {2, 2}, {3, 3}}), 1124 storeSeriesResponse(t, labels.FromStrings("a", "b"), []sample{{4, 1}, {5, 2}, {6, 3}}), 1125 storeSeriesResponse(t, labels.FromStrings("a", "b"), []sample{{7, 1}, {8, 2}, {9, 3}}), 1126 }, 1127 RespDuration: 3 * time.Second, 1128 }, 1129 ExtLset: []labels.Labels{labels.FromStrings("ext", "1")}, 1130 MinTime: 1, 1131 MaxTime: 300, 1132 }, 1133 &storetestutil.TestClient{ 1134 StoreClient: &mockedStoreAPI{ 1135 RespSeries: []*storepb.SeriesResponse{ 1136 storeSeriesResponse(t, labels.FromStrings("b", "c"), []sample{{1, 1}, {2, 2}, {3, 3}}), 1137 storeSeriesResponse(t, labels.FromStrings("b", "c"), []sample{{4, 1}, {5, 2}, {6, 3}}), 1138 storeSeriesResponse(t, labels.FromStrings("b", "c"), []sample{{7, 1}, {8, 2}, {9, 3}}), 1139 }, 1140 RespDuration: 3 * time.Second, 1141 }, 1142 ExtLset: []labels.Labels{labels.FromStrings("ext", "1")}, 1143 MinTime: 1, 1144 MaxTime: 300, 1145 }, 1146 }, 1147 req: &storepb.SeriesRequest{ 1148 MinTime: 1, 1149 MaxTime: 300, 1150 Matchers: []storepb.LabelMatcher{{Name: "ext", Value: "1", Type: storepb.LabelMatcher_EQ}}, 1151 }, 1152 expectedSeries: []rawSeries{ 1153 { 1154 lset: labels.FromStrings("a", "b"), 1155 chunks: [][]sample{{{1, 1}, {2, 2}, {3, 3}}}, 1156 }, 1157 { 1158 lset: labels.FromStrings("b", "c"), 1159 chunks: [][]sample{{{1, 1}, {2, 2}, {3, 3}}}, 1160 }, 1161 }, 1162 expectedWarningsLen: 2, 1163 }, 1164 } { 1165 if ok := t.Run(tc.title, func(t *testing.T) { 1166 for _, strategy := range []RetrievalStrategy{EagerRetrieval, LazyRetrieval} { 1167 if ok := t.Run(string(strategy), func(t *testing.T) { 1168 q := NewProxyStore(nil, 1169 nil, 1170 func() []Client { return tc.storeAPIs }, 1171 component.Query, 1172 tc.selectorLabels, 1173 4*time.Second, strategy, 1174 ) 1175 1176 ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) 1177 defer cancel() 1178 s := newStoreSeriesServer(ctx) 1179 1180 t0 := time.Now() 1181 err := q.Series(tc.req, s) 1182 elapsedTime := time.Since(t0) 1183 if tc.expectedErr != nil { 1184 testutil.NotOk(t, err) 1185 testutil.Equals(t, tc.expectedErr.Error(), err.Error()) 1186 return 1187 } 1188 1189 testutil.Ok(t, err) 1190 1191 seriesEquals(t, tc.expectedSeries, s.SeriesSet) 1192 testutil.Equals(t, tc.expectedWarningsLen, len(s.Warnings), "got %v", s.Warnings) 1193 1194 testutil.Assert(t, elapsedTime < 5010*time.Millisecond, fmt.Sprintf("Request has taken %f, expected: <%d, it seems that responseTimeout doesn't work properly.", elapsedTime.Seconds(), 5)) 1195 1196 }); !ok { 1197 return 1198 } 1199 } 1200 }); !ok { 1201 return 1202 } 1203 } 1204 1205 // Wait until the last goroutine exits which is stuck on time.Sleep(). 1206 // Otherwise, goleak complains. 1207 time.Sleep(5 * time.Second) 1208 } 1209 1210 func TestProxyStore_Series_RequestParamsProxied(t *testing.T) { 1211 defer custom.TolerantVerifyLeak(t) 1212 1213 m := &mockedStoreAPI{ 1214 RespSeries: []*storepb.SeriesResponse{ 1215 storepb.NewWarnSeriesResponse(errors.New("warning")), 1216 }, 1217 } 1218 cls := []Client{ 1219 &storetestutil.TestClient{ 1220 StoreClient: m, 1221 ExtLset: []labels.Labels{labels.FromStrings("ext", "1")}, 1222 MinTime: 1, 1223 MaxTime: 300, 1224 }, 1225 } 1226 q := NewProxyStore(nil, 1227 nil, 1228 func() []Client { return cls }, 1229 component.Query, 1230 nil, 1231 1*time.Second, EagerRetrieval, 1232 ) 1233 1234 ctx := context.Background() 1235 s := newStoreSeriesServer(ctx) 1236 1237 req := &storepb.SeriesRequest{ 1238 MinTime: 1, 1239 MaxTime: 300, 1240 Matchers: []storepb.LabelMatcher{{Name: "ext", Value: "1", Type: storepb.LabelMatcher_EQ}}, 1241 PartialResponseDisabled: false, 1242 Aggregates: []storepb.Aggr{ 1243 storepb.Aggr_COUNTER, 1244 storepb.Aggr_COUNT, 1245 }, 1246 PartialResponseStrategy: storepb.PartialResponseStrategy_WARN, 1247 MaxResolutionWindow: 1234, 1248 } 1249 testutil.Ok(t, q.Series(req, s)) 1250 1251 testutil.Assert(t, proto.Equal(req, m.LastSeriesReq), "request was not proxied properly to underlying storeAPI: %s vs %s", req, m.LastSeriesReq) 1252 } 1253 1254 func TestProxyStore_Series_RegressionFillResponseChannel(t *testing.T) { 1255 defer custom.TolerantVerifyLeak(t) 1256 1257 var cls []Client 1258 for i := 0; i < 10; i++ { 1259 cls = append(cls, &storetestutil.TestClient{ 1260 StoreClient: &mockedStoreAPI{ 1261 RespError: errors.New("test error"), 1262 }, 1263 MinTime: 1, 1264 MaxTime: 300, 1265 }) 1266 cls = append(cls, &storetestutil.TestClient{ 1267 StoreClient: &mockedStoreAPI{ 1268 RespSeries: []*storepb.SeriesResponse{ 1269 storepb.NewWarnSeriesResponse(errors.New("warning")), 1270 storepb.NewWarnSeriesResponse(errors.New("warning")), 1271 storepb.NewWarnSeriesResponse(errors.New("warning")), 1272 storepb.NewWarnSeriesResponse(errors.New("warning")), 1273 storepb.NewWarnSeriesResponse(errors.New("warning")), 1274 storepb.NewWarnSeriesResponse(errors.New("warning")), 1275 storepb.NewWarnSeriesResponse(errors.New("warning")), 1276 storepb.NewWarnSeriesResponse(errors.New("warning")), 1277 storepb.NewWarnSeriesResponse(errors.New("warning")), 1278 storepb.NewWarnSeriesResponse(errors.New("warning")), 1279 }, 1280 }, 1281 MinTime: 1, 1282 MaxTime: 300, 1283 }) 1284 1285 } 1286 1287 q := NewProxyStore(nil, 1288 nil, 1289 func() []Client { return cls }, 1290 component.Query, 1291 labels.FromStrings("fed", "a"), 1292 5*time.Second, EagerRetrieval, 1293 ) 1294 1295 ctx := context.Background() 1296 s := newStoreSeriesServer(ctx) 1297 1298 testutil.Ok(t, q.Series( 1299 &storepb.SeriesRequest{ 1300 MinTime: 1, 1301 MaxTime: 300, 1302 Matchers: []storepb.LabelMatcher{{Name: "any", Value: ".*", Type: storepb.LabelMatcher_RE}}, 1303 PartialResponseStrategy: storepb.PartialResponseStrategy_WARN, 1304 }, s, 1305 )) 1306 testutil.Equals(t, 0, len(s.SeriesSet)) 1307 testutil.Equals(t, 110, len(s.Warnings)) 1308 } 1309 1310 func TestProxyStore_LabelValues(t *testing.T) { 1311 defer custom.TolerantVerifyLeak(t) 1312 1313 m1 := &mockedStoreAPI{ 1314 RespLabelValues: &storepb.LabelValuesResponse{ 1315 Values: []string{"1", "2"}, 1316 Warnings: []string{"warning"}, 1317 }, 1318 } 1319 cls := []Client{ 1320 &storetestutil.TestClient{StoreClient: m1}, 1321 &storetestutil.TestClient{StoreClient: &mockedStoreAPI{ 1322 RespLabelValues: &storepb.LabelValuesResponse{ 1323 Values: []string{"3", "4"}, 1324 }, 1325 }}, 1326 &storetestutil.TestClient{StoreClient: &mockedStoreAPI{ 1327 RespLabelValues: &storepb.LabelValuesResponse{ 1328 Values: []string{"5", "6"}, 1329 }}, 1330 MinTime: timestamp.FromTime(time.Now().Add(-1 * time.Minute)), 1331 MaxTime: timestamp.FromTime(time.Now()), 1332 }, 1333 } 1334 q := NewProxyStore(nil, 1335 nil, 1336 func() []Client { return cls }, 1337 component.Query, 1338 nil, 1339 0*time.Second, EagerRetrieval, 1340 ) 1341 1342 ctx := context.Background() 1343 req := &storepb.LabelValuesRequest{ 1344 Label: "a", 1345 PartialResponseDisabled: true, 1346 Start: timestamp.FromTime(minTime), 1347 End: timestamp.FromTime(maxTime), 1348 } 1349 resp, err := q.LabelValues(ctx, req) 1350 testutil.Ok(t, err) 1351 testutil.Assert(t, proto.Equal(req, m1.LastLabelValuesReq), "request was not proxied properly to underlying storeAPI: %s vs %s", req, m1.LastLabelValuesReq) 1352 1353 testutil.Equals(t, []string{"1", "2", "3", "4", "5", "6"}, resp.Values) 1354 testutil.Equals(t, 1, len(resp.Warnings)) 1355 1356 // Request outside the time range of the last store client. 1357 req = &storepb.LabelValuesRequest{ 1358 Label: "a", 1359 PartialResponseDisabled: true, 1360 Start: timestamp.FromTime(minTime), 1361 End: timestamp.FromTime(time.Now().Add(-1 * time.Hour)), 1362 } 1363 resp, err = q.LabelValues(ctx, req) 1364 testutil.Ok(t, err) 1365 testutil.Assert(t, proto.Equal(req, m1.LastLabelValuesReq), "request was not proxied properly to underlying storeAPI: %s vs %s", req, m1.LastLabelValuesReq) 1366 1367 testutil.Equals(t, []string{"1", "2", "3", "4"}, resp.Values) 1368 testutil.Equals(t, 1, len(resp.Warnings)) 1369 } 1370 1371 func TestProxyStore_LabelNames(t *testing.T) { 1372 defer custom.TolerantVerifyLeak(t) 1373 1374 for _, tc := range []struct { 1375 title string 1376 storeAPIs []Client 1377 1378 req *storepb.LabelNamesRequest 1379 storeDebugMatchers [][]*labels.Matcher 1380 1381 expectedNames []string 1382 expectedErr error 1383 expectedWarningsLen int 1384 }{ 1385 { 1386 title: "label_names partial response disabled", 1387 storeAPIs: []Client{ 1388 &storetestutil.TestClient{ 1389 StoreClient: &mockedStoreAPI{ 1390 RespLabelNames: &storepb.LabelNamesResponse{ 1391 Names: []string{"a", "b"}, 1392 }, 1393 }, 1394 }, 1395 &storetestutil.TestClient{ 1396 StoreClient: &mockedStoreAPI{ 1397 RespLabelNames: &storepb.LabelNamesResponse{ 1398 Names: []string{"a", "c", "d"}, 1399 }, 1400 }, 1401 }, 1402 }, 1403 req: &storepb.LabelNamesRequest{ 1404 Start: timestamp.FromTime(minTime), 1405 End: timestamp.FromTime(maxTime), 1406 PartialResponseDisabled: true, 1407 }, 1408 expectedNames: []string{"a", "b", "c", "d"}, 1409 expectedWarningsLen: 0, 1410 }, 1411 { 1412 title: "label_names partial response disabled, but returns error", 1413 storeAPIs: []Client{ 1414 &storetestutil.TestClient{ 1415 StoreClient: &mockedStoreAPI{ 1416 RespLabelNames: &storepb.LabelNamesResponse{ 1417 Names: []string{"a", "b"}, 1418 }, 1419 }, 1420 }, 1421 &storetestutil.TestClient{ 1422 StoreClient: &mockedStoreAPI{ 1423 RespError: errors.New("error!"), 1424 }, 1425 Name: "test", 1426 }, 1427 }, 1428 req: &storepb.LabelNamesRequest{ 1429 Start: timestamp.FromTime(minTime), 1430 End: timestamp.FromTime(maxTime), 1431 PartialResponseDisabled: true, 1432 }, 1433 expectedErr: errors.New("fetch label names from store test: error!"), 1434 }, 1435 { 1436 title: "label_names partial response enabled", 1437 storeAPIs: []Client{ 1438 &storetestutil.TestClient{ 1439 StoreClient: &mockedStoreAPI{ 1440 RespLabelNames: &storepb.LabelNamesResponse{ 1441 Names: []string{"a", "b"}, 1442 }, 1443 }, 1444 }, 1445 &storetestutil.TestClient{ 1446 StoreClient: &mockedStoreAPI{ 1447 RespError: errors.New("error!"), 1448 }, 1449 }, 1450 }, 1451 req: &storepb.LabelNamesRequest{ 1452 Start: timestamp.FromTime(minTime), 1453 End: timestamp.FromTime(maxTime), 1454 PartialResponseDisabled: false, 1455 }, 1456 expectedNames: []string{"a", "b"}, 1457 expectedWarningsLen: 1, 1458 }, 1459 { 1460 title: "stores filtered by time range", 1461 storeAPIs: []Client{ 1462 &storetestutil.TestClient{ 1463 StoreClient: &mockedStoreAPI{ 1464 RespLabelNames: &storepb.LabelNamesResponse{ 1465 Names: []string{"a", "b"}, 1466 }, 1467 }, 1468 MinTime: timestamp.FromTime(time.Now().Add(-4 * time.Hour)), 1469 MaxTime: timestamp.FromTime(time.Now().Add(-3 * time.Hour)), 1470 }, 1471 &storetestutil.TestClient{ 1472 StoreClient: &mockedStoreAPI{ 1473 RespLabelNames: &storepb.LabelNamesResponse{ 1474 Names: []string{"c", "d"}, 1475 }, 1476 }, 1477 MinTime: timestamp.FromTime(time.Now().Add(-2 * time.Hour)), 1478 MaxTime: timestamp.FromTime(time.Now().Add(-1 * time.Hour)), 1479 }, 1480 }, 1481 req: &storepb.LabelNamesRequest{ 1482 Start: timestamp.FromTime(time.Now().Add(-1 * time.Minute)), 1483 End: timestamp.FromTime(time.Now()), 1484 PartialResponseDisabled: false, 1485 }, 1486 expectedNames: nil, 1487 expectedWarningsLen: 0, 1488 }, 1489 { 1490 title: "store matchers blocks", 1491 storeAPIs: []Client{ 1492 &storetestutil.TestClient{ 1493 StoreClient: &mockedStoreAPI{ 1494 RespLabelNames: &storepb.LabelNamesResponse{ 1495 Names: []string{"a", "b"}, 1496 }, 1497 }, 1498 Name: "testaddr", 1499 }, 1500 }, 1501 req: &storepb.LabelNamesRequest{ 1502 Start: timestamp.FromTime(minTime), 1503 End: timestamp.FromTime(maxTime), 1504 PartialResponseDisabled: false, 1505 }, 1506 storeDebugMatchers: [][]*labels.Matcher{{labels.MustNewMatcher(labels.MatchEqual, "__address__", "foo")}}, 1507 expectedNames: nil, 1508 expectedWarningsLen: 0, 1509 }, 1510 { 1511 title: "store matchers allows", 1512 storeAPIs: []Client{ 1513 &storetestutil.TestClient{ 1514 StoreClient: &mockedStoreAPI{ 1515 RespLabelNames: &storepb.LabelNamesResponse{ 1516 Names: []string{"a", "b"}, 1517 }, 1518 }, 1519 Name: "testaddr", 1520 }, 1521 }, 1522 req: &storepb.LabelNamesRequest{ 1523 Start: timestamp.FromTime(minTime), 1524 End: timestamp.FromTime(maxTime), 1525 PartialResponseDisabled: false, 1526 }, 1527 storeDebugMatchers: [][]*labels.Matcher{{labels.MustNewMatcher(labels.MatchEqual, "__address__", "testaddr")}}, 1528 expectedNames: []string{"a", "b"}, 1529 expectedWarningsLen: 0, 1530 }, 1531 } { 1532 if ok := t.Run(tc.title, func(t *testing.T) { 1533 q := NewProxyStore( 1534 nil, 1535 nil, 1536 func() []Client { return tc.storeAPIs }, 1537 component.Query, 1538 nil, 1539 5*time.Second, EagerRetrieval, 1540 ) 1541 1542 ctx := context.Background() 1543 if len(tc.storeDebugMatchers) > 0 { 1544 ctx = context.WithValue(ctx, StoreMatcherKey, tc.storeDebugMatchers) 1545 } 1546 resp, err := q.LabelNames(ctx, tc.req) 1547 if tc.expectedErr != nil { 1548 testutil.NotOk(t, err) 1549 testutil.Equals(t, tc.expectedErr.Error(), err.Error()) 1550 return 1551 } 1552 testutil.Ok(t, err) 1553 1554 testutil.Equals(t, tc.expectedNames, resp.Names) 1555 testutil.Equals(t, tc.expectedWarningsLen, len(resp.Warnings), "got %v", resp.Warnings) 1556 }); !ok { 1557 return 1558 } 1559 } 1560 } 1561 1562 type rawSeries struct { 1563 lset labels.Labels 1564 chunks [][]sample 1565 } 1566 1567 func seriesEquals(t *testing.T, expected []rawSeries, got []storepb.Series) { 1568 testutil.Equals(t, len(expected), len(got), "got unexpected number of series: \n %v", got) 1569 1570 ret := make([]rawSeries, len(got)) 1571 for i, s := range got { 1572 r := rawSeries{ 1573 lset: labelpb.ZLabelsToPromLabels(s.Labels), 1574 } 1575 for _, chk := range s.Chunks { 1576 var samples []sample 1577 1578 c, err := chunkenc.FromData(chunkenc.EncXOR, chk.Raw.Data) 1579 testutil.Ok(t, err) 1580 1581 iter := c.Iterator(nil) 1582 for iter.Next() != chunkenc.ValNone { 1583 tv, v := iter.At() 1584 samples = append(samples, sample{tv, v}) 1585 } 1586 testutil.Ok(t, iter.Err()) 1587 1588 r.chunks = append(r.chunks, samples) 1589 } 1590 ret[i] = r 1591 } 1592 1593 for i := range ret { 1594 testutil.Equals(t, expected[i], ret[i]) 1595 } 1596 } 1597 1598 func TestStoreMatches(t *testing.T) { 1599 for _, c := range []struct { 1600 s Client 1601 mint, maxt int64 1602 ms []*labels.Matcher 1603 1604 expectedMatch bool 1605 expectedReason string 1606 }{ 1607 { 1608 s: &storetestutil.TestClient{ExtLset: []labels.Labels{labels.FromStrings("a", "b")}}, 1609 ms: []*labels.Matcher{ 1610 labels.MustNewMatcher(labels.MatchEqual, "b", "1"), 1611 }, 1612 maxt: -1, 1613 expectedMatch: false, 1614 expectedReason: "does not have data within this time period: [0,-1]. Store time ranges: [0,0]", 1615 }, 1616 { 1617 s: &storetestutil.TestClient{ExtLset: []labels.Labels{labels.FromStrings("a", "b")}}, 1618 ms: []*labels.Matcher{ 1619 labels.MustNewMatcher(labels.MatchEqual, "b", "1"), 1620 }, 1621 maxt: 1, 1622 expectedMatch: true, 1623 }, 1624 { 1625 s: &storetestutil.TestClient{MinTime: 100, MaxTime: 200}, 1626 mint: 201, 1627 maxt: 300, 1628 expectedMatch: false, 1629 expectedReason: "does not have data within this time period: [201,300]. Store time ranges: [100,200]", 1630 }, 1631 { 1632 s: &storetestutil.TestClient{MinTime: 100, MaxTime: 200}, 1633 mint: 200, 1634 maxt: 300, 1635 expectedMatch: true, 1636 }, 1637 { 1638 s: &storetestutil.TestClient{MinTime: 100, MaxTime: 200}, 1639 mint: 50, 1640 maxt: 99, 1641 expectedMatch: false, 1642 expectedReason: "does not have data within this time period: [50,99]. Store time ranges: [100,200]", 1643 }, 1644 { 1645 s: &storetestutil.TestClient{MinTime: 100, MaxTime: 200}, 1646 mint: 50, 1647 maxt: 101, 1648 expectedMatch: true, 1649 }, 1650 { 1651 s: &storetestutil.TestClient{ExtLset: []labels.Labels{labels.FromStrings("a", "b")}}, 1652 ms: []*labels.Matcher{ 1653 labels.MustNewMatcher(labels.MatchEqual, "a", "b"), 1654 }, 1655 maxt: 1, 1656 expectedMatch: true, 1657 }, 1658 { 1659 s: &storetestutil.TestClient{ExtLset: []labels.Labels{labels.FromStrings("a", "b")}}, 1660 ms: []*labels.Matcher{ 1661 labels.MustNewMatcher(labels.MatchEqual, "a", "c"), 1662 }, 1663 maxt: 1, 1664 expectedMatch: false, 1665 expectedReason: "external labels [{a=\"b\"}] does not match request label matchers: [a=\"c\"]", 1666 }, 1667 { 1668 s: &storetestutil.TestClient{ExtLset: []labels.Labels{labels.FromStrings("a", "b")}}, 1669 ms: []*labels.Matcher{ 1670 labels.MustNewMatcher(labels.MatchRegexp, "a", "b|c"), 1671 }, 1672 maxt: 1, 1673 expectedMatch: true, 1674 }, 1675 { 1676 s: &storetestutil.TestClient{ExtLset: []labels.Labels{labels.FromStrings("a", "b")}}, 1677 ms: []*labels.Matcher{ 1678 labels.MustNewMatcher(labels.MatchNotRegexp, "a", ""), 1679 }, 1680 maxt: 1, 1681 expectedMatch: true, 1682 }, 1683 { 1684 s: &storetestutil.TestClient{ExtLset: []labels.Labels{ 1685 labels.FromStrings("a", "b"), 1686 labels.FromStrings("a", "c"), 1687 labels.FromStrings("a", "d"), 1688 }}, 1689 ms: []*labels.Matcher{ 1690 labels.MustNewMatcher(labels.MatchEqual, "a", "e"), 1691 }, 1692 maxt: 1, 1693 expectedMatch: false, 1694 expectedReason: "external labels [{a=\"b\"} {a=\"c\"} {a=\"d\"}] does not match request label matchers: [a=\"e\"]", 1695 }, 1696 { 1697 s: &storetestutil.TestClient{ExtLset: []labels.Labels{ 1698 labels.FromStrings("a", "b"), 1699 labels.FromStrings("a", "c"), 1700 labels.FromStrings("a", "d"), 1701 }}, 1702 ms: []*labels.Matcher{ 1703 labels.MustNewMatcher(labels.MatchEqual, "a", "c"), 1704 }, 1705 maxt: 1, 1706 expectedMatch: true, 1707 }, 1708 { 1709 s: &storetestutil.TestClient{ExtLset: []labels.Labels{ 1710 labels.FromStrings("a", "b"), 1711 labels.FromStrings("a", "c"), 1712 labels.FromStrings("a", "d"), 1713 }}, 1714 ms: []*labels.Matcher{ 1715 labels.MustNewMatcher(labels.MatchNotRegexp, "a", ""), 1716 }, 1717 maxt: 1, 1718 expectedMatch: true, 1719 }, 1720 } { 1721 t.Run("", func(t *testing.T) { 1722 ok, reason := storeMatches(context.TODO(), c.s, true, c.mint, c.maxt, c.ms...) 1723 testutil.Equals(t, c.expectedMatch, ok) 1724 testutil.Equals(t, c.expectedReason, reason) 1725 1726 }) 1727 } 1728 } 1729 1730 // storeSeriesServer is test gRPC storeAPI series server. 1731 type storeSeriesServer struct { 1732 // This field just exist to pseudo-implement the unused methods of the interface. 1733 storepb.Store_SeriesServer 1734 1735 ctx context.Context 1736 1737 SeriesSet []storepb.Series 1738 Warnings []string 1739 HintsSet []*types.Any 1740 1741 Size int64 1742 } 1743 1744 func newStoreSeriesServer(ctx context.Context) *storeSeriesServer { 1745 return &storeSeriesServer{ctx: ctx} 1746 } 1747 1748 func (s *storeSeriesServer) Send(r *storepb.SeriesResponse) error { 1749 s.Size += int64(r.Size()) 1750 1751 if r.GetWarning() != "" { 1752 s.Warnings = append(s.Warnings, r.GetWarning()) 1753 return nil 1754 } 1755 1756 if r.GetSeries() != nil { 1757 s.SeriesSet = append(s.SeriesSet, *r.GetSeries()) 1758 return nil 1759 } 1760 1761 if r.GetHints() != nil { 1762 s.HintsSet = append(s.HintsSet, r.GetHints()) 1763 return nil 1764 } 1765 1766 // Unsupported field, skip. 1767 return nil 1768 } 1769 1770 func (s *storeSeriesServer) Context() context.Context { 1771 return s.ctx 1772 } 1773 1774 // mockedStoreAPI is test gRPC store API client. 1775 type mockedStoreAPI struct { 1776 RespSeries []*storepb.SeriesResponse 1777 RespLabelValues *storepb.LabelValuesResponse 1778 RespLabelNames *storepb.LabelNamesResponse 1779 RespError error 1780 RespDuration time.Duration 1781 // Index of series in store to slow response. 1782 SlowSeriesIndex int 1783 1784 LastSeriesReq *storepb.SeriesRequest 1785 LastLabelValuesReq *storepb.LabelValuesRequest 1786 LastLabelNamesReq *storepb.LabelNamesRequest 1787 1788 // injectedError will be injected into Recv() if not nil. 1789 injectedError error 1790 injectedErrorIndex int 1791 } 1792 1793 func (s *mockedStoreAPI) Info(context.Context, *storepb.InfoRequest, ...grpc.CallOption) (*storepb.InfoResponse, error) { 1794 return nil, status.Error(codes.Unimplemented, "not implemented") 1795 } 1796 1797 func (s *mockedStoreAPI) Series(ctx context.Context, req *storepb.SeriesRequest, _ ...grpc.CallOption) (storepb.Store_SeriesClient, error) { 1798 s.LastSeriesReq = req 1799 return &storetestutil.StoreSeriesClient{InjectedErrorIndex: s.injectedErrorIndex, InjectedError: s.injectedError, Ctx: ctx, RespSet: s.RespSeries, RespDur: s.RespDuration, SlowSeriesIndex: s.SlowSeriesIndex}, s.RespError 1800 } 1801 1802 func (s *mockedStoreAPI) LabelNames(_ context.Context, req *storepb.LabelNamesRequest, _ ...grpc.CallOption) (*storepb.LabelNamesResponse, error) { 1803 s.LastLabelNamesReq = req 1804 1805 return s.RespLabelNames, s.RespError 1806 } 1807 1808 func (s *mockedStoreAPI) LabelValues(_ context.Context, req *storepb.LabelValuesRequest, _ ...grpc.CallOption) (*storepb.LabelValuesResponse, error) { 1809 s.LastLabelValuesReq = req 1810 1811 return s.RespLabelValues, s.RespError 1812 } 1813 1814 // storeSeriesResponse creates test storepb.SeriesResponse that includes series with single chunk that stores all the given samples. 1815 func storeSeriesResponse(t testing.TB, lset labels.Labels, smplChunks ...[]sample) *storepb.SeriesResponse { 1816 var s storepb.Series 1817 1818 s.Labels = append(s.Labels, labelpb.ZLabelsFromPromLabels(lset)...) 1819 1820 for _, smpls := range smplChunks { 1821 c := chunkenc.NewXORChunk() 1822 a, err := c.Appender() 1823 testutil.Ok(t, err) 1824 1825 for _, smpl := range smpls { 1826 a.Append(smpl.t, smpl.v) 1827 } 1828 1829 ch := storepb.AggrChunk{ 1830 MinTime: smpls[0].t, 1831 MaxTime: smpls[len(smpls)-1].t, 1832 Raw: &storepb.Chunk{Type: storepb.Chunk_XOR, Data: c.Bytes()}, 1833 } 1834 1835 s.Chunks = append(s.Chunks, ch) 1836 } 1837 return storepb.NewSeriesResponse(&s) 1838 } 1839 1840 func TestProxySeries(t *testing.T) { 1841 tb := testutil.NewTB(t) 1842 storetestutil.RunSeriesInterestingCases(tb, 200e3, 200e3, func(t testutil.TB, samplesPerSeries, series int) { 1843 benchProxySeries(t, samplesPerSeries, series) 1844 }) 1845 } 1846 1847 func BenchmarkProxySeries(b *testing.B) { 1848 tb := testutil.NewTB(b) 1849 storetestutil.RunSeriesInterestingCases(tb, 10e6, 10e5, func(t testutil.TB, samplesPerSeries, series int) { 1850 benchProxySeries(t, samplesPerSeries, series) 1851 }) 1852 } 1853 1854 func benchProxySeries(t testutil.TB, totalSamples, totalSeries int) { 1855 tmpDir := t.TempDir() 1856 1857 const numOfClients = 4 1858 1859 samplesPerSeriesPerClient := totalSamples / numOfClients 1860 if samplesPerSeriesPerClient == 0 { 1861 samplesPerSeriesPerClient = 1 1862 } 1863 seriesPerClient := totalSeries / numOfClients 1864 if seriesPerClient == 0 { 1865 seriesPerClient = 1 1866 } 1867 1868 random := rand.New(rand.NewSource(120)) 1869 clients := make([]Client, numOfClients) 1870 for j := range clients { 1871 var resps []*storepb.SeriesResponse 1872 1873 head, created := storetestutil.CreateHeadWithSeries(t, j, storetestutil.HeadGenOptions{ 1874 TSDBDir: filepath.Join(tmpDir, fmt.Sprintf("%d", j)), 1875 SamplesPerSeries: samplesPerSeriesPerClient, 1876 Series: seriesPerClient, 1877 Random: random, 1878 SkipChunks: t.IsBenchmark(), 1879 }) 1880 testutil.Ok(t, head.Close()) 1881 1882 for i := 0; i < len(created); i++ { 1883 resps = append(resps, storepb.NewSeriesResponse(created[i])) 1884 } 1885 1886 clients[j] = &storetestutil.TestClient{ 1887 StoreClient: &mockedStoreAPI{ 1888 RespSeries: resps, 1889 }, 1890 MinTime: math.MinInt64, 1891 MaxTime: math.MaxInt64, 1892 WithoutReplicaLabelsEnabled: true, 1893 } 1894 } 1895 1896 logger := log.NewNopLogger() 1897 store := &ProxyStore{ 1898 logger: logger, 1899 stores: func() []Client { return clients }, 1900 metrics: newProxyStoreMetrics(nil), 1901 responseTimeout: 5 * time.Second, 1902 retrievalStrategy: EagerRetrieval, 1903 } 1904 1905 var allResps []*storepb.SeriesResponse 1906 var expected []*storepb.Series 1907 lastLabels := storepb.Series{} 1908 for _, c := range clients { 1909 m := c.(*storetestutil.TestClient).StoreClient.(*mockedStoreAPI) 1910 1911 // NOTE: Proxy will merge all series with same labels without any frame limit (https://github.com/thanos-io/thanos/issues/2332). 1912 for _, r := range m.RespSeries { 1913 allResps = append(allResps, r) 1914 1915 x := storepb.Series{Labels: r.GetSeries().Labels} 1916 if x.String() == lastLabels.String() { 1917 expected[len(expected)-1].Chunks = append(expected[len(expected)-1].Chunks, r.GetSeries().Chunks...) 1918 continue 1919 } 1920 lastLabels = x 1921 expected = append(expected, r.GetSeries()) 1922 } 1923 1924 } 1925 1926 chunkLen := len(allResps[len(allResps)-1].GetSeries().Chunks) 1927 var maxTime int64 1928 if len(allResps[len(allResps)-1].GetSeries().Chunks) == 0 { 1929 maxTime = math.MaxInt64 1930 } else { 1931 maxTime = allResps[len(allResps)-1].GetSeries().Chunks[chunkLen-1].MaxTime 1932 } 1933 storetestutil.TestServerSeries(t, store, 1934 &storetestutil.SeriesCase{ 1935 Name: fmt.Sprintf("%d client with %d samples, %d series each", numOfClients, samplesPerSeriesPerClient, seriesPerClient), 1936 Req: &storepb.SeriesRequest{ 1937 MinTime: 0, 1938 MaxTime: maxTime, 1939 Matchers: []storepb.LabelMatcher{ 1940 {Type: storepb.LabelMatcher_EQ, Name: "foo", Value: "bar"}, 1941 }, 1942 }, 1943 ExpectedSeries: expected, 1944 }, 1945 ) 1946 1947 // Change client to one, containing all series. 1948 store.stores = func() []Client { 1949 return []Client{&storetestutil.TestClient{ 1950 StoreClient: &mockedStoreAPI{ 1951 // All responses. 1952 RespSeries: allResps, 1953 }, 1954 ExtLset: []labels.Labels{labels.FromStrings("ext1", "1")}, 1955 MinTime: math.MinInt64, 1956 MaxTime: math.MaxInt64, 1957 WithoutReplicaLabelsEnabled: true, 1958 }} 1959 } 1960 1961 // In this we expect exactly the same response as input. 1962 expected = expected[:0] 1963 for _, r := range allResps { 1964 expected = append(expected, r.GetSeries()) 1965 } 1966 storetestutil.TestServerSeries(t, store, 1967 &storetestutil.SeriesCase{ 1968 Name: fmt.Sprintf("single client with %d samples, %d series", totalSamples, totalSeries), 1969 Req: &storepb.SeriesRequest{ 1970 MinTime: 0, 1971 MaxTime: maxTime, 1972 Matchers: []storepb.LabelMatcher{ 1973 {Type: storepb.LabelMatcher_EQ, Name: "foo", Value: "bar"}, 1974 }, 1975 }, 1976 ExpectedSeries: expected, 1977 }, 1978 ) 1979 } 1980 1981 func TestProxyStore_NotLeakingOnPrematureFinish(t *testing.T) { 1982 defer custom.TolerantVerifyLeak(t) 1983 1984 clients := []Client{ 1985 &storetestutil.TestClient{ 1986 StoreClient: &mockedStoreAPI{ 1987 RespSeries: []*storepb.SeriesResponse{ 1988 // Ensure more than 10 (internal respCh channel). 1989 storeSeriesResponse(t, labels.FromStrings("a", "a"), []sample{{0, 0}, {2, 1}, {3, 2}}), 1990 storeSeriesResponse(t, labels.FromStrings("a", "b"), []sample{{0, 0}, {2, 1}, {3, 2}}), 1991 storeSeriesResponse(t, labels.FromStrings("a", "c"), []sample{{0, 0}, {2, 1}, {3, 2}}), 1992 storeSeriesResponse(t, labels.FromStrings("a", "d"), []sample{{0, 0}, {2, 1}, {3, 2}}), 1993 storeSeriesResponse(t, labels.FromStrings("a", "e"), []sample{{0, 0}, {2, 1}, {3, 2}}), 1994 storeSeriesResponse(t, labels.FromStrings("a", "f"), []sample{{0, 0}, {2, 1}, {3, 2}}), 1995 storeSeriesResponse(t, labels.FromStrings("a", "g"), []sample{{0, 0}, {2, 1}, {3, 2}}), 1996 storeSeriesResponse(t, labels.FromStrings("a", "h"), []sample{{0, 0}, {2, 1}, {3, 2}}), 1997 storeSeriesResponse(t, labels.FromStrings("a", "i"), []sample{{0, 0}, {2, 1}, {3, 2}}), 1998 storeSeriesResponse(t, labels.FromStrings("a", "j"), []sample{{0, 0}, {2, 1}, {3, 2}}), 1999 }, 2000 }, 2001 MinTime: math.MinInt64, 2002 MaxTime: math.MaxInt64, 2003 }, 2004 &storetestutil.TestClient{ 2005 StoreClient: &mockedStoreAPI{ 2006 RespSeries: []*storepb.SeriesResponse{ 2007 storeSeriesResponse(t, labels.FromStrings("b", "a"), []sample{{0, 0}, {2, 1}, {3, 2}}), 2008 storeSeriesResponse(t, labels.FromStrings("b", "b"), []sample{{0, 0}, {2, 1}, {3, 2}}), 2009 storeSeriesResponse(t, labels.FromStrings("b", "c"), []sample{{0, 0}, {2, 1}, {3, 2}}), 2010 storeSeriesResponse(t, labels.FromStrings("b", "d"), []sample{{0, 0}, {2, 1}, {3, 2}}), 2011 storeSeriesResponse(t, labels.FromStrings("b", "e"), []sample{{0, 0}, {2, 1}, {3, 2}}), 2012 storeSeriesResponse(t, labels.FromStrings("b", "f"), []sample{{0, 0}, {2, 1}, {3, 2}}), 2013 storeSeriesResponse(t, labels.FromStrings("b", "g"), []sample{{0, 0}, {2, 1}, {3, 2}}), 2014 storeSeriesResponse(t, labels.FromStrings("b", "h"), []sample{{0, 0}, {2, 1}, {3, 2}}), 2015 storeSeriesResponse(t, labels.FromStrings("b", "i"), []sample{{0, 0}, {2, 1}, {3, 2}}), 2016 storeSeriesResponse(t, labels.FromStrings("b", "j"), []sample{{0, 0}, {2, 1}, {3, 2}}), 2017 }, 2018 }, 2019 MinTime: math.MinInt64, 2020 MaxTime: math.MaxInt64, 2021 }, 2022 } 2023 2024 logger := log.NewNopLogger() 2025 p := &ProxyStore{ 2026 logger: logger, 2027 stores: func() []Client { return clients }, 2028 metrics: newProxyStoreMetrics(nil), 2029 responseTimeout: 0, 2030 retrievalStrategy: EagerRetrieval, 2031 } 2032 2033 t.Run("failling send", func(t *testing.T) { 2034 ctx, cancel := context.WithCancel(context.Background()) 2035 // We mimic failing series server, but practically context cancel will do the same. 2036 testutil.NotOk(t, p.Series(&storepb.SeriesRequest{Matchers: []storepb.LabelMatcher{{}}, PartialResponseStrategy: storepb.PartialResponseStrategy_ABORT}, &mockedSeriesServer{ 2037 ctx: ctx, 2038 send: func(*storepb.SeriesResponse) error { 2039 cancel() 2040 return ctx.Err() 2041 }, 2042 })) 2043 testutil.NotOk(t, ctx.Err()) 2044 }) 2045 } 2046 2047 func TestProxyStore_storeMatchMetadata(t *testing.T) { 2048 c := storetestutil.TestClient{Name: "testaddr"} 2049 c.IsLocalStore = true 2050 2051 ok, reason := storeMatchDebugMetadata(c, [][]*labels.Matcher{{}}) 2052 testutil.Assert(t, !ok) 2053 testutil.Equals(t, "the store is not remote, cannot match __address__", reason) 2054 2055 // Change client to remote. 2056 c.IsLocalStore = false 2057 2058 ok, reason = storeMatchDebugMetadata(c, [][]*labels.Matcher{{labels.MustNewMatcher(labels.MatchEqual, "__address__", "wrong")}}) 2059 testutil.Assert(t, !ok) 2060 testutil.Equals(t, "__address__ testaddr does not match debug store metadata matchers: [[__address__=\"wrong\"]]", reason) 2061 2062 ok, reason = storeMatchDebugMetadata(c, [][]*labels.Matcher{{labels.MustNewMatcher(labels.MatchEqual, "__address__", "testaddr")}}) 2063 testutil.Assert(t, ok) 2064 testutil.Equals(t, "", reason) 2065 } 2066 2067 func TestDedupRespHeap_Deduplication(t *testing.T) { 2068 t.Parallel() 2069 2070 for _, tcase := range []struct { 2071 responses []*storepb.SeriesResponse 2072 testFn func(responses []*storepb.SeriesResponse, h *dedupResponseHeap) 2073 tname string 2074 }{ 2075 { 2076 tname: "edge case with zero responses", 2077 responses: []*storepb.SeriesResponse{}, 2078 testFn: func(responses []*storepb.SeriesResponse, h *dedupResponseHeap) { 2079 testutil.Equals(t, false, h.Next()) 2080 2081 callAtExpectPanic := func() { 2082 defer func() { 2083 testutil.Assert(t, recover() != nil, "expected a panic from At()") 2084 }() 2085 2086 h.At() 2087 } 2088 callAtExpectPanic() 2089 }, 2090 }, 2091 { 2092 tname: "edge case with only one response", 2093 responses: []*storepb.SeriesResponse{ 2094 { 2095 Result: &storepb.SeriesResponse_Series{ 2096 Series: &storepb.Series{ 2097 Labels: labelpb.ZLabelsFromPromLabels(labels.FromStrings("foo", "bar")), 2098 Chunks: []storepb.AggrChunk{ 2099 { 2100 Raw: &storepb.Chunk{ 2101 Type: storepb.Chunk_XOR, 2102 Data: []byte(`abcdefgh`), 2103 }, 2104 }, 2105 }, 2106 }, 2107 }, 2108 }, 2109 }, 2110 testFn: func(responses []*storepb.SeriesResponse, h *dedupResponseHeap) { 2111 testutil.Equals(t, true, h.Next()) 2112 resp := h.At() 2113 testutil.Equals(t, responses[0], resp) 2114 testutil.Equals(t, false, h.Next()) 2115 }, 2116 }, 2117 { 2118 tname: "dedups identical series", 2119 responses: []*storepb.SeriesResponse{ 2120 { 2121 Result: &storepb.SeriesResponse_Series{ 2122 Series: &storepb.Series{ 2123 Labels: labelpb.ZLabelsFromPromLabels(labels.FromStrings("foo", "bar")), 2124 Chunks: []storepb.AggrChunk{ 2125 { 2126 Raw: &storepb.Chunk{ 2127 Type: storepb.Chunk_XOR, 2128 Data: []byte(`abcdefgh`), 2129 }, 2130 }, 2131 }, 2132 }, 2133 }, 2134 }, 2135 { 2136 Result: &storepb.SeriesResponse_Series{ 2137 Series: &storepb.Series{ 2138 Labels: labelpb.ZLabelsFromPromLabels(labels.FromStrings("foo", "bar")), 2139 Chunks: []storepb.AggrChunk{ 2140 { 2141 Raw: &storepb.Chunk{ 2142 Type: storepb.Chunk_XOR, 2143 Hash: xxhash.Sum64([]byte(`abcdefgh`)), 2144 Data: []byte(`abcdefgh`), 2145 }, 2146 }, 2147 }, 2148 }, 2149 }, 2150 }, 2151 }, 2152 testFn: func(responses []*storepb.SeriesResponse, h *dedupResponseHeap) { 2153 testutil.Equals(t, true, h.Next()) 2154 resp := h.At() 2155 testutil.Equals(t, responses[0], resp) 2156 testutil.Equals(t, false, h.Next()) 2157 }, 2158 }, 2159 } { 2160 t.Run(tcase.tname, func(t *testing.T) { 2161 h := NewDedupResponseHeap(NewProxyResponseHeap( 2162 &eagerRespSet{ 2163 wg: &sync.WaitGroup{}, 2164 bufferedResponses: tcase.responses, 2165 }, 2166 )) 2167 tcase.testFn(tcase.responses, h) 2168 }) 2169 } 2170 2171 }