github.com/m3db/m3@v1.5.0/src/dbnode/storage/bootstrap/result/result_index_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 "github.com/m3db/m3/src/dbnode/namespace" 28 "github.com/m3db/m3/src/m3ninx/index/segment" 29 idxpersist "github.com/m3db/m3/src/m3ninx/persist" 30 xtime "github.com/m3db/m3/src/x/time" 31 32 "github.com/golang/mock/gomock" 33 "github.com/stretchr/testify/assert" 34 "github.com/stretchr/testify/require" 35 ) 36 37 func TestIndexResultMergeMergesExistingSegments(t *testing.T) { 38 ctrl := gomock.NewController(t) 39 defer ctrl.Finish() 40 41 start := xtime.Now().Truncate(testBlockSize) 42 43 segments := []Segment{ 44 NewSegment(segment.NewMockSegment(ctrl), false), 45 NewSegment(segment.NewMockSegment(ctrl), false), 46 NewSegment(segment.NewMockSegment(ctrl), false), 47 NewSegment(segment.NewMockSegment(ctrl), false), 48 NewSegment(segment.NewMockSegment(ctrl), false), 49 NewSegment(segment.NewMockSegment(ctrl), false), 50 } 51 52 times := []xtime.UnixNano{start, start.Add(testBlockSize), start.Add(2 * testBlockSize)} 53 tr0 := NewShardTimeRangesFromRange(times[0], times[1], 1, 2, 3) 54 tr1 := NewShardTimeRangesFromRange(times[1], times[2], 1, 2, 3) 55 56 first := NewIndexBootstrapResult() 57 blk1 := NewIndexBlockByVolumeType(times[0]) 58 blk1.SetBlock(idxpersist.DefaultIndexVolumeType, NewIndexBlock([]Segment{segments[0]}, tr0)) 59 first.Add(blk1, nil) 60 blk2 := NewIndexBlockByVolumeType(times[0]) 61 blk2.SetBlock(idxpersist.DefaultIndexVolumeType, NewIndexBlock([]Segment{segments[1]}, tr0)) 62 first.Add(blk2, nil) 63 blk3 := NewIndexBlockByVolumeType(times[1]) 64 blk3.SetBlock(idxpersist.DefaultIndexVolumeType, NewIndexBlock([]Segment{segments[2], segments[3]}, tr1)) 65 first.Add(blk3, nil) 66 67 second := NewIndexBootstrapResult() 68 blk4 := NewIndexBlockByVolumeType(times[0]) 69 blk4.SetBlock(idxpersist.DefaultIndexVolumeType, NewIndexBlock([]Segment{segments[4]}, tr0)) 70 second.Add(blk4, nil) 71 blk5 := NewIndexBlockByVolumeType(times[1]) 72 blk5.SetBlock(idxpersist.DefaultIndexVolumeType, NewIndexBlock([]Segment{segments[5]}, tr1)) 73 second.Add(blk5, nil) 74 75 merged := MergedIndexBootstrapResult(first, second) 76 77 expected := NewIndexBootstrapResult() 78 blk6 := NewIndexBlockByVolumeType(times[0]) 79 blk6.SetBlock(idxpersist.DefaultIndexVolumeType, NewIndexBlock([]Segment{segments[0], segments[1], segments[4]}, tr0)) 80 expected.Add(blk6, nil) 81 blk7 := NewIndexBlockByVolumeType(times[1]) 82 blk7.SetBlock(idxpersist.DefaultIndexVolumeType, NewIndexBlock([]Segment{segments[2], segments[3], segments[5]}, tr1)) 83 expected.Add(blk7, nil) 84 85 assert.True(t, segmentsInResultsSame(expected.IndexResults(), merged.IndexResults())) 86 } 87 88 func TestIndexResultSetUnfulfilled(t *testing.T) { 89 ctrl := gomock.NewController(t) 90 defer ctrl.Finish() 91 92 t0 := xtime.Now().Truncate(time.Hour) 93 tn := func(i int) xtime.UnixNano { 94 return t0.Add(time.Duration(i) * time.Hour) 95 } 96 results := NewIndexBootstrapResult() 97 testRanges := NewShardTimeRangesFromRange(tn(0), tn(1), 1, 2, 3, 4, 5, 6, 7, 8, 9, 10) 98 results.SetUnfulfilled(testRanges) 99 require.Equal(t, testRanges, results.Unfulfilled()) 100 } 101 102 func TestIndexResultAdd(t *testing.T) { 103 ctrl := gomock.NewController(t) 104 defer ctrl.Finish() 105 106 t0 := xtime.Now().Truncate(time.Hour) 107 tn := func(i int) xtime.UnixNano { 108 return t0.Add(time.Duration(i) * time.Hour) 109 } 110 results := NewIndexBootstrapResult() 111 testRanges := NewShardTimeRangesFromRange(tn(0), tn(1), 1, 2, 3, 4, 5, 6, 7, 8, 9, 10) 112 results.Add(NewIndexBlockByVolumeType(0), testRanges) 113 require.Equal(t, testRanges, results.Unfulfilled()) 114 } 115 116 func TestShardTimeRangesToUnfulfilledIndexResult(t *testing.T) { 117 now := xtime.Now() 118 str := shardTimeRanges{ 119 0: xtime.NewRanges(xtime.Range{ 120 Start: now, 121 End: now.Add(time.Minute), 122 }), 123 1: xtime.NewRanges(xtime.Range{ 124 Start: now.Add(3 * time.Minute), 125 End: now.Add(4 * time.Minute), 126 }), 127 } 128 r := str.ToUnfulfilledIndexResult() 129 assert.Equal(t, 0, len(r.IndexResults())) 130 assert.True(t, r.Unfulfilled().Equal(str)) 131 } 132 133 func TestIndexResultsMarkFulfilled(t *testing.T) { 134 ctrl := gomock.NewController(t) 135 defer ctrl.Finish() 136 137 iopts := namespace.NewIndexOptions().SetBlockSize(time.Hour * 2) 138 t0 := xtime.Now().Truncate(2 * time.Hour) 139 tn := func(i int) xtime.UnixNano { 140 return t0.Add(time.Duration(i) * time.Hour) 141 } 142 results := make(IndexResults) 143 144 // range checks 145 require.Error(t, results.MarkFulfilled(tn(0), 146 NewShardTimeRangesFromRange(tn(4), tn(6), 1), idxpersist.DefaultIndexVolumeType, iopts)) 147 require.Error(t, results.MarkFulfilled(tn(0), 148 NewShardTimeRangesFromRange(tn(-1), tn(1), 1), idxpersist.DefaultIndexVolumeType, iopts)) 149 150 // valid add 151 fulfilledRange := NewShardTimeRangesFromRange(tn(0), tn(1), 1) 152 require.NoError(t, results.MarkFulfilled(tn(0), fulfilledRange, idxpersist.DefaultIndexVolumeType, iopts)) 153 require.Equal(t, 1, len(results)) 154 blkByVolumeType, ok := results[tn(0)] 155 require.True(t, ok) 156 require.True(t, tn(0).Equal(blkByVolumeType.blockStart)) 157 blk, ok := blkByVolumeType.GetBlock(idxpersist.DefaultIndexVolumeType) 158 require.True(t, ok) 159 require.Equal(t, fulfilledRange, blk.fulfilled) 160 161 // additional add for same block 162 nextFulfilledRange := NewShardTimeRangesFromRange(tn(1), tn(2), 2) 163 require.NoError(t, results.MarkFulfilled(tn(1), nextFulfilledRange, idxpersist.DefaultIndexVolumeType, iopts)) 164 require.Equal(t, 1, len(results)) 165 blkByVolumeType, ok = results[tn(0)] 166 require.True(t, ok) 167 require.True(t, tn(0).Equal(blkByVolumeType.blockStart)) 168 fulfilledRange.AddRanges(nextFulfilledRange) 169 blk, ok = blkByVolumeType.GetBlock(idxpersist.DefaultIndexVolumeType) 170 require.True(t, ok) 171 require.Equal(t, fulfilledRange, blk.fulfilled) 172 173 // additional add for next block 174 nextFulfilledRange = NewShardTimeRangesFromRange(tn(2), tn(4), 1, 2, 3) 175 require.NoError(t, results.MarkFulfilled(tn(2), nextFulfilledRange, idxpersist.DefaultIndexVolumeType, iopts)) 176 require.Equal(t, 2, len(results)) 177 blkByVolumeType, ok = results[tn(2)] 178 require.True(t, ok) 179 require.True(t, tn(2).Equal(blkByVolumeType.blockStart)) 180 blk, ok = blkByVolumeType.GetBlock(idxpersist.DefaultIndexVolumeType) 181 require.True(t, ok) 182 require.Equal(t, nextFulfilledRange, blk.fulfilled) 183 } 184 185 func segmentsInResultsSame(a, b IndexResults) bool { 186 if len(a) != len(b) { 187 return false 188 } 189 for t, blockByVolumeType := range a { 190 otherBlockByVolumeType, ok := b[t] 191 if !ok { 192 return false 193 } 194 block, ok := blockByVolumeType.GetBlock(idxpersist.DefaultIndexVolumeType) 195 if !ok { 196 return false 197 } 198 otherBlock, ok := otherBlockByVolumeType.GetBlock(idxpersist.DefaultIndexVolumeType) 199 if !ok { 200 return false 201 } 202 if len(block.Segments()) != len(otherBlock.Segments()) { 203 return false 204 } 205 for i, s := range block.Segments() { 206 if s != otherBlock.Segments()[i] { 207 return false 208 } 209 } 210 } 211 return true 212 }