github.com/m3db/m3@v1.5.0/src/query/storage/fanout/storage_test.go (about) 1 // 2 // Copyright (c) 2018 Uber Technologies, Inc. 3 // 4 // Permission is hereby granted, free of charge, to any person obtaining a copy 5 // of this software and associated documentation files (the "Software"), to deal 6 // in the Software without restriction, including without limitation the rights 7 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 // copies of the Software, and to permit persons to whom the Software is 9 // furnished to do so, subject to the following conditions: 10 // 11 // The above copyright notice and this permission notice shall be included in 12 // all copies or substantial portions of the Software. 13 // 14 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 // THE SOFTWARE. 21 22 package fanout 23 24 import ( 25 "context" 26 "errors" 27 "fmt" 28 "sort" 29 "testing" 30 "time" 31 32 "github.com/m3db/m3/src/dbnode/client" 33 "github.com/m3db/m3/src/dbnode/encoding" 34 "github.com/m3db/m3/src/query/block" 35 errs "github.com/m3db/m3/src/query/errors" 36 "github.com/m3db/m3/src/query/models" 37 "github.com/m3db/m3/src/query/policy/filter" 38 "github.com/m3db/m3/src/query/storage" 39 storagem3 "github.com/m3db/m3/src/query/storage/m3" 40 "github.com/m3db/m3/src/query/storage/m3/consolidators" 41 "github.com/m3db/m3/src/query/storage/m3/storagemetadata" 42 "github.com/m3db/m3/src/query/test" 43 "github.com/m3db/m3/src/query/test/m3" 44 "github.com/m3db/m3/src/query/test/seriesiter" 45 "github.com/m3db/m3/src/query/ts" 46 "github.com/m3db/m3/src/x/ident" 47 "github.com/m3db/m3/src/x/instrument" 48 xtest "github.com/m3db/m3/src/x/test" 49 xtime "github.com/m3db/m3/src/x/time" 50 51 "github.com/golang/mock/gomock" 52 "github.com/stretchr/testify/assert" 53 "github.com/stretchr/testify/require" 54 ) 55 56 func filterFunc(output bool) filter.Storage { 57 return func(query storage.Query, store storage.Storage) bool { 58 return output 59 } 60 } 61 62 func filterCompleteTagsFunc(output bool) filter.StorageCompleteTags { 63 return func(query storage.CompleteTagsQuery, store storage.Storage) bool { 64 return output 65 } 66 } 67 68 func fakeIterator(t *testing.T) encoding.SeriesIterators { 69 id := ident.StringID("id") 70 namespace := ident.StringID("metrics") 71 return encoding.NewSeriesIterators([]encoding.SeriesIterator{ 72 encoding.NewSeriesIterator(encoding.SeriesIteratorOptions{ 73 ID: id, 74 Namespace: namespace, 75 Tags: seriesiter.GenerateSingleSampleTagIterator( 76 xtest.NewController(t), seriesiter.GenerateTag()), 77 }, nil), 78 }) 79 } 80 81 type fetchResponse struct { 82 result encoding.SeriesIterators 83 err error 84 } 85 86 func setupFanoutRead(t *testing.T, output bool, response ...*fetchResponse) storage.Storage { 87 if len(response) == 0 { 88 response = []*fetchResponse{{err: fmt.Errorf("unable to get response")}} 89 } 90 91 ctrl := xtest.NewController(t) 92 store1, session1 := m3.NewStorageAndSession(t, ctrl) 93 store2, session2 := m3.NewStorageAndSession(t, ctrl) 94 session1.EXPECT().FetchTagged(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()). 95 Return(response[0].result, client.FetchResponseMetadata{Exhaustive: true}, response[0].err) 96 session2.EXPECT().FetchTagged(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()). 97 Return(response[len(response)-1].result, client.FetchResponseMetadata{Exhaustive: true}, response[len(response)-1].err) 98 session1.EXPECT().FetchTaggedIDs(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()). 99 Return(nil, client.FetchResponseMetadata{Exhaustive: false}, errs.ErrNotImplemented) 100 session2.EXPECT().FetchTaggedIDs(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()). 101 Return(nil, client.FetchResponseMetadata{Exhaustive: false}, errs.ErrNotImplemented) 102 103 stores := []storage.Storage{ 104 store1, store2, 105 } 106 107 store := NewStorage(stores, filterFunc(output), filterFunc(output), 108 filterCompleteTagsFunc(output), models.NewTagOptions(), 109 storagem3.NewOptions(encoding.NewOptions()), instrument.NewOptions()) 110 return store 111 } 112 113 func setupFanoutWrite(t *testing.T, output bool, errs ...error) storage.Storage { 114 ctrl := xtest.NewController(t) 115 store1, session1 := m3.NewStorageAndSession(t, ctrl) 116 store2, session2 := m3.NewStorageAndSession(t, ctrl) 117 session1.EXPECT(). 118 WriteTagged(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), 119 gomock.Any(), gomock.Any(), gomock.Any()).Return(errs[0]) 120 session1.EXPECT().IteratorPools(). 121 Return(nil, nil).AnyTimes() 122 session1.EXPECT().FetchTaggedIDs(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()). 123 Return(nil, client.FetchResponseMetadata{Exhaustive: true}, errs[0]).AnyTimes() 124 session1.EXPECT().Aggregate(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()). 125 Return(nil, client.FetchResponseMetadata{Exhaustive: true}, errs[0]).AnyTimes() 126 127 session2.EXPECT(). 128 WriteTagged(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), 129 gomock.Any(), gomock.Any(), gomock.Any()).Return(errs[len(errs)-1]) 130 session2.EXPECT().IteratorPools(). 131 Return(nil, nil).AnyTimes() 132 133 stores := []storage.Storage{ 134 store1, store2, 135 } 136 store := NewStorage(stores, filterFunc(output), filterFunc(output), 137 filterCompleteTagsFunc(output), models.NewTagOptions(), 138 storagem3.NewOptions(encoding.NewOptions()), instrument.NewOptions()) 139 return store 140 } 141 142 func TestCompleteTags_RestrictOptionsWorks_SingleStore(t *testing.T) { 143 ctrl := xtest.NewController(t) 144 store := storage.NewMockStorage(ctrl) 145 146 meta := block.NewResultMetadata() 147 meta.Exhaustive = false 148 fullResult := &consolidators.CompleteTagsResult{ 149 CompleteNameOnly: false, 150 CompletedTags: []consolidators.CompletedTag{ 151 {Name: []byte("bar"), Values: xtest.BytesArray("zulu", "quail")}, 152 {Name: []byte("foo"), Values: xtest.BytesArray("quail")}, 153 }, 154 155 Metadata: meta, 156 } 157 158 store.EXPECT().CompleteTags(gomock.Any(), gomock.Any(), gomock.Any()).Return(fullResult, nil) 159 160 stores := []storage.Storage{store} 161 fanoutStorage := NewStorage(stores, filterFunc(false), filterFunc(false), 162 filterCompleteTagsFunc(true), models.NewTagOptions(), 163 storagem3.NewOptions(encoding.NewOptions()), instrument.NewOptions()) 164 165 fetchOptions := storage.NewFetchOptions() 166 fetchOptions.RestrictQueryOptions = &storage.RestrictQueryOptions{ 167 RestrictByTag: &storage.RestrictByTag{ 168 Strip: xtest.BytesArray("bar"), 169 }, 170 } 171 172 completeTagsResult, err := fanoutStorage.CompleteTags( 173 context.TODO(), 174 &storage.CompleteTagsQuery{ 175 CompleteNameOnly: true, 176 TagMatchers: models.Matchers{}, 177 }, 178 fetchOptions) 179 180 require.NoError(t, err) 181 182 actualTags := completeTagsResult.CompletedTags 183 require.Len(t, actualTags, 1) 184 require.Equal(t, []byte("foo"), actualTags[0].Name) 185 } 186 187 func TestQueryStorageMetadataAttributes(t *testing.T) { 188 ctrl := xtest.NewController(t) 189 store1, _ := m3.NewStorageAndSession(t, ctrl) 190 191 stores := []storage.Storage{store1} 192 193 store := NewStorage(stores, filterFunc(false), filterFunc(false), 194 filterCompleteTagsFunc(false), models.NewTagOptions(), 195 storagem3.NewOptions(encoding.NewOptions()), instrument.NewOptions()) 196 197 attrs, err := store.QueryStorageMetadataAttributes( 198 context.Background(), 199 time.Now().Add(-10*time.Minute), 200 time.Now(), 201 storage.NewFetchOptions(), 202 ) 203 require.NoError(t, err) 204 require.Equal(t, []storagemetadata.Attributes{ 205 { 206 MetricsType: storagemetadata.UnaggregatedMetricsType, 207 Retention: m3.TestRetention, 208 }, 209 }, attrs) 210 } 211 212 func TestQueryStorageMetadataAttributesMultipleStores(t *testing.T) { 213 ctrl := xtest.NewController(t) 214 aggNs1 := []storagem3.AggregatedClusterNamespaceDefinition{ 215 { 216 NamespaceID: ident.StringID("5m:90d"), 217 Resolution: 5 * time.Minute, 218 Retention: 120 * 24 * time.Hour, 219 }, 220 } 221 aggNs2 := []storagem3.AggregatedClusterNamespaceDefinition{ 222 { 223 NamespaceID: ident.StringID("5m:90d"), 224 Resolution: 5 * time.Minute, 225 Retention: 120 * 24 * time.Hour, 226 }, 227 { 228 NamespaceID: ident.StringID("10m:120d"), 229 Resolution: 10 * time.Minute, 230 Retention: 150 * 24 * time.Hour, 231 }, 232 } 233 store1, _ := m3.NewStorageAndSessionWithAggregatedNamespaces(t, ctrl, aggNs1) 234 store2, _ := m3.NewStorageAndSessionWithAggregatedNamespaces(t, ctrl, aggNs2) 235 236 stores := []storage.Storage{store1, store2} 237 238 store := NewStorage(stores, filterFunc(false), filterFunc(false), 239 filterCompleteTagsFunc(false), models.NewTagOptions(), 240 storagem3.NewOptions(encoding.NewOptions()), instrument.NewOptions()) 241 242 fetchOpts := storage.NewFetchOptions() 243 fetchOpts.FanoutOptions.FanoutAggregated = storage.FanoutForceEnable 244 attrs, err := store.QueryStorageMetadataAttributes( 245 context.Background(), 246 time.Now().Add(-200*24*time.Hour), 247 time.Now(), 248 fetchOpts, 249 ) 250 require.NoError(t, err) 251 252 sort.Slice(attrs, func(i, j int) bool { 253 return attrs[i].Retention < attrs[j].Retention 254 }) 255 require.Equal(t, []storagemetadata.Attributes{ 256 { 257 MetricsType: storagemetadata.AggregatedMetricsType, 258 Resolution: 5 * time.Minute, 259 Retention: 120 * 24 * time.Hour, 260 }, 261 { 262 MetricsType: storagemetadata.AggregatedMetricsType, 263 Resolution: 10 * time.Minute, 264 Retention: 150 * 24 * time.Hour, 265 }, 266 }, attrs) 267 } 268 269 func TestFanoutReadEmpty(t *testing.T) { 270 store := setupFanoutRead(t, false) 271 res, err := store.FetchProm(context.TODO(), nil, storage.NewFetchOptions()) 272 assert.NoError(t, err) 273 require.NotNil(t, res) 274 assert.Equal(t, 0, len(res.PromResult.GetTimeseries())) 275 assert.Equal(t, 0, res.Metadata.FetchedSeriesCount) 276 assert.Equal(t, block.ResultMetricMetadata{}, res.Metadata.MetadataByNameMerged()) 277 } 278 279 func TestFanoutReadError(t *testing.T) { 280 store := setupFanoutRead(t, true) 281 opts := storage.NewFetchOptions() 282 _, err := store.FetchProm(context.TODO(), &storage.FetchQuery{}, opts) 283 assert.Error(t, err) 284 } 285 286 func TestFanoutReadSuccess(t *testing.T) { 287 store := setupFanoutRead(t, true, &fetchResponse{ 288 result: fakeIterator(t), 289 }, 290 &fetchResponse{result: fakeIterator(t)}, 291 ) 292 res, err := store.FetchProm(context.TODO(), &storage.FetchQuery{ 293 Start: time.Now().Add(-time.Hour), 294 End: time.Now(), 295 }, storage.NewFetchOptions()) 296 require.NoError(t, err, "no error on read") 297 assert.NotNil(t, res) 298 assert.NoError(t, store.Close()) 299 } 300 301 func TestFanoutSearchEmpty(t *testing.T) { 302 store := setupFanoutRead(t, false) 303 res, err := store.SearchSeries(context.TODO(), nil, nil) 304 assert.NoError(t, err, "No error") 305 require.NotNil(t, res, "Non empty result") 306 assert.Len(t, res.Metrics, 0, "No series") 307 } 308 309 func TestFanoutSearchError(t *testing.T) { 310 store := setupFanoutRead(t, true) 311 opts := storage.NewFetchOptions() 312 _, err := store.SearchSeries(context.TODO(), &storage.FetchQuery{}, opts) 313 assert.Error(t, err) 314 } 315 316 func TestFanoutWriteEmpty(t *testing.T) { 317 store := setupFanoutWrite(t, false, fmt.Errorf("write error")) 318 err := store.Write(context.TODO(), nil) 319 assert.NoError(t, err) 320 } 321 322 func TestFanoutWriteError(t *testing.T) { 323 store := setupFanoutWrite(t, true, fmt.Errorf("write error")) 324 datapoints := make(ts.Datapoints, 1) 325 datapoints[0] = ts.Datapoint{Timestamp: xtime.Now(), Value: 1} 326 327 writeQuery, err := storage.NewWriteQuery(storage.WriteQueryOptions{ 328 Datapoints: datapoints, 329 Tags: models.MustMakeTags("foo", "bar"), 330 Unit: xtime.Second, 331 }) 332 require.NoError(t, err) 333 334 assert.Error(t, store.Write(context.TODO(), writeQuery)) 335 } 336 337 func TestFanoutWriteSuccess(t *testing.T) { 338 store := setupFanoutWrite(t, true, nil) 339 datapoints := make(ts.Datapoints, 1) 340 datapoints[0] = ts.Datapoint{Timestamp: xtime.Now(), Value: 1} 341 342 writeQuery, err := storage.NewWriteQuery(storage.WriteQueryOptions{ 343 Datapoints: datapoints, 344 Tags: models.MustMakeTags("foo", "bar"), 345 Unit: xtime.Second, 346 Attributes: storagemetadata.Attributes{ 347 MetricsType: storagemetadata.UnaggregatedMetricsType, 348 }, 349 }) 350 require.NoError(t, err) 351 352 assert.NoError(t, store.Write(context.TODO(), writeQuery)) 353 } 354 355 func TestCompleteTagsError(t *testing.T) { 356 store := setupFanoutWrite(t, true, fmt.Errorf("err")) 357 datapoints := make(ts.Datapoints, 1) 358 datapoints[0] = ts.Datapoint{Timestamp: xtime.Now(), Value: 1} 359 _, err := store.CompleteTags( 360 context.TODO(), 361 &storage.CompleteTagsQuery{ 362 CompleteNameOnly: true, 363 TagMatchers: models.Matchers{}, 364 }, 365 storage.NewFetchOptions(), 366 ) 367 assert.Error(t, err) 368 } 369 370 // Error continuation tests below. 371 func TestFanoutSearchErrorContinues(t *testing.T) { 372 ctrl := xtest.NewController(t) 373 defer ctrl.Finish() 374 375 filter := func(_ storage.Query, _ storage.Storage) bool { return true } 376 tFilter := func(_ storage.CompleteTagsQuery, _ storage.Storage) bool { return true } 377 okStore := storage.NewMockStorage(ctrl) 378 okStore.EXPECT().SearchSeries(gomock.Any(), gomock.Any(), gomock.Any()). 379 Return( 380 &storage.SearchResults{ 381 Metrics: models.Metrics{ 382 models.Metric{ 383 ID: []byte("ok"), 384 Tags: models.NewTags(1, models.NewTagOptions()).AddTag(models.Tag{ 385 Name: []byte("foo"), 386 Value: []byte("bar"), 387 }), 388 }, 389 }, 390 }, 391 nil, 392 ) 393 394 dupeStore := storage.NewMockStorage(ctrl) 395 dupeStore.EXPECT().SearchSeries(gomock.Any(), gomock.Any(), gomock.Any()). 396 Return( 397 &storage.SearchResults{ 398 Metrics: models.Metrics{ 399 models.Metric{ 400 ID: []byte("ok"), 401 Tags: models.NewTags(1, models.NewTagOptions()).AddTag(models.Tag{ 402 Name: []byte("foo"), 403 Value: []byte("bar"), 404 }), 405 }, 406 }, 407 }, 408 nil, 409 ) 410 411 warnStore := storage.NewMockStorage(ctrl) 412 warnStore.EXPECT().SearchSeries(gomock.Any(), gomock.Any(), gomock.Any()). 413 Return( 414 &storage.SearchResults{ 415 Metrics: models.Metrics{ 416 models.Metric{ 417 ID: []byte("warn"), 418 }, 419 }, 420 }, 421 errors.New("e"), 422 ) 423 warnStore.EXPECT().ErrorBehavior().Return(storage.BehaviorWarn) 424 warnStore.EXPECT().Name().Return("warn").AnyTimes() 425 426 stores := []storage.Storage{warnStore, okStore, dupeStore} 427 store := NewStorage(stores, filter, filter, tFilter, 428 models.NewTagOptions(), storagem3.NewOptions(encoding.NewOptions()), 429 instrument.NewOptions()) 430 opts := storage.NewFetchOptions() 431 result, err := store.SearchSeries(context.TODO(), &storage.FetchQuery{}, opts) 432 assert.NoError(t, err) 433 434 require.Equal(t, 1, len(result.Metrics)) 435 assert.Equal(t, []byte("ok"), result.Metrics[0].ID) 436 require.Equal(t, 1, result.Metrics[0].Tags.Len()) 437 tag := result.Metrics[0].Tags.Tags[0] 438 require.Equal(t, []byte("foo"), tag.Name) 439 require.Equal(t, []byte("bar"), tag.Value) 440 } 441 442 func TestFanoutCompleteTagsErrorContinues(t *testing.T) { 443 ctrl := xtest.NewController(t) 444 defer ctrl.Finish() 445 446 filter := func(_ storage.Query, _ storage.Storage) bool { return true } 447 tFilter := func(_ storage.CompleteTagsQuery, _ storage.Storage) bool { return true } 448 okStore := storage.NewMockStorage(ctrl) 449 okStore.EXPECT().CompleteTags(gomock.Any(), gomock.Any(), gomock.Any()). 450 Return( 451 &consolidators.CompleteTagsResult{ 452 CompleteNameOnly: true, 453 CompletedTags: []consolidators.CompletedTag{ 454 { 455 Name: []byte("ok"), 456 }, 457 }, 458 }, 459 nil, 460 ) 461 462 warnStore := storage.NewMockStorage(ctrl) 463 warnStore.EXPECT().CompleteTags(gomock.Any(), gomock.Any(), gomock.Any()). 464 Return( 465 &consolidators.CompleteTagsResult{ 466 CompleteNameOnly: true, 467 CompletedTags: []consolidators.CompletedTag{ 468 { 469 Name: []byte("warn"), 470 }, 471 }, 472 }, 473 errors.New("e"), 474 ) 475 warnStore.EXPECT().ErrorBehavior().Return(storage.BehaviorWarn) 476 warnStore.EXPECT().Name().Return("warn").AnyTimes() 477 478 stores := []storage.Storage{warnStore, okStore} 479 store := NewStorage(stores, filter, filter, tFilter, 480 models.NewTagOptions(), storagem3.NewOptions(encoding.NewOptions()), 481 instrument.NewOptions()) 482 opts := storage.NewFetchOptions() 483 q := &storage.CompleteTagsQuery{CompleteNameOnly: true} 484 result, err := store.CompleteTags(context.TODO(), q, opts) 485 assert.NoError(t, err) 486 487 require.True(t, result.CompleteNameOnly) 488 require.Equal(t, 1, len(result.CompletedTags)) 489 assert.Equal(t, []byte("ok"), result.CompletedTags[0].Name) 490 } 491 492 func TestFanoutFetchBlocksErrorContinues(t *testing.T) { 493 ctrl := xtest.NewController(t) 494 defer ctrl.Finish() 495 496 filter := func(_ storage.Query, _ storage.Storage) bool { return true } 497 tFilter := func(_ storage.CompleteTagsQuery, _ storage.Storage) bool { return true } 498 okBlock := block.NewScalar(1, block.Metadata{}) 499 okStore := storage.NewMockStorage(ctrl) 500 okStore.EXPECT().FetchBlocks(gomock.Any(), gomock.Any(), gomock.Any()). 501 Return( 502 block.Result{ 503 Blocks: []block.Block{okBlock}, 504 }, 505 nil, 506 ) 507 508 warnStore := storage.NewMockStorage(ctrl) 509 warnBlock := block.NewScalar(2, block.Metadata{}) 510 warnStore.EXPECT().FetchBlocks(gomock.Any(), gomock.Any(), gomock.Any()). 511 Return( 512 block.Result{ 513 Blocks: []block.Block{warnBlock}, 514 }, 515 errors.New("e"), 516 ) 517 warnStore.EXPECT().ErrorBehavior().Return(storage.BehaviorWarn) 518 warnStore.EXPECT().Name().Return("warn").AnyTimes() 519 520 stores := []storage.Storage{okStore, warnStore} 521 store := NewStorage(stores, filter, filter, tFilter, 522 models.NewTagOptions(), storagem3.NewOptions(encoding.NewOptions()), 523 instrument.NewOptions()) 524 opts := storage.NewFetchOptions() 525 result, err := store.FetchBlocks(context.TODO(), &storage.FetchQuery{}, opts) 526 assert.NoError(t, err) 527 528 require.Equal(t, 1, len(result.Blocks)) 529 assert.Equal(t, block.BlockLazy, result.Blocks[0].Info().Type()) 530 it, err := result.Blocks[0].StepIter() 531 require.NoError(t, err) 532 for it.Next() { 533 assert.Equal(t, []float64{1}, it.Current().Values()) 534 } 535 } 536 537 func TestFanoutFetchErrorContinues(t *testing.T) { 538 ctrl := xtest.NewController(t) 539 defer ctrl.Finish() 540 541 filter := func(_ storage.Query, _ storage.Storage) bool { 542 return true 543 } 544 545 tFilter := func(_ storage.CompleteTagsQuery, _ storage.Storage) bool { 546 return true 547 } 548 549 okStore := storage.NewMockStorage(ctrl) 550 okStore.EXPECT().FetchCompressed(gomock.Any(), gomock.Any(), gomock.Any()). 551 Return( 552 fetchResult("ok"), 553 nil, 554 ).AnyTimes() 555 okStore.EXPECT().Type().Return(storage.TypeLocalDC).AnyTimes() 556 557 warnStore := storage.NewMockStorage(ctrl) 558 warnStore.EXPECT().FetchCompressed(gomock.Any(), gomock.Any(), gomock.Any()). 559 Return( 560 fetchResult("warn"), 561 errors.New("e"), 562 ) 563 warnStore.EXPECT().ErrorBehavior().Return(storage.BehaviorWarn) 564 warnStore.EXPECT().Name().Return("warn").AnyTimes() 565 566 stores := []storage.Storage{okStore, warnStore} 567 store := NewStorage(stores, filter, filter, tFilter, 568 models.NewTagOptions(), storagem3.NewOptions(encoding.NewOptions()), 569 instrument.NewOptions()) 570 opts := storage.NewFetchOptions() 571 opts.SeriesLimit = 300 572 result, err := store.FetchProm(context.TODO(), &storage.FetchQuery{}, opts) 573 assert.NoError(t, err) 574 575 series := result.PromResult.GetTimeseries() 576 require.Equal(t, 2, len(series)) 577 } 578 579 func fetchResult(name string) consolidators.MultiFetchResult { 580 it, _ := test.BuildTestSeriesIterator(name) 581 iters := encoding.NewSeriesIterators([]encoding.SeriesIterator{it}) 582 result := consolidators.NewMultiFetchResult( 583 consolidators.NamespaceCoversAllQueryRange, 584 consolidators.MatchOptions{MatchType: consolidators.MatchTags}, 585 models.NewTagOptions(), 586 consolidators.LimitOptions{ 587 Limit: 300, 588 RequireExhaustive: false, 589 }, 590 ) 591 result.Add(consolidators.MultiFetchResults{ 592 SeriesIterators: iters, 593 Metadata: block.NewResultMetadata(), 594 Attrs: storagemetadata.Attributes{ 595 MetricsType: 0, 596 Retention: 30, 597 Resolution: 30, 598 }, 599 Err: nil, 600 }) 601 return result 602 }