github.com/m3db/m3@v1.5.0/src/query/storage/m3/cluster_resolver_test.go (about) 1 // Copyright (c) 2019 Uber Technologies, Inc. 2 // 3 // Permission is hereby granted, free of charge, to any person obtaining a copy 4 // of this software and associated documentation files (the "Software"), to deal 5 // in the Software without restriction, including without limitation the rights 6 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 // copies of the Software, and to permit persons to whom the Software is 8 // furnished to do so, subject to the following conditions: 9 // 10 // The above copyright notice and this permission notice shall be included in 11 // all copies or substantial portions of the Software. 12 // 13 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 // THE SOFTWARE. 20 21 package m3 22 23 import ( 24 "sort" 25 "strings" 26 "testing" 27 "time" 28 29 "github.com/golang/mock/gomock" 30 "github.com/stretchr/testify/assert" 31 "github.com/stretchr/testify/require" 32 33 "github.com/m3db/m3/src/dbnode/client" 34 "github.com/m3db/m3/src/metrics/policy" 35 "github.com/m3db/m3/src/query/storage" 36 "github.com/m3db/m3/src/query/storage/m3/consolidators" 37 "github.com/m3db/m3/src/query/storage/m3/storagemetadata" 38 xerrors "github.com/m3db/m3/src/x/errors" 39 "github.com/m3db/m3/src/x/ident" 40 xtime "github.com/m3db/m3/src/x/time" 41 ) 42 43 func TestFanoutAggregatedOptimizationDisabledGivesAllClustersAsPartial(t *testing.T) { 44 ctrl := gomock.NewController(t) 45 defer ctrl.Finish() 46 47 now := xtime.Now() 48 s, _ := setup(t, ctrl) 49 store, ok := s.(*m3storage) 50 assert.True(t, ok) 51 var r reusedAggregatedNamespaceSlices 52 opts := &storage.FanoutOptions{ 53 FanoutAggregatedOptimized: storage.FanoutForceDisable, 54 } 55 56 clusters := store.clusters.ClusterNamespaces() 57 r = aggregatedNamespaces(clusters, r, nil, now, now, opts) 58 assert.Equal(t, 0, len(r.completeAggregated)) 59 assert.Equal(t, 4, len(r.partialAggregated)) 60 } 61 62 func TestFanoutUnaggregatedDisableReturnsAggregatedNamespaces(t *testing.T) { 63 ctrl := gomock.NewController(t) 64 defer ctrl.Finish() 65 s, _ := setup(t, ctrl) 66 store, ok := s.(*m3storage) 67 assert.True(t, ok) 68 opts := &storage.FanoutOptions{ 69 FanoutUnaggregated: storage.FanoutForceDisable, 70 } 71 72 start := xtime.Now() 73 end := start.Add(time.Hour * 24 * -90) 74 _, clusters, err := resolveClusterNamespacesForQuery(start, start, end, store.clusters, opts, 75 nil, nil) 76 require.NoError(t, err) 77 require.Equal(t, 1, len(clusters)) 78 assert.Equal(t, "metrics_aggregated_1m:30d", clusters[0].NamespaceID().String()) 79 } 80 81 func TestFanoutUnaggregatedEnabledReturnsUnaggregatedNamespaces(t *testing.T) { 82 ctrl := gomock.NewController(t) 83 defer ctrl.Finish() 84 s, _ := setup(t, ctrl) 85 store, ok := s.(*m3storage) 86 assert.True(t, ok) 87 opts := &storage.FanoutOptions{ 88 FanoutUnaggregated: storage.FanoutForceEnable, 89 } 90 91 start := xtime.Now() 92 end := start.Add(time.Hour * 24 * -90) 93 _, clusters, err := resolveClusterNamespacesForQuery(start, 94 start, end, store.clusters, opts, nil, nil) 95 require.NoError(t, err) 96 require.Equal(t, 1, len(clusters)) 97 assert.Equal(t, "metrics_unaggregated", clusters[0].NamespaceID().String()) 98 } 99 100 func TestGraphitePath(t *testing.T) { 101 ctrl := gomock.NewController(t) 102 defer ctrl.Finish() 103 s, _ := setup(t, ctrl) 104 store, ok := s.(*m3storage) 105 assert.True(t, ok) 106 opts := &storage.FanoutOptions{ 107 FanoutUnaggregated: storage.FanoutForceDisable, 108 FanoutAggregated: storage.FanoutForceEnable, 109 FanoutAggregatedOptimized: storage.FanoutForceDisable, 110 } 111 112 start := xtime.Now() 113 end := start.Add(time.Second * -30) 114 _, clusters, err := resolveClusterNamespacesForQuery(start, start, end, store.clusters, opts, 115 nil, nil) 116 require.NoError(t, err) 117 require.Equal(t, 4, len(clusters)) 118 expected := []string{ 119 "metrics_aggregated_1m:30d", "metrics_aggregated_5m:90d", 120 "metrics_aggregated_partial_1m:180d", "metrics_aggregated_10m:365d", 121 } 122 123 for i, cluster := range clusters { 124 assert.Equal(t, expected[i], cluster.NamespaceID().String()) 125 } 126 } 127 128 var ( 129 genRetentionFiltered, genRetentionUnfiltered = time.Hour, time.Hour * 10 130 genResolution = time.Minute 131 ) 132 133 func generateClusters(t *testing.T, ctrl *gomock.Controller) Clusters { 134 session := client.NewMockSession(ctrl) 135 136 clusters, err := NewClusters(UnaggregatedClusterNamespaceDefinition{ 137 NamespaceID: ident.StringID("UNAGG"), 138 Retention: genRetentionFiltered + time.Minute, 139 Session: session, 140 }, AggregatedClusterNamespaceDefinition{ 141 NamespaceID: ident.StringID("AGG_FILTERED"), 142 Retention: genRetentionFiltered, 143 Resolution: genResolution, 144 Downsample: &ClusterNamespaceDownsampleOptions{All: false}, 145 Session: session, 146 }, AggregatedClusterNamespaceDefinition{ 147 NamespaceID: ident.StringID("AGG_NO_FILTER"), 148 Retention: genRetentionUnfiltered, 149 Resolution: genResolution, 150 Downsample: &ClusterNamespaceDownsampleOptions{All: false}, 151 Session: session, 152 }, AggregatedClusterNamespaceDefinition{ 153 NamespaceID: ident.StringID("AGG_FILTERED_COMPLETE"), 154 Retention: genRetentionFiltered, 155 Resolution: genResolution + time.Second, 156 Downsample: &ClusterNamespaceDownsampleOptions{All: true}, 157 Session: session, 158 }, AggregatedClusterNamespaceDefinition{ 159 NamespaceID: ident.StringID("AGG_NO_FILTER_COMPLETE"), 160 Retention: genRetentionUnfiltered, 161 Resolution: genResolution + time.Second, 162 Downsample: &ClusterNamespaceDownsampleOptions{All: true}, 163 Session: session, 164 }) 165 166 require.NoError(t, err) 167 return clusters 168 } 169 170 // used to generate storage.RelatedQueries during a test 171 type testTimeOffsets struct { 172 startOffset time.Duration 173 endOffset time.Duration 174 } 175 176 var testCases = []struct { 177 name string 178 queryLength time.Duration 179 opts *storage.FanoutOptions 180 restrict *storage.RestrictQueryOptions 181 expectedType consolidators.QueryFanoutType 182 relatedQueryOffsets []testTimeOffsets 183 expectedClusterNames []string 184 expectedErr error 185 expectedErrContains string 186 expectedErrInvalidParams bool 187 }{ 188 { 189 name: "all disabled", 190 opts: &storage.FanoutOptions{ 191 FanoutUnaggregated: storage.FanoutForceDisable, 192 FanoutAggregated: storage.FanoutForceDisable, 193 FanoutAggregatedOptimized: storage.FanoutForceDisable, 194 }, 195 expectedType: consolidators.NamespaceInvalid, 196 expectedErr: errUnaggregatedAndAggregatedDisabled, 197 }, 198 { 199 name: "optimize enabled", 200 opts: &storage.FanoutOptions{ 201 FanoutUnaggregated: storage.FanoutForceDisable, 202 FanoutAggregated: storage.FanoutForceDisable, 203 FanoutAggregatedOptimized: storage.FanoutForceEnable, 204 }, 205 expectedType: consolidators.NamespaceInvalid, 206 expectedErr: errUnaggregatedAndAggregatedDisabled, 207 }, 208 { 209 name: "unagg enabled", 210 opts: &storage.FanoutOptions{ 211 FanoutUnaggregated: storage.FanoutForceEnable, 212 FanoutAggregated: storage.FanoutForceDisable, 213 FanoutAggregatedOptimized: storage.FanoutForceDisable, 214 }, 215 expectedType: consolidators.NamespaceCoversPartialQueryRange, 216 expectedClusterNames: []string{"UNAGG"}, 217 }, 218 { 219 name: "unagg enabled short range", 220 queryLength: time.Minute, 221 opts: &storage.FanoutOptions{ 222 FanoutUnaggregated: storage.FanoutForceEnable, 223 FanoutAggregated: storage.FanoutForceDisable, 224 FanoutAggregatedOptimized: storage.FanoutForceDisable, 225 }, 226 expectedType: consolidators.NamespaceCoversAllQueryRange, 227 expectedClusterNames: []string{"UNAGG"}, 228 }, 229 { 230 name: "unagg optimized enabled", 231 opts: &storage.FanoutOptions{ 232 FanoutUnaggregated: storage.FanoutForceEnable, 233 FanoutAggregated: storage.FanoutForceDisable, 234 FanoutAggregatedOptimized: storage.FanoutForceEnable, 235 }, 236 expectedType: consolidators.NamespaceCoversPartialQueryRange, 237 expectedClusterNames: []string{"UNAGG"}, 238 }, 239 { 240 name: "unagg optimized enabled short range", 241 queryLength: time.Minute, 242 opts: &storage.FanoutOptions{ 243 FanoutUnaggregated: storage.FanoutForceEnable, 244 FanoutAggregated: storage.FanoutForceDisable, 245 FanoutAggregatedOptimized: storage.FanoutForceEnable, 246 }, 247 expectedType: consolidators.NamespaceCoversAllQueryRange, 248 expectedClusterNames: []string{"UNAGG"}, 249 }, 250 { 251 name: "agg enabled", 252 opts: &storage.FanoutOptions{ 253 FanoutUnaggregated: storage.FanoutForceDisable, 254 FanoutAggregated: storage.FanoutForceEnable, 255 FanoutAggregatedOptimized: storage.FanoutForceDisable, 256 }, 257 expectedType: consolidators.NamespaceCoversPartialQueryRange, 258 expectedClusterNames: []string{ 259 "AGG_FILTERED", "AGG_NO_FILTER", 260 "AGG_FILTERED_COMPLETE", "AGG_NO_FILTER_COMPLETE", 261 }, 262 }, 263 { 264 name: "agg enabled short range", 265 queryLength: time.Minute, 266 opts: &storage.FanoutOptions{ 267 FanoutUnaggregated: storage.FanoutForceDisable, 268 FanoutAggregated: storage.FanoutForceEnable, 269 FanoutAggregatedOptimized: storage.FanoutForceDisable, 270 }, 271 expectedType: consolidators.NamespaceCoversAllQueryRange, 272 expectedClusterNames: []string{ 273 "AGG_FILTERED", "AGG_NO_FILTER", 274 "AGG_FILTERED_COMPLETE", "AGG_NO_FILTER_COMPLETE", 275 }, 276 }, 277 { 278 name: "unagg and agg enabled", 279 opts: &storage.FanoutOptions{ 280 FanoutUnaggregated: storage.FanoutForceEnable, 281 FanoutAggregated: storage.FanoutForceEnable, 282 FanoutAggregatedOptimized: storage.FanoutForceDisable, 283 }, 284 expectedType: consolidators.NamespaceCoversPartialQueryRange, 285 expectedClusterNames: []string{ 286 "UNAGG", "AGG_FILTERED", "AGG_NO_FILTER", 287 "AGG_FILTERED_COMPLETE", "AGG_NO_FILTER_COMPLETE", 288 }, 289 }, 290 { 291 name: "unagg and agg enabled short range", 292 queryLength: time.Minute, 293 opts: &storage.FanoutOptions{ 294 FanoutUnaggregated: storage.FanoutForceEnable, 295 FanoutAggregated: storage.FanoutForceEnable, 296 FanoutAggregatedOptimized: storage.FanoutForceDisable, 297 }, 298 expectedType: consolidators.NamespaceCoversAllQueryRange, 299 expectedClusterNames: []string{"UNAGG"}, 300 }, 301 { 302 name: "agg and optimization enabled", 303 opts: &storage.FanoutOptions{ 304 FanoutUnaggregated: storage.FanoutForceDisable, 305 FanoutAggregated: storage.FanoutForceEnable, 306 FanoutAggregatedOptimized: storage.FanoutForceEnable, 307 }, 308 expectedType: consolidators.NamespaceCoversAllQueryRange, 309 expectedClusterNames: []string{"AGG_NO_FILTER", "AGG_NO_FILTER_COMPLETE"}, 310 }, 311 { 312 name: "all enabled short range", 313 queryLength: time.Minute, 314 opts: &storage.FanoutOptions{ 315 FanoutUnaggregated: storage.FanoutForceEnable, 316 FanoutAggregated: storage.FanoutForceEnable, 317 FanoutAggregatedOptimized: storage.FanoutForceEnable, 318 }, 319 expectedType: consolidators.NamespaceCoversAllQueryRange, 320 expectedClusterNames: []string{"UNAGG"}, 321 }, 322 { 323 name: "all enabled long range", 324 queryLength: time.Hour * 1000, 325 opts: &storage.FanoutOptions{ 326 FanoutUnaggregated: storage.FanoutForceEnable, 327 FanoutAggregated: storage.FanoutForceEnable, 328 FanoutAggregatedOptimized: storage.FanoutForceEnable, 329 }, 330 expectedType: consolidators.NamespaceCoversPartialQueryRange, 331 expectedClusterNames: []string{"AGG_NO_FILTER", "AGG_NO_FILTER_COMPLETE"}, 332 }, 333 { 334 name: "restrict to unaggregated", 335 queryLength: time.Hour * 1000, 336 restrict: &storage.RestrictQueryOptions{ 337 RestrictByType: &storage.RestrictByType{ 338 MetricsType: storagemetadata.UnaggregatedMetricsType, 339 }, 340 }, 341 expectedType: consolidators.NamespaceCoversPartialQueryRange, 342 expectedClusterNames: []string{"UNAGG"}, 343 }, 344 { 345 name: "restrict to aggregate filtered", 346 queryLength: time.Hour * 1000, 347 restrict: &storage.RestrictQueryOptions{ 348 RestrictByType: &storage.RestrictByType{ 349 MetricsType: storagemetadata.AggregatedMetricsType, 350 StoragePolicy: policy.MustParseStoragePolicy( 351 genResolution.String() + ":" + genRetentionFiltered.String()), 352 }, 353 }, 354 expectedType: consolidators.NamespaceCoversPartialQueryRange, 355 expectedClusterNames: []string{"AGG_FILTERED"}, 356 }, 357 { 358 name: "restrict to aggregate unfiltered", 359 queryLength: time.Hour * 1000, 360 restrict: &storage.RestrictQueryOptions{ 361 RestrictByType: &storage.RestrictByType{ 362 MetricsType: storagemetadata.AggregatedMetricsType, 363 StoragePolicy: policy.MustParseStoragePolicy( 364 genResolution.String() + ":" + genRetentionUnfiltered.String()), 365 }, 366 }, 367 expectedType: consolidators.NamespaceCoversPartialQueryRange, 368 expectedClusterNames: []string{"AGG_NO_FILTER"}, 369 }, 370 { 371 name: "restrict with unknown metrics type", 372 queryLength: time.Hour * 1000, 373 restrict: &storage.RestrictQueryOptions{ 374 RestrictByType: &storage.RestrictByType{ 375 MetricsType: storagemetadata.UnknownMetricsType, 376 }, 377 }, 378 expectedErrContains: "unrecognized metrics type:", 379 expectedErrInvalidParams: true, 380 }, 381 { 382 name: "restrict with unknown storage policy", 383 queryLength: time.Hour * 1000, 384 restrict: &storage.RestrictQueryOptions{ 385 RestrictByType: &storage.RestrictByType{ 386 MetricsType: storagemetadata.AggregatedMetricsType, 387 StoragePolicy: policy.MustParseStoragePolicy("1s:100d"), 388 }, 389 }, 390 expectedErrContains: "could not find namespace for storage policy:", 391 expectedErrInvalidParams: true, 392 }, 393 { 394 name: "restrict to multiple aggregate", 395 queryLength: time.Hour * 1000, 396 restrict: &storage.RestrictQueryOptions{ 397 RestrictByTypes: []*storage.RestrictByType{ 398 { 399 MetricsType: storagemetadata.AggregatedMetricsType, 400 StoragePolicy: policy.MustParseStoragePolicy( 401 genResolution.String() + ":" + genRetentionFiltered.String()), 402 }, 403 { 404 MetricsType: storagemetadata.AggregatedMetricsType, 405 StoragePolicy: policy.MustParseStoragePolicy( 406 (genResolution + time.Second).String() + ":" + genRetentionUnfiltered.String()), 407 }, 408 }, 409 }, 410 expectedType: consolidators.NamespaceCoversPartialQueryRange, 411 expectedClusterNames: []string{"AGG_FILTERED", "AGG_NO_FILTER_COMPLETE"}, 412 }, 413 { 414 name: "restrict to multiple aggregate all range", 415 queryLength: time.Minute, 416 opts: &storage.FanoutOptions{ 417 FanoutUnaggregated: storage.FanoutForceDisable, 418 FanoutAggregated: storage.FanoutDefault, 419 FanoutAggregatedOptimized: storage.FanoutForceDisable, 420 }, 421 restrict: &storage.RestrictQueryOptions{ 422 RestrictByTypes: []*storage.RestrictByType{ 423 { 424 MetricsType: storagemetadata.AggregatedMetricsType, 425 StoragePolicy: policy.MustParseStoragePolicy( 426 (genResolution + time.Second).String() + ":" + genRetentionFiltered.String()), 427 }, 428 { 429 MetricsType: storagemetadata.AggregatedMetricsType, 430 StoragePolicy: policy.MustParseStoragePolicy( 431 (genResolution + time.Second).String() + ":" + genRetentionUnfiltered.String()), 432 }, 433 }, 434 }, 435 expectedType: consolidators.NamespaceCoversAllQueryRange, 436 expectedClusterNames: []string{"AGG_FILTERED_COMPLETE", "AGG_NO_FILTER_COMPLETE"}, 437 }, 438 { 439 name: "all enabled short range w/ related queries", 440 queryLength: time.Minute, 441 opts: &storage.FanoutOptions{ 442 FanoutUnaggregated: storage.FanoutForceEnable, 443 FanoutAggregated: storage.FanoutForceEnable, 444 FanoutAggregatedOptimized: storage.FanoutForceEnable, 445 }, 446 relatedQueryOffsets: []testTimeOffsets{{startOffset: time.Hour * 1000, endOffset: 0}}, 447 expectedType: consolidators.NamespaceCoversPartialQueryRange, 448 expectedClusterNames: []string{"AGG_NO_FILTER", "AGG_NO_FILTER_COMPLETE"}, 449 }, 450 } 451 452 func TestResolveClusterNamespacesForQueryWithOptions(t *testing.T) { 453 ctrl := gomock.NewController(t) 454 defer ctrl.Finish() 455 clusters := generateClusters(t, ctrl) 456 457 now := xtime.Now() 458 end := now 459 for _, tt := range testCases { 460 t.Run(tt.name, func(t *testing.T) { 461 start := now.Add(tt.queryLength * -1) 462 if tt.queryLength == 0 { 463 // default case 464 start = start.Add(time.Hour * -2) 465 } 466 467 relatedQueries := make([]storage.QueryTimespan, 0, len(tt.relatedQueryOffsets)) 468 for _, offset := range tt.relatedQueryOffsets { 469 timespan := storage.QueryTimespan{ 470 Start: now.Add(-offset.startOffset), 471 End: now.Add(-offset.endOffset), 472 } 473 relatedQueries = append(relatedQueries, timespan) 474 } 475 476 fanoutType, clusters, err := resolveClusterNamespacesForQuery(now, 477 start, end, clusters, tt.opts, tt.restrict, 478 &storage.RelatedQueryOptions{Timespans: relatedQueries}) 479 if tt.expectedErr != nil { 480 assert.Error(t, err) 481 assert.Equal(t, tt.expectedErr, err) 482 assert.Nil(t, clusters) 483 return 484 } 485 486 if substr := tt.expectedErrContains; substr != "" { 487 assert.Error(t, err) 488 assert.True(t, strings.Contains(err.Error(), substr)) 489 assert.Nil(t, clusters) 490 invalidParams := xerrors.IsInvalidParams(err) 491 assert.Equal(t, tt.expectedErrInvalidParams, invalidParams) 492 return 493 } 494 495 require.NoError(t, err) 496 actualNames := make([]string, len(clusters)) 497 for i, c := range clusters { 498 actualNames[i] = c.NamespaceID().String() 499 } 500 501 // NB: order does not matter. 502 sort.Strings(actualNames) 503 sort.Strings(tt.expectedClusterNames) 504 assert.Equal(t, tt.expectedClusterNames, actualNames) 505 assert.Equal(t, tt.expectedType, fanoutType) 506 }) 507 } 508 } 509 510 func TestLongUnaggregatedRetention(t *testing.T) { 511 ctrl := gomock.NewController(t) 512 defer ctrl.Finish() 513 session := client.NewMockSession(ctrl) 514 retentionFiltered, retentionUnfiltered := time.Hour, time.Hour*10 515 resolution := time.Minute 516 517 clusters, err := NewClusters(UnaggregatedClusterNamespaceDefinition{ 518 NamespaceID: ident.StringID("UNAGG"), 519 Retention: retentionUnfiltered, 520 Session: session, 521 }, AggregatedClusterNamespaceDefinition{ 522 NamespaceID: ident.StringID("AGG_FILTERED"), 523 Retention: retentionFiltered, 524 Resolution: resolution, 525 Downsample: &ClusterNamespaceDownsampleOptions{All: false}, 526 Session: session, 527 }, AggregatedClusterNamespaceDefinition{ 528 NamespaceID: ident.StringID("AGG_NO_FILTER"), 529 Retention: retentionUnfiltered + time.Minute, 530 Resolution: resolution, 531 Downsample: &ClusterNamespaceDownsampleOptions{All: false}, 532 Session: session, 533 }, AggregatedClusterNamespaceDefinition{ 534 NamespaceID: ident.StringID("AGG_FILTERED_COMPLETE"), 535 Retention: retentionFiltered, 536 Resolution: resolution + time.Second, 537 Downsample: &ClusterNamespaceDownsampleOptions{All: true}, 538 Session: session, 539 }, AggregatedClusterNamespaceDefinition{ 540 NamespaceID: ident.StringID("AGG_NO_FILTER_COMPLETE"), 541 Retention: retentionUnfiltered, 542 Resolution: resolution + time.Second, 543 Downsample: &ClusterNamespaceDownsampleOptions{All: true}, 544 Session: session, 545 }) 546 547 require.NoError(t, err) 548 now := xtime.Now() 549 end := now 550 start := now.Add(time.Hour * -100) 551 opts := &storage.FanoutOptions{ 552 FanoutUnaggregated: storage.FanoutForceEnable, 553 FanoutAggregated: storage.FanoutForceEnable, 554 FanoutAggregatedOptimized: storage.FanoutForceEnable, 555 } 556 557 fanoutType, ns, err := resolveClusterNamespacesForQuery(now, start, end, clusters, 558 opts, nil, nil) 559 560 require.NoError(t, err) 561 actualNames := make([]string, len(ns)) 562 for i, c := range ns { 563 actualNames[i] = c.NamespaceID().String() 564 } 565 566 expected := []string{"UNAGG", "AGG_NO_FILTER"} 567 // NB: order does not matter. 568 sort.Strings(actualNames) 569 sort.Strings(expected) 570 assert.Equal(t, expected, actualNames) 571 assert.Equal(t, consolidators.NamespaceCoversPartialQueryRange, fanoutType) 572 } 573 574 func TestExampleCase(t *testing.T) { 575 ctrl := gomock.NewController(t) 576 defer ctrl.Finish() 577 578 session := client.NewMockSession(ctrl) 579 ns, err := NewClusters( 580 UnaggregatedClusterNamespaceDefinition{ 581 NamespaceID: ident.StringID("metrics_10s_24h"), 582 Retention: 24 * time.Hour, 583 Session: session, 584 }, AggregatedClusterNamespaceDefinition{ 585 NamespaceID: ident.StringID("metrics_180s_360h"), 586 Retention: 360 * time.Hour, 587 Resolution: 120 * time.Second, 588 Downsample: &ClusterNamespaceDownsampleOptions{All: false}, 589 Session: session, 590 }, AggregatedClusterNamespaceDefinition{ 591 NamespaceID: ident.StringID("metrics_600s_17520h"), 592 Retention: 17520 * time.Hour, 593 Resolution: 600 * time.Second, 594 Downsample: &ClusterNamespaceDownsampleOptions{All: false}, 595 Session: session, 596 }, 597 ) 598 require.NoError(t, err) 599 600 now := xtime.Now() 601 end := now 602 603 for i := 27; i < 17520; i++ { 604 start := now.Add(time.Hour * -1 * time.Duration(i)) 605 fanoutType, clusters, err := resolveClusterNamespacesForQuery(now, start, end, ns, 606 &storage.FanoutOptions{}, nil, nil) 607 608 require.NoError(t, err) 609 actualNames := make([]string, len(clusters)) 610 for i, c := range clusters { 611 actualNames[i] = c.NamespaceID().String() 612 } 613 614 // NB: order does not matter. 615 sort.Strings(actualNames) 616 assert.Equal(t, []string{ 617 "metrics_10s_24h", 618 "metrics_180s_360h", "metrics_600s_17520h", 619 }, actualNames) 620 assert.Equal(t, consolidators.NamespaceCoversPartialQueryRange, fanoutType) 621 } 622 } 623 624 func TestDeduplicatePartialAggregateNamespaces(t *testing.T) { 625 ctrl := gomock.NewController(t) 626 defer ctrl.Finish() 627 628 session := client.NewMockSession(ctrl) 629 ns, err := NewClusters(UnaggregatedClusterNamespaceDefinition{ 630 NamespaceID: ident.StringID("default"), 631 Retention: 24 * time.Hour, 632 Session: session, 633 }, AggregatedClusterNamespaceDefinition{ 634 NamespaceID: ident.StringID("aggregated_block_6h"), 635 Retention: 360 * time.Hour, 636 Resolution: 10 * time.Minute, 637 Downsample: &ClusterNamespaceDownsampleOptions{All: false}, 638 Session: session, 639 }, AggregatedClusterNamespaceDefinition{ 640 NamespaceID: ident.StringID("aggregated_block_6h"), 641 Retention: 360 * time.Hour, 642 Resolution: 1 * time.Hour, 643 Downsample: &ClusterNamespaceDownsampleOptions{All: true}, 644 Session: session, 645 }) 646 require.NoError(t, err) 647 648 now := xtime.Now() 649 end := now 650 651 start := now.Add(-48 * time.Hour) 652 fanoutType, clusters, err := resolveClusterNamespacesForQuery(now, start, end, ns, 653 &storage.FanoutOptions{}, nil, nil) 654 require.NoError(t, err) 655 656 actualNames := make([]string, len(clusters)) 657 for i, c := range clusters { 658 actualNames[i] = c.NamespaceID().String() 659 } 660 661 // NB: order does not matter. 662 sort.Strings(actualNames) 663 assert.Equal(t, []string{"aggregated_block_6h"}, actualNames) 664 assert.Equal(t, consolidators.NamespaceCoversAllQueryRange, fanoutType) 665 } 666 667 func TestResolveNamespaceWithDataLatency(t *testing.T) { 668 ctrl := gomock.NewController(t) 669 defer ctrl.Finish() 670 671 dataLatency := 10 * time.Hour 672 673 session := client.NewMockSession(ctrl) 674 ns, err := NewClusters( 675 UnaggregatedClusterNamespaceDefinition{ 676 NamespaceID: ident.StringID("default"), 677 Retention: 24 * time.Hour, 678 Session: session, 679 }, 680 AggregatedClusterNamespaceDefinition{ 681 NamespaceID: ident.StringID("aggregated_30d"), 682 Retention: 30 * 24 * time.Hour, 683 Resolution: 5 * time.Minute, 684 Downsample: &ClusterNamespaceDownsampleOptions{All: true}, 685 Session: session, 686 }, 687 AggregatedClusterNamespaceDefinition{ 688 NamespaceID: ident.StringID("aggregated_60d"), 689 Retention: 60 * 24 * time.Hour, 690 Resolution: 10 * time.Minute, 691 Downsample: &ClusterNamespaceDownsampleOptions{All: true}, 692 DataLatency: dataLatency, 693 Session: session, 694 }, 695 ) 696 require.NoError(t, err) 697 698 var ( 699 now = xtime.Now() 700 start = now.Add(-40 * 24 * time.Hour) 701 end = now.Add(-3 * time.Hour) 702 ) 703 704 fanoutType, clusters, err := resolveClusterNamespacesForQuery(now, start, end, ns, 705 &storage.FanoutOptions{}, nil, nil) 706 require.NoError(t, err) 707 708 actualNamespaces := make(map[string]narrowing) 709 for _, c := range clusters { 710 actualNamespaces[c.NamespaceID().String()] = c.narrowing 711 } 712 713 stitchAt := now.Add(-dataLatency).Truncate(10 * time.Minute) 714 expectedNamespaces := map[string]narrowing{ 715 "default": {start: stitchAt}, 716 "aggregated_60d": {end: stitchAt}, 717 } 718 719 assert.Equal(t, expectedNamespaces, actualNamespaces) 720 assert.Equal(t, consolidators.NamespaceCoversAllQueryRange, fanoutType) 721 }