github.com/m3db/m3@v1.5.1-0.20231129193456-75a402aa583b/src/dbnode/storage/bootstrap/bootstrapper/commitlog/source_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 commitlog 22 23 import ( 24 "testing" 25 "time" 26 27 "github.com/m3db/m3/src/dbnode/namespace" 28 "github.com/m3db/m3/src/dbnode/persist/fs" 29 "github.com/m3db/m3/src/dbnode/persist/fs/commitlog" 30 "github.com/m3db/m3/src/dbnode/storage/bootstrap" 31 "github.com/m3db/m3/src/dbnode/storage/bootstrap/result" 32 "github.com/m3db/m3/src/dbnode/ts" 33 "github.com/m3db/m3/src/x/ident" 34 "github.com/m3db/m3/src/x/pool" 35 "github.com/m3db/m3/src/x/serialize" 36 xtime "github.com/m3db/m3/src/x/time" 37 38 "github.com/stretchr/testify/require" 39 ) 40 41 var namespaceOptions = namespace.NewOptions() 42 43 func toEncodedBytes( 44 t *testing.T, 45 pool serialize.TagEncoderPool, 46 tags ...ident.Tag, 47 ) []byte { 48 encoder := pool.Get() 49 seriesTags := ident.NewTags(tags...) 50 err := encoder.Encode(ident.NewTagsIterator(seriesTags)) 51 require.NoError(t, err) 52 data, ok := encoder.Data() 53 require.True(t, ok) 54 return data.Bytes() 55 } 56 57 func TestBootstrapIndex(t *testing.T) { 58 var ( 59 opts = testDefaultOpts 60 src = newCommitLogSource(opts, fs.Inspection{}).(*commitLogSource) 61 dataBlockSize = 2 * time.Hour 62 indexBlockSize = 4 * time.Hour 63 namespaceOptions = namespaceOptions. 64 SetRetentionOptions( 65 namespaceOptions. 66 RetentionOptions(). 67 SetBlockSize(dataBlockSize), 68 ). 69 SetIndexOptions( 70 namespaceOptions. 71 IndexOptions(). 72 SetBlockSize(indexBlockSize). 73 SetEnabled(true), 74 ) 75 ) 76 md1, err := namespace.NewMetadata(testNamespaceID, namespaceOptions) 77 require.NoError(t, err) 78 79 testNamespaceID2 := ident.StringID("someOtherNamespace") 80 md2, err := namespace.NewMetadata(testNamespaceID2, namespaceOptions) 81 require.NoError(t, err) 82 83 testNamespaceNoData := ident.StringID("noData") 84 md3, err := namespace.NewMetadata(testNamespaceNoData, namespaceOptions) 85 require.NoError(t, err) 86 87 now := xtime.Now() 88 start := now.Truncate(indexBlockSize) 89 90 testTagEncodingPool := serialize. 91 NewTagEncoderPool(serialize.NewTagEncoderOptions(), 92 pool.NewObjectPoolOptions().SetSize(1)) 93 testTagEncodingPool.Init() 94 95 fooTags := toEncodedBytes(t, testTagEncodingPool, 96 ident.StringTag("city", "ny"), 97 ident.StringTag("conference", "monitoroma"), 98 ) 99 100 barTags := toEncodedBytes(t, testTagEncodingPool, 101 ident.StringTag("city", "sf")) 102 bazTags := toEncodedBytes(t, testTagEncodingPool, 103 ident.StringTag("city", "oakland")) 104 105 shardn := func(n int) uint32 { return uint32(n) } 106 foo := ts.Series{UniqueIndex: 0, Namespace: testNamespaceID, Shard: shardn(0), 107 ID: ident.StringID("foo"), EncodedTags: fooTags} 108 bar := ts.Series{UniqueIndex: 1, Namespace: testNamespaceID, Shard: shardn(0), 109 ID: ident.StringID("bar"), EncodedTags: barTags} 110 baz := ts.Series{UniqueIndex: 2, Namespace: testNamespaceID, Shard: shardn(5), 111 ID: ident.StringID("baz"), EncodedTags: bazTags} 112 // Make sure we can handle series that don't have tags. 113 untagged := ts.Series{UniqueIndex: 3, Namespace: testNamespaceID, 114 Shard: shardn(5), ID: ident.StringID("untagged")} 115 // Make sure we skip series that are not within the bootstrap range. 116 outOfRange := ts.Series{UniqueIndex: 4, Namespace: testNamespaceID, 117 Shard: shardn(3), ID: ident.StringID("outOfRange")} 118 // Make sure we skip and dont panic on writes for shards that are higher than the maximum we're trying to bootstrap. 119 shardTooHigh := ts.Series{UniqueIndex: 5, Namespace: testNamespaceID, 120 Shard: shardn(100), ID: ident.StringID("shardTooHigh")} 121 // Make sure we skip series for shards that have no requested bootstrap ranges. The shard for this write needs 122 // to be less than the highest shard we actually plan to bootstrap. 123 noShardBootstrapRange := ts.Series{UniqueIndex: 6, Namespace: testNamespaceID, 124 Shard: shardn(4), ID: ident.StringID("noShardBootstrapRange")} 125 // Make sure it handles multiple namespaces 126 someOtherNamespace := ts.Series{UniqueIndex: 7, Namespace: testNamespaceID2, 127 Shard: shardn(0), ID: ident.StringID("series_OtherNamespace")} 128 129 valuesNs := testValues{ 130 {foo, start, 1.0, xtime.Second, nil}, 131 {foo, start, 2.0, xtime.Second, nil}, 132 {foo, start.Add(dataBlockSize), 3.0, xtime.Second, nil}, 133 {bar, start.Add(dataBlockSize), 1.0, xtime.Second, nil}, 134 {bar, start.Add(dataBlockSize), 2.0, xtime.Second, nil}, 135 {baz, start.Add(2 * dataBlockSize), 1.0, xtime.Second, nil}, 136 {baz, start.Add(2 * dataBlockSize), 2.0, xtime.Second, nil}, 137 {untagged, start.Add(2 * dataBlockSize), 1.0, xtime.Second, nil}, 138 } 139 140 invalidNs := testValues{ 141 {outOfRange, start.Add(-dataBlockSize), 1.0, xtime.Second, nil}, 142 {shardTooHigh, start.Add(dataBlockSize), 1.0, xtime.Second, nil}, 143 {noShardBootstrapRange, start.Add(dataBlockSize), 1.0, xtime.Second, nil}, 144 } 145 146 valuesOtherNs := testValues{ 147 {someOtherNamespace, start.Add(dataBlockSize), 1.0, xtime.Second, nil}, 148 } 149 150 values := append(valuesNs, invalidNs...) 151 values = append(values, valuesOtherNs...) 152 src.newIteratorFn = func( 153 _ commitlog.IteratorOpts, 154 ) (commitlog.Iterator, []commitlog.ErrorWithPath, error) { 155 return newTestCommitLogIterator(values, nil), nil, nil 156 } 157 158 ranges := xtime.NewRanges( 159 xtime.Range{Start: start, End: start.Add(dataBlockSize)}, 160 xtime.Range{Start: start.Add(dataBlockSize), End: start.Add(2 * dataBlockSize)}, 161 xtime.Range{Start: start.Add(2 * dataBlockSize), End: start.Add(3 * dataBlockSize)}) 162 163 // Don't include ranges for shard 4 as thats how we're testing the noShardBootstrapRange series. 164 targetRanges := result.NewShardTimeRanges().Set( 165 shardn(0), 166 ranges, 167 ).Set( 168 shardn(1), 169 ranges, 170 ).Set( 171 shardn(2), 172 ranges, 173 ).Set( 174 shardn(5), 175 ranges, 176 ) 177 178 tester := bootstrap.BuildNamespacesTester(t, testDefaultRunOpts, targetRanges, md1, md2, md3) 179 defer tester.Finish() 180 181 tester.TestReadWith(src) 182 tester.TestUnfulfilledForNamespaceIsEmpty(md1) 183 tester.TestUnfulfilledForNamespaceIsEmpty(md2) 184 tester.TestUnfulfilledForNamespaceIsEmpty(md3) 185 186 nsWrites := tester.EnsureDumpWritesForNamespace(md1) 187 enforceValuesAreCorrect(t, valuesNs, nsWrites) 188 189 otherNamespaceWrites := tester.EnsureDumpWritesForNamespace(md2) 190 enforceValuesAreCorrect(t, valuesOtherNs, otherNamespaceWrites) 191 192 noWrites := tester.EnsureDumpWritesForNamespace(md3) 193 require.Equal(t, 0, len(noWrites)) 194 tester.EnsureNoLoadedBlocks() 195 } 196 197 func TestBootstrapIndexEmptyShardTimeRanges(t *testing.T) { 198 var ( 199 opts = testDefaultOpts 200 src = newCommitLogSource(opts, fs.Inspection{}).(*commitLogSource) 201 dataBlockSize = 2 * time.Hour 202 indexBlockSize = 4 * time.Hour 203 namespaceOptions = namespace.NewOptions(). 204 SetRetentionOptions( 205 namespace.NewOptions(). 206 RetentionOptions(). 207 SetBlockSize(dataBlockSize), 208 ). 209 SetIndexOptions( 210 namespace.NewOptions(). 211 IndexOptions(). 212 SetBlockSize(indexBlockSize). 213 SetEnabled(true), 214 ) 215 ) 216 md, err := namespace.NewMetadata(testNamespaceID, namespaceOptions) 217 require.NoError(t, err) 218 219 values := testValues{} 220 src.newIteratorFn = func(_ commitlog.IteratorOpts) (commitlog.Iterator, []commitlog.ErrorWithPath, error) { 221 return newTestCommitLogIterator(values, nil), nil, nil 222 } 223 224 target := result.NewShardTimeRanges() 225 tester := bootstrap.BuildNamespacesTester(t, testDefaultRunOpts, target, md) 226 defer tester.Finish() 227 228 tester.TestReadWith(src) 229 tester.TestUnfulfilledForNamespaceIsEmpty(md) 230 tester.EnsureNoLoadedBlocks() 231 tester.EnsureNoWrites() 232 }