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