github.com/m3db/m3@v1.5.1-0.20231129193456-75a402aa583b/src/dbnode/storage/bootstrap/result/shard_ranges_test.go (about)

     1  // Copyright (c) 2020 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 result
    22  
    23  import (
    24  	"testing"
    25  	"time"
    26  
    27  	xtime "github.com/m3db/m3/src/x/time"
    28  
    29  	"github.com/stretchr/testify/assert"
    30  	"github.com/stretchr/testify/require"
    31  )
    32  
    33  func TestShardTimeRangesAdd(t *testing.T) {
    34  	start := xtime.Now().Truncate(testBlockSize)
    35  	times := []xtime.UnixNano{
    36  		start, start.Add(testBlockSize),
    37  		start.Add(2 * testBlockSize), start.Add(3 * testBlockSize),
    38  	}
    39  
    40  	sr := []ShardTimeRanges{
    41  		NewShardTimeRangesFromRange(times[0], times[1], 1, 2, 3),
    42  		NewShardTimeRangesFromRange(times[1], times[2], 1, 2, 3),
    43  		NewShardTimeRangesFromRange(times[2], times[3], 1, 2, 3),
    44  	}
    45  	ranges := NewShardTimeRanges()
    46  	for _, r := range sr {
    47  		ranges.AddRanges(r)
    48  	}
    49  	for i, r := range sr {
    50  		min, max, r := r.MinMaxRange()
    51  		require.Equal(t, r, testBlockSize)
    52  		require.Equal(t, min, times[i])
    53  		require.Equal(t, max, times[i+1])
    54  	}
    55  }
    56  
    57  func TestFilterShards(t *testing.T) {
    58  	start := xtime.Now().Truncate(testBlockSize)
    59  	end := start.Add(testBlockSize)
    60  	ranges := NewShardTimeRangesFromRange(start, end, 0, 1, 2)
    61  
    62  	tests := []struct {
    63  		name   string
    64  		filter []uint32
    65  		result []uint32
    66  	}{
    67  		{
    68  			name:   "empty filter",
    69  			filter: []uint32{},
    70  			result: []uint32{},
    71  		},
    72  		{
    73  			name:   "all exist",
    74  			filter: []uint32{0, 1, 2},
    75  			result: []uint32{0, 1, 2},
    76  		},
    77  		{
    78  			name:   "none exists",
    79  			filter: []uint32{10, 11, 12},
    80  			result: []uint32{},
    81  		},
    82  		{
    83  			name:   "some exist",
    84  			filter: []uint32{0, 1, 10, 11},
    85  			result: []uint32{0, 1},
    86  		},
    87  		{
    88  			name:   "some filtered out",
    89  			filter: []uint32{0, 1},
    90  			result: []uint32{0, 1},
    91  		},
    92  	}
    93  
    94  	for _, tt := range tests {
    95  		t.Run(tt.name, func(t *testing.T) {
    96  			filtered := ranges.FilterShards(tt.filter)
    97  			require.Equal(t, filtered.Len(), len(tt.result), "unexpected length")
    98  			for _, s := range tt.result {
    99  				_, ok := filtered.Get(s)
   100  				assert.True(t, ok, "missing shard %v", s)
   101  			}
   102  		})
   103  	}
   104  }
   105  
   106  func TestShardTimeRangesIsSuperset(t *testing.T) {
   107  	start := xtime.Now().Truncate(testBlockSize)
   108  
   109  	ranges1 := createShardTimeRanges(start, testBlockSize, 3, []uint32{1, 2, 3})
   110  	ranges2 := createShardTimeRanges(start, testBlockSize, 3, []uint32{1, 2})
   111  	ranges3 := createShardTimeRanges(start, testBlockSize, 1, []uint32{1, 2, 3})
   112  
   113  	require.True(t, ranges1.IsSuperset(ranges2))
   114  	require.True(t, ranges1.IsSuperset(ranges1))
   115  	require.True(t, ranges1.IsSuperset(ranges3))
   116  
   117  	// Subset of shards fails superset condition.
   118  	require.False(t, ranges2.IsSuperset(ranges1))
   119  	// Subset of time ranges fails superset condition.
   120  	require.False(t, ranges3.IsSuperset(ranges1))
   121  }
   122  
   123  func TestShardTimeRangesIsSupersetNoOverlapShards(t *testing.T) {
   124  	start := xtime.Now().Truncate(testBlockSize)
   125  
   126  	ranges1 := createShardTimeRanges(start, testBlockSize, 3, []uint32{1, 3, 5})
   127  	ranges2 := createShardTimeRanges(start, testBlockSize, 3, []uint32{2, 4, 6})
   128  
   129  	// Neither are supersets of each other as they have non overlapping shards.
   130  	require.False(t, ranges1.IsSuperset(ranges2))
   131  	require.False(t, ranges2.IsSuperset(ranges1))
   132  }
   133  
   134  func TestShardTimeRangesIsSupersetPartialOverlapShards(t *testing.T) {
   135  	start := xtime.Now().Truncate(testBlockSize)
   136  
   137  	ranges1 := createShardTimeRanges(start, testBlockSize, 3, []uint32{1, 3, 5})
   138  	ranges2 := createShardTimeRanges(start, testBlockSize, 3, []uint32{3, 4, 6})
   139  
   140  	// Neither are supersets of each other as they only have partially overlapping shards.
   141  	require.False(t, ranges1.IsSuperset(ranges2))
   142  	require.False(t, ranges2.IsSuperset(ranges1))
   143  }
   144  
   145  func TestShardTimeRangesIsSupersetNoOverlapTimeRanges(t *testing.T) {
   146  	start := xtime.Now().Truncate(testBlockSize)
   147  
   148  	ranges1 := createShardTimeRanges(start, testBlockSize, 3, []uint32{1, 3, 5})
   149  	ranges2 := createShardTimeRanges(start.Add(testBlockSize*3), testBlockSize, 3,
   150  		[]uint32{1, 3, 5})
   151  
   152  	// Neither are supersets of each other as they have non overlapping time ranges.
   153  	require.False(t, ranges1.IsSuperset(ranges2))
   154  	require.False(t, ranges2.IsSuperset(ranges1))
   155  }
   156  
   157  func TestShardTimeRangesIsSupersetPartialOverlapTimeRanges(t *testing.T) {
   158  	start := xtime.Now().Truncate(testBlockSize)
   159  
   160  	ranges1 := createShardTimeRanges(start, testBlockSize, 3, []uint32{1, 3, 5})
   161  	ranges2 := createShardTimeRanges(start.Add(testBlockSize*2), testBlockSize, 3,
   162  		[]uint32{1, 3, 5})
   163  
   164  	// Neither are supersets of each other as they have non overlapping time ranges.
   165  	require.False(t, ranges1.IsSuperset(ranges2))
   166  	require.False(t, ranges2.IsSuperset(ranges1))
   167  }
   168  
   169  func TestShardTimeRangesIsSupersetEmpty(t *testing.T) {
   170  	start := xtime.Now().Truncate(testBlockSize)
   171  
   172  	ranges1 := createShardTimeRanges(start, testBlockSize, 3, []uint32{1, 3, 5})
   173  	ranges2 := NewShardTimeRanges()
   174  
   175  	require.True(t, ranges1.IsSuperset(ranges2))
   176  	require.False(t, ranges2.IsSuperset(ranges1))
   177  	require.True(t, ranges2.IsSuperset(ranges2))
   178  }
   179  
   180  func createShardTimeRanges(
   181  	start xtime.UnixNano,
   182  	blockSize time.Duration,
   183  	numBlocks int,
   184  	shards []uint32,
   185  ) ShardTimeRanges {
   186  	ranges := NewShardTimeRanges()
   187  	for i := 0; i < numBlocks; i++ {
   188  		blockStart := start.Add(time.Duration(i) * blockSize)
   189  		ranges.AddRanges(NewShardTimeRangesFromRange(
   190  			blockStart,
   191  			blockStart.Add(blockSize),
   192  			shards...,
   193  		))
   194  	}
   195  	return ranges
   196  }