github.com/m3db/m3@v1.5.1-0.20231129193456-75a402aa583b/src/dbnode/storage/bootstrap/bootstrapper/base_test.go (about) 1 // Copyright (c) 2016 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 bootstrapper 22 23 import ( 24 "testing" 25 "time" 26 27 "github.com/m3db/m3/src/dbnode/namespace" 28 "github.com/m3db/m3/src/dbnode/storage/bootstrap" 29 "github.com/m3db/m3/src/dbnode/storage/bootstrap/result" 30 "github.com/m3db/m3/src/m3ninx/persist" 31 "github.com/m3db/m3/src/x/context" 32 "github.com/m3db/m3/src/x/ident" 33 xtime "github.com/m3db/m3/src/x/time" 34 35 "github.com/golang/mock/gomock" 36 "github.com/stretchr/testify/assert" 37 "github.com/stretchr/testify/require" 38 ) 39 40 var ( 41 testNamespaceID = ident.StringID("testNamespace") 42 testTargetStart = xtime.Now() 43 testShard = uint32(0) 44 testDefaultRunOpts = bootstrap.NewRunOptions(). 45 SetPersistConfig(bootstrap.PersistConfig{Enabled: false}) 46 ) 47 48 func testNsMetadata(t *testing.T, withIndex bool) namespace.Metadata { 49 opts := namespace.NewOptions() 50 opts = opts.SetIndexOptions(opts.IndexOptions().SetEnabled(withIndex)) 51 md, err := namespace.NewMetadata(testNamespaceID, opts) 52 require.NoError(t, err) 53 return md 54 } 55 56 func testBaseBootstrapper( 57 t *testing.T, 58 ctrl *gomock.Controller, 59 ) (*bootstrap.MockSource, *bootstrap.MockBootstrapper, baseBootstrapper) { 60 source := bootstrap.NewMockSource(ctrl) 61 opts := result.NewOptions() 62 next := bootstrap.NewMockBootstrapper(ctrl) 63 bootstrapper, err := NewBaseBootstrapper("mock", source, opts, next) 64 require.NoError(t, err) 65 baseBootstrapper := bootstrapper.(baseBootstrapper) 66 return source, next, baseBootstrapper 67 } 68 69 func testTargetRanges() xtime.Ranges { 70 return xtime.NewRanges(xtime.Range{Start: testTargetStart, End: testTargetStart.Add(2 * time.Hour)}) 71 } 72 73 func testShardTimeRanges() result.ShardTimeRanges { 74 r := result.NewShardTimeRanges() 75 r.Set(testShard, testTargetRanges()) 76 return r 77 } 78 79 func testResult( 80 ns namespace.Metadata, 81 withIndex bool, 82 shard uint32, 83 unfulfilledRange xtime.Ranges, 84 indexResult result.IndexBootstrapResult, 85 ) bootstrap.NamespaceResults { 86 unfulfilled := result.NewShardTimeRanges() 87 unfulfilled.Set(shard, unfulfilledRange) 88 89 opts := bootstrap.NamespaceResultsMapOptions{} 90 results := bootstrap.NewNamespaceResultsMap(opts) 91 dataResult := result.NewDataBootstrapResult() 92 dataResult.SetUnfulfilled(unfulfilled.Copy()) 93 94 if withIndex { 95 indexResult.SetUnfulfilled(unfulfilled.Copy()) 96 } 97 98 results.Set(ns.ID(), bootstrap.NamespaceResult{ 99 Metadata: ns, 100 Shards: []uint32{shard}, 101 DataResult: dataResult, 102 IndexResult: indexResult, 103 }) 104 105 return bootstrap.NamespaceResults{Results: results} 106 } 107 108 func testEmptyResult( 109 ns namespace.Metadata, 110 ) bootstrap.NamespaceResults { 111 opts := bootstrap.NamespaceResultsMapOptions{} 112 results := bootstrap.NewNamespaceResultsMap(opts) 113 results.Set(ns.ID(), bootstrap.NamespaceResult{ 114 Metadata: ns, 115 DataResult: result.NewDataBootstrapResult(), 116 IndexResult: result.NewIndexBootstrapResult(), 117 }) 118 119 return bootstrap.NamespaceResults{Results: results} 120 } 121 122 func TestBaseBootstrapperEmptyRange(t *testing.T) { 123 testBaseBootstrapperEmptyRange(t, false) 124 } 125 126 func TestBaseBootstrapperEmptyRangeWithIndex(t *testing.T) { 127 testBaseBootstrapperEmptyRange(t, true) 128 } 129 130 func testBaseBootstrapperEmptyRange(t *testing.T, withIndex bool) { 131 ctrl := gomock.NewController(t) 132 defer ctrl.Finish() 133 src, next, base := testBaseBootstrapper(t, ctrl) 134 testNs := testNsMetadata(t, withIndex) 135 136 rngs := result.NewShardTimeRanges() 137 unfulfilled := xtime.NewRanges() 138 nsResults := testResult(testNs, withIndex, testShard, unfulfilled, result.NewIndexBootstrapResult()) 139 nextResult := testResult(testNs, withIndex, testShard, xtime.NewRanges(), result.NewIndexBootstrapResult()) 140 shardRangeMatcher := bootstrap.ShardTimeRangesMatcher{Ranges: rngs} 141 142 tester := bootstrap.BuildNamespacesTester(t, testDefaultRunOpts, rngs, testNs) 143 defer tester.Finish() 144 145 cache := tester.Cache 146 src.EXPECT().AvailableData(testNs, shardRangeMatcher, cache, testDefaultRunOpts). 147 Return(rngs, nil) 148 if withIndex { 149 src.EXPECT().AvailableIndex(testNs, shardRangeMatcher, cache, testDefaultRunOpts). 150 Return(rngs, nil) 151 } 152 153 matcher := bootstrap.NamespaceMatcher{Namespaces: tester.Namespaces} 154 src.EXPECT(). 155 Read(gomock.Any(), matcher, cache). 156 DoAndReturn(func( 157 ctx context.Context, 158 namespaces bootstrap.Namespaces, 159 cache bootstrap.Cache, 160 ) (bootstrap.NamespaceResults, error) { 161 return nsResults, nil 162 }) 163 next.EXPECT().Bootstrap(gomock.Any(), matcher, cache).Return(nextResult, nil) 164 165 // Test non-nil empty range 166 tester.TestBootstrapWith(base) 167 tester.TestUnfulfilledForNamespaceIsEmpty(testNs) 168 assert.Equal(t, nsResults, tester.Results) 169 170 tester.EnsureNoLoadedBlocks() 171 tester.EnsureNoWrites() 172 } 173 174 func TestBaseBootstrapperCurrentNoUnfulfilled(t *testing.T) { 175 testBaseBootstrapperCurrentNoUnfulfilled(t, false) 176 } 177 178 func TestBaseBootstrapperCurrentNoUnfulfilledWithIndex(t *testing.T) { 179 testBaseBootstrapperCurrentNoUnfulfilled(t, true) 180 } 181 182 func testBaseBootstrapperCurrentNoUnfulfilled(t *testing.T, withIndex bool) { 183 ctrl := gomock.NewController(t) 184 defer ctrl.Finish() 185 src, next, base := testBaseBootstrapper(t, ctrl) 186 testNs := testNsMetadata(t, withIndex) 187 188 unfulfilled := xtime.NewRanges() 189 nsResults := testResult(testNs, withIndex, testShard, unfulfilled, result.NewIndexBootstrapResult()) 190 nextResult := testResult(testNs, withIndex, testShard, xtime.NewRanges(), result.NewIndexBootstrapResult()) 191 192 targetRanges := testShardTimeRanges() 193 194 tester := bootstrap.BuildNamespacesTester(t, testDefaultRunOpts, targetRanges, 195 testNs) 196 defer tester.Finish() 197 198 cache := tester.Cache 199 src.EXPECT().AvailableData(testNs, targetRanges, cache, testDefaultRunOpts). 200 Return(targetRanges, nil) 201 if withIndex { 202 src.EXPECT().AvailableIndex(testNs, targetRanges, cache, testDefaultRunOpts). 203 Return(targetRanges, nil) 204 } 205 206 matcher := bootstrap.NamespaceMatcher{Namespaces: tester.Namespaces} 207 src.EXPECT(). 208 Read(gomock.Any(), matcher, cache). 209 DoAndReturn(func( 210 ctx context.Context, 211 namespaces bootstrap.Namespaces, 212 cache bootstrap.Cache, 213 ) (bootstrap.NamespaceResults, error) { 214 return nsResults, nil 215 }) 216 next.EXPECT().Bootstrap(gomock.Any(), matcher, cache).Return(nextResult, nil) 217 218 tester.TestBootstrapWith(base) 219 assert.Equal(t, nsResults, tester.Results) 220 tester.TestUnfulfilledForNamespaceIsEmpty(testNs) 221 222 tester.EnsureNoLoadedBlocks() 223 tester.EnsureNoWrites() 224 } 225 226 func TestBaseBootstrapperCurrentSomeUnfulfilled(t *testing.T) { 227 testBaseBootstrapperCurrentSomeUnfulfilled(t, false) 228 } 229 230 func TestBaseBootstrapperCurrentSomeUnfulfilledWithIndex(t *testing.T) { 231 testBaseBootstrapperCurrentSomeUnfulfilled(t, true) 232 } 233 234 func testBaseBootstrapperCurrentSomeUnfulfilled(t *testing.T, withIndex bool) { 235 ctrl := gomock.NewController(t) 236 defer ctrl.Finish() 237 src, next, base := testBaseBootstrapper(t, ctrl) 238 testNs := testNsMetadata(t, withIndex) 239 targetRanges := testShardTimeRanges() 240 currUnfulfilled := xtime.NewRanges(xtime.Range{ 241 Start: testTargetStart.Add(time.Hour), 242 End: testTargetStart.Add(time.Hour * 2), 243 }) 244 245 currResult := testResult(testNs, withIndex, testShard, currUnfulfilled, result.NewIndexBootstrapResult()) 246 nextResult := testResult(testNs, withIndex, testShard, xtime.NewRanges(), result.NewIndexBootstrapResult()) 247 tester := bootstrap.BuildNamespacesTester(t, testDefaultRunOpts, targetRanges, 248 testNs) 249 defer tester.Finish() 250 251 cache := tester.Cache 252 src.EXPECT().AvailableData(testNs, targetRanges, cache, testDefaultRunOpts). 253 Return(targetRanges, nil) 254 if withIndex { 255 src.EXPECT().AvailableIndex(testNs, targetRanges, cache, testDefaultRunOpts). 256 Return(targetRanges, nil) 257 } 258 259 matcher := bootstrap.NamespaceMatcher{Namespaces: tester.Namespaces} 260 src.EXPECT().Read(gomock.Any(), matcher, cache).Return(currResult, nil) 261 next.EXPECT().Bootstrap(gomock.Any(), matcher, cache).Return(nextResult, nil) 262 263 tester.TestBootstrapWith(base) 264 tester.TestUnfulfilledForNamespaceIsEmpty(testNs) 265 } 266 267 func testBaseBootstrapperNext( 268 t *testing.T, 269 nextUnfulfilled xtime.Ranges, 270 withIndex bool, 271 ) { 272 ctrl := gomock.NewController(t) 273 defer ctrl.Finish() 274 src, next, base := testBaseBootstrapper(t, ctrl) 275 testNs := testNsMetadata(t, withIndex) 276 targetRanges := testShardTimeRanges() 277 278 tester := bootstrap.BuildNamespacesTester(t, testDefaultRunOpts, targetRanges, 279 testNs) 280 defer tester.Finish() 281 282 cache := tester.Cache 283 src.EXPECT(). 284 AvailableData(testNs, targetRanges, cache, testDefaultRunOpts). 285 Return(result.NewShardTimeRanges(), nil) 286 if withIndex { 287 src.EXPECT(). 288 AvailableIndex(testNs, targetRanges, cache, testDefaultRunOpts). 289 Return(result.NewShardTimeRanges(), nil) 290 } 291 292 emptyResult := testEmptyResult(testNs) 293 nextResult := testResult(testNs, withIndex, testShard, nextUnfulfilled, result.NewIndexBootstrapResult()) 294 matcher := bootstrap.NamespaceMatcher{Namespaces: tester.Namespaces} 295 src.EXPECT().Read(gomock.Any(), matcher, cache).Return(emptyResult, nil) 296 next.EXPECT().Bootstrap(gomock.Any(), matcher, cache).Return(nextResult, nil) 297 298 tester.TestBootstrapWith(base) 299 300 ex, ok := nextResult.Results.Get(testNs.ID()) 301 require.True(t, ok) 302 303 expected := ex.DataResult.Unfulfilled() 304 expectedIdx := ex.IndexResult.Unfulfilled() 305 if !withIndex { 306 expectedIdx = result.NewShardTimeRanges() 307 } 308 309 tester.TestUnfulfilledForNamespace(testNs, expected, expectedIdx) 310 } 311 312 func TestBaseBootstrapperNextNoUnfulfilled(t *testing.T) { 313 nextUnfulfilled := xtime.NewRanges() 314 testBaseBootstrapperNext(t, nextUnfulfilled, false) 315 } 316 317 func TestBaseBootstrapperNextNoUnfulfilledWithIndex(t *testing.T) { 318 nextUnfulfilled := xtime.NewRanges() 319 testBaseBootstrapperNext(t, nextUnfulfilled, true) 320 } 321 322 func TestBaseBootstrapperNextSomeUnfulfilled(t *testing.T) { 323 nextUnfulfilled := xtime.NewRanges(xtime.Range{ 324 Start: testTargetStart, 325 End: testTargetStart.Add(time.Hour), 326 }) 327 328 testBaseBootstrapperNext(t, nextUnfulfilled, false) 329 } 330 331 func TestBaseBootstrapperNextSomeUnfulfilledWithIndex(t *testing.T) { 332 nextUnfulfilled := xtime.NewRanges(xtime.Range{ 333 Start: testTargetStart, 334 End: testTargetStart.Add(time.Hour), 335 }) 336 337 testBaseBootstrapperNext(t, nextUnfulfilled, true) 338 } 339 340 func TestBaseBootstrapperNextAddIndexResults(t *testing.T) { 341 ctrl := gomock.NewController(t) 342 defer ctrl.Finish() 343 src, next, base := testBaseBootstrapper(t, ctrl) 344 testNs := testNsMetadata(t, true) 345 targetRanges := testShardTimeRanges() 346 347 tester := bootstrap.BuildNamespacesTester(t, testDefaultRunOpts, targetRanges, testNs) 348 defer tester.Finish() 349 350 cache := tester.Cache 351 src.EXPECT(). 352 AvailableData(testNs, targetRanges, cache, testDefaultRunOpts). 353 Return(result.NewShardTimeRanges(), nil) 354 src.EXPECT(). 355 AvailableIndex(testNs, targetRanges, cache, testDefaultRunOpts). 356 Return(result.NewShardTimeRanges(), nil) 357 358 var ( 359 blockStart = xtime.UnixNano(1) 360 361 nextBlock = result.NewIndexBlock(nil, result.NewShardTimeRanges()) 362 nextBlocks = result.NewIndexBlockByVolumeType(blockStart) 363 nextIndexResult = result.NewIndexBootstrapResult() 364 ) 365 366 nextBlocks.SetBlock(persist.DefaultIndexVolumeType, nextBlock) 367 nextIndexResult.IndexResults().Add(nextBlocks) 368 369 emptyResult := testEmptyResult(testNs) 370 nextResult := testResult(testNs, true, testShard, xtime.NewRanges(), nextIndexResult) 371 372 matcher := bootstrap.NamespaceMatcher{Namespaces: tester.Namespaces} 373 src.EXPECT().Read(gomock.Any(), matcher, cache).Return(emptyResult, nil) 374 next.EXPECT().Bootstrap(gomock.Any(), matcher, cache).Return(nextResult, nil) 375 376 tester.TestBootstrapWith(base) 377 378 tester.TestIndexResultForNamespace(testNs, nextIndexResult) 379 }