github.com/m3db/m3@v1.5.1-0.20231129193456-75a402aa583b/src/dbnode/storage/bootstrap/bootstrapper/peers/source_data_test.go (about) 1 // Copyright (c) 2017 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 peers 22 23 import ( 24 "errors" 25 "fmt" 26 "sync" 27 "testing" 28 29 "github.com/m3db/m3/src/dbnode/client" 30 "github.com/m3db/m3/src/dbnode/namespace" 31 "github.com/m3db/m3/src/dbnode/persist" 32 "github.com/m3db/m3/src/dbnode/persist/fs" 33 m3dbruntime "github.com/m3db/m3/src/dbnode/runtime" 34 "github.com/m3db/m3/src/dbnode/storage/block" 35 "github.com/m3db/m3/src/dbnode/storage/bootstrap" 36 "github.com/m3db/m3/src/dbnode/storage/bootstrap/result" 37 "github.com/m3db/m3/src/dbnode/storage/index" 38 "github.com/m3db/m3/src/dbnode/storage/index/compaction" 39 "github.com/m3db/m3/src/dbnode/storage/series" 40 "github.com/m3db/m3/src/dbnode/topology" 41 "github.com/m3db/m3/src/dbnode/ts" 42 "github.com/m3db/m3/src/m3ninx/index/segment/fst" 43 "github.com/m3db/m3/src/x/checked" 44 "github.com/m3db/m3/src/x/context" 45 "github.com/m3db/m3/src/x/ident" 46 xtest "github.com/m3db/m3/src/x/test" 47 xtime "github.com/m3db/m3/src/x/time" 48 49 "github.com/golang/mock/gomock" 50 "github.com/stretchr/testify/assert" 51 "github.com/stretchr/testify/require" 52 ) 53 54 var ( 55 testNamespace = ident.StringID("testnamespace") 56 testNamespaceMetadata = func(t *testing.T, opts ...namespaceOption) namespace.Metadata { 57 namespaceOpts := namespace.NewOptions() 58 idxOpts := namespaceOpts.IndexOptions() 59 namespaceOpts = namespaceOpts.SetIndexOptions(idxOpts.SetEnabled(true)) 60 for _, opt := range opts { 61 namespaceOpts = opt(namespaceOpts) 62 } 63 ns, err := namespace.NewMetadata(testNamespace, namespaceOpts) 64 require.NoError(t, err) 65 return ns 66 } 67 testNamespaceMetadataNoIndex = func(t *testing.T, opts ...namespaceOption) namespace.Metadata { 68 newOpts := append([]namespaceOption(nil), opts...) 69 newOpts = append(newOpts, func(namespaceOpts namespace.Options) namespace.Options { 70 idxOpts := namespaceOpts.IndexOptions() 71 return namespaceOpts.SetIndexOptions(idxOpts.SetEnabled(false)) 72 }) 73 return testNamespaceMetadata(t, newOpts...) 74 } 75 76 testDefaultRunOpts = bootstrap.NewRunOptions().SetPersistConfig(bootstrap.PersistConfig{Enabled: false}) 77 testRunOptsWithPersist = bootstrap.NewRunOptions().SetPersistConfig(bootstrap.PersistConfig{Enabled: true}) 78 testBlockOpts = block.NewOptions() 79 testDefaultResultOpts = result.NewOptions().SetSeriesCachePolicy(series.CacheAll) 80 ) 81 82 type namespaceOption func(namespace.Options) namespace.Options 83 84 func newTestDefaultOpts(t *testing.T, ctrl *gomock.Controller) Options { 85 idxOpts := index.NewOptions() 86 compactor, err := compaction.NewCompactor(idxOpts.MetadataArrayPool(), 87 index.MetadataArrayPoolCapacity, 88 idxOpts.SegmentBuilderOptions(), 89 idxOpts.FSTSegmentOptions(), 90 compaction.CompactorOptions{ 91 FSTWriterOptions: &fst.WriterOptions{ 92 // DisableRegistry is set to true to trade a larger FST size 93 // for a faster FST compaction since we want to reduce the end 94 // to end latency for time to first index a metric. 95 DisableRegistry: true, 96 }, 97 }) 98 require.NoError(t, err) 99 fsOpts := fs.NewOptions() 100 101 // Allow multiple index claim managers since need to create one 102 // for each file path prefix (fs options changes between tests). 103 fs.ResetIndexClaimsManagersUnsafe() 104 105 icm, err := fs.NewIndexClaimsManager(fsOpts) 106 require.NoError(t, err) 107 return NewOptions(). 108 SetResultOptions(testDefaultResultOpts). 109 SetPersistManager(persist.NewMockManager(ctrl)). 110 SetIndexClaimsManager(icm). 111 SetAdminClient(client.NewMockAdminClient(ctrl)). 112 SetFilesystemOptions(fsOpts). 113 SetCompactor(compactor). 114 SetIndexOptions(idxOpts). 115 SetAdminClient(newValidMockClient(ctrl)). 116 SetRuntimeOptionsManager(newValidMockRuntimeOptionsManager(ctrl)) 117 } 118 119 func newValidMockClient(ctrl *gomock.Controller) *client.MockAdminClient { 120 mockAdminSession := client.NewMockAdminSession(ctrl) 121 mockClient := client.NewMockAdminClient(ctrl) 122 mockClient.EXPECT(). 123 DefaultAdminSession(). 124 Return(mockAdminSession, nil). 125 AnyTimes() 126 127 return mockClient 128 } 129 130 func newValidMockRuntimeOptionsManager(ctrl *gomock.Controller) m3dbruntime.OptionsManager { 131 mockRuntimeOpts := m3dbruntime.NewMockOptions(ctrl) 132 mockRuntimeOpts. 133 EXPECT(). 134 ClientBootstrapConsistencyLevel(). 135 Return(topology.ReadConsistencyLevelAll). 136 AnyTimes() 137 138 mockRuntimeOptsMgr := m3dbruntime.NewMockOptionsManager(ctrl) 139 mockRuntimeOptsMgr. 140 EXPECT(). 141 Get(). 142 Return(mockRuntimeOpts). 143 AnyTimes() 144 145 return mockRuntimeOptsMgr 146 } 147 148 func TestPeersSourceEmptyShardTimeRanges(t *testing.T) { 149 ctrl := xtest.NewController(t) 150 defer ctrl.Finish() 151 152 opts := newTestDefaultOpts(t, ctrl). 153 SetRuntimeOptionsManager(newValidMockRuntimeOptionsManager(ctrl)) 154 155 src, err := newPeersSource(opts) 156 require.NoError(t, err) 157 158 var ( 159 nsMetadata = testNamespaceMetadataNoIndex(t) 160 target = result.NewShardTimeRanges() 161 runOpts = testDefaultRunOpts.SetInitialTopologyState(&topology.StateSnapshot{}) 162 ) 163 cache, err := bootstrap.NewCache(bootstrap.NewCacheOptions(). 164 SetFilesystemOptions(opts.FilesystemOptions()). 165 SetInstrumentOptions(opts.FilesystemOptions().InstrumentOptions())) 166 require.NoError(t, err) 167 available, err := src.AvailableData(nsMetadata, target, cache, runOpts) 168 require.NoError(t, err) 169 require.Equal(t, target, available) 170 171 tester := bootstrap.BuildNamespacesTester(t, runOpts, target, nsMetadata) 172 defer tester.Finish() 173 tester.TestReadWith(src) 174 tester.TestUnfulfilledForNamespaceIsEmpty(nsMetadata) 175 tester.EnsureNoLoadedBlocks() 176 tester.EnsureNoWrites() 177 } 178 179 func TestPeersSourceReturnsErrorForAdminSession(t *testing.T) { 180 ctrl := xtest.NewController(t) 181 defer ctrl.Finish() 182 183 nsMetadata := testNamespaceMetadataNoIndex(t) 184 ropts := nsMetadata.Options().RetentionOptions() 185 186 expectedErr := errors.New("an error") 187 188 mockAdminClient := client.NewMockAdminClient(ctrl) 189 mockAdminClient.EXPECT().DefaultAdminSession().Return(nil, expectedErr) 190 191 opts := newTestDefaultOpts(t, ctrl).SetAdminClient(mockAdminClient) 192 src, err := newPeersSource(opts) 193 require.NoError(t, err) 194 195 start := xtime.Now().Add(-ropts.RetentionPeriod()).Truncate(ropts.BlockSize()) 196 end := start.Add(ropts.BlockSize()) 197 198 target := result.NewShardTimeRanges().Set( 199 0, 200 xtime.NewRanges(xtime.Range{Start: start, End: end}), 201 ).Set( 202 1, 203 xtime.NewRanges(xtime.Range{Start: start, End: end}), 204 ) 205 206 tester := bootstrap.BuildNamespacesTester(t, testDefaultRunOpts, target, nsMetadata) 207 defer tester.Finish() 208 209 ctx := context.NewBackground() 210 defer ctx.Close() 211 212 _, err = src.Read(ctx, tester.Namespaces, tester.Cache) 213 require.Error(t, err) 214 assert.Equal(t, expectedErr, err) 215 tester.EnsureNoLoadedBlocks() 216 tester.EnsureNoWrites() 217 } 218 219 func TestPeersSourceReturnsUnfulfilled(t *testing.T) { 220 ctrl := xtest.NewController(t) 221 defer ctrl.Finish() 222 223 opts := newTestDefaultOpts(t, ctrl) 224 nsMetadata := testNamespaceMetadataNoIndex(t) 225 ropts := nsMetadata.Options().RetentionOptions() 226 227 start := xtime.Now().Add(-ropts.RetentionPeriod()).Truncate(ropts.BlockSize()) 228 end := start.Add(ropts.BlockSize()) 229 230 goodResult := result.NewShardResult(opts.ResultOptions()) 231 fooBlock := block.NewDatabaseBlock(start, ropts.BlockSize(), ts.Segment{}, testBlockOpts, namespace.Context{}) 232 goodID := ident.StringID("foo") 233 goodResult.AddBlock(goodID, ident.NewTags(ident.StringTag("foo", "oof")), fooBlock) 234 235 mockAdminSession := client.NewMockAdminSession(ctrl) 236 mockAdminSession.EXPECT(). 237 FetchBootstrapBlocksFromPeers(namespace.NewMetadataMatcher(nsMetadata), 238 uint32(0), start, end, gomock.Any()). 239 Return(goodResult, nil) 240 241 peerMetaIter := client.NewMockPeerBlockMetadataIter(ctrl) 242 peerMetaIter.EXPECT().Next().Return(false).AnyTimes() 243 peerMetaIter.EXPECT().Err().Return(nil).AnyTimes() 244 mockAdminSession.EXPECT(). 245 FetchBootstrapBlocksMetadataFromPeers(gomock.Any(), gomock.Any(), 246 gomock.Any(), gomock.Any(), gomock.Any()). 247 Return(peerMetaIter, nil).AnyTimes() 248 249 mockAdminClient := client.NewMockAdminClient(ctrl) 250 mockAdminClient.EXPECT().DefaultAdminSession().Return(mockAdminSession, nil).AnyTimes() 251 252 opts = opts.SetAdminClient(mockAdminClient) 253 254 src, err := newPeersSource(opts) 255 require.NoError(t, err) 256 257 target := result.NewShardTimeRanges().Set( 258 0, 259 xtime.NewRanges(xtime.Range{Start: start, End: end}), 260 ) 261 262 tester := bootstrap.BuildNamespacesTester(t, testDefaultRunOpts, target, nsMetadata) 263 defer tester.Finish() 264 tester.TestReadWith(src) 265 tester.TestUnfulfilledForNamespaceIsEmpty(nsMetadata) 266 vals := tester.DumpLoadedBlocks() 267 assert.Equal(t, 1, len(vals)) 268 series, found := vals[nsMetadata.ID().String()] 269 require.True(t, found) 270 271 assert.Equal(t, 1, len(series)) 272 points, found := series[goodID.String()] 273 require.True(t, found) 274 assert.Equal(t, 0, len(points)) 275 tester.EnsureNoWrites() 276 } 277 278 func TestPeersSourceRunWithPersist(t *testing.T) { 279 for _, cachePolicy := range []series.CachePolicy{ 280 series.CacheNone, 281 series.CacheRecentlyRead, 282 series.CacheLRU, 283 } { 284 ctrl := xtest.NewController(t) 285 defer ctrl.Finish() 286 287 testNsMd := testNamespaceMetadataNoIndex(t) 288 resultOpts := testDefaultResultOpts.SetSeriesCachePolicy(cachePolicy) 289 opts := newTestDefaultOpts(t, ctrl).SetResultOptions(resultOpts) 290 ropts := testNsMd.Options().RetentionOptions() 291 testNsMd.Options() 292 blockSize := ropts.BlockSize() 293 294 start := xtime.Now().Add(-ropts.RetentionPeriod()).Truncate(ropts.BlockSize()) 295 end := start.Add(2 * ropts.BlockSize()) 296 297 shard0ResultBlock1 := result.NewShardResult(opts.ResultOptions()) 298 shard0ResultBlock2 := result.NewShardResult(opts.ResultOptions()) 299 fooBlock := block.NewDatabaseBlock(start, ropts.BlockSize(), 300 ts.NewSegment(checked.NewBytes([]byte{1, 2, 3}, nil), nil, 1, ts.FinalizeNone), 301 testBlockOpts, namespace.Context{}) 302 barBlock := block.NewDatabaseBlock(start.Add(ropts.BlockSize()), ropts.BlockSize(), 303 ts.NewSegment(checked.NewBytes([]byte{4, 5, 6}, nil), nil, 2, ts.FinalizeNone), 304 testBlockOpts, namespace.Context{}) 305 shard0ResultBlock1.AddBlock(ident.StringID("foo"), ident.NewTags(ident.StringTag("foo", "oof")), fooBlock) 306 shard0ResultBlock2.AddBlock(ident.StringID("bar"), ident.NewTags(ident.StringTag("bar", "rab")), barBlock) 307 308 shard1ResultBlock1 := result.NewShardResult(opts.ResultOptions()) 309 shard1ResultBlock2 := result.NewShardResult(opts.ResultOptions()) 310 bazBlock := block.NewDatabaseBlock(start, ropts.BlockSize(), 311 ts.NewSegment(checked.NewBytes([]byte{7, 8, 9}, nil), nil, 3, ts.FinalizeNone), 312 testBlockOpts, namespace.Context{}) 313 shard1ResultBlock1.AddBlock(ident.StringID("baz"), ident.NewTags(ident.StringTag("baz", "zab")), bazBlock) 314 315 mockAdminSession := client.NewMockAdminSession(ctrl) 316 mockAdminSession.EXPECT(). 317 FetchBootstrapBlocksFromPeers(namespace.NewMetadataMatcher(testNsMd), 318 uint32(0), start, start.Add(blockSize), gomock.Any()). 319 Return(shard0ResultBlock1, nil) 320 mockAdminSession.EXPECT(). 321 FetchBootstrapBlocksFromPeers(namespace.NewMetadataMatcher(testNsMd), 322 uint32(0), start.Add(blockSize), start.Add(blockSize*2), gomock.Any()). 323 Return(shard0ResultBlock2, nil) 324 mockAdminSession.EXPECT(). 325 FetchBootstrapBlocksFromPeers(namespace.NewMetadataMatcher(testNsMd), 326 uint32(1), start, start.Add(blockSize), gomock.Any()). 327 Return(shard1ResultBlock1, nil) 328 mockAdminSession.EXPECT(). 329 FetchBootstrapBlocksFromPeers(namespace.NewMetadataMatcher(testNsMd), 330 uint32(1), start.Add(blockSize), start.Add(blockSize*2), gomock.Any()). 331 Return(shard1ResultBlock2, nil) 332 333 peerMetaIter := client.NewMockPeerBlockMetadataIter(ctrl) 334 peerMetaIter.EXPECT().Next().Return(false).AnyTimes() 335 peerMetaIter.EXPECT().Err().Return(nil).AnyTimes() 336 mockAdminSession.EXPECT(). 337 FetchBootstrapBlocksMetadataFromPeers(gomock.Any(), gomock.Any(), 338 gomock.Any(), gomock.Any(), gomock.Any()). 339 Return(peerMetaIter, nil).AnyTimes() 340 341 mockAdminClient := client.NewMockAdminClient(ctrl) 342 mockAdminClient.EXPECT().DefaultAdminSession().Return(mockAdminSession, nil).AnyTimes() 343 344 opts = opts.SetAdminClient(mockAdminClient) 345 346 flushPreparer := persist.NewMockFlushPreparer(ctrl) 347 flushPreparer.EXPECT().DoneFlush().Times(DefaultShardPersistenceFlushConcurrency) 348 349 var ( 350 flushMut sync.Mutex 351 persists = make(map[string]int) 352 closes = make(map[string]int) 353 ) 354 355 prepareOpts := xtest.CmpMatcher(persist.DataPrepareOptions{ 356 NamespaceMetadata: testNsMd, 357 Shard: uint32(0), 358 BlockStart: start, 359 DeleteIfExists: true, 360 }) 361 flushPreparer.EXPECT(). 362 PrepareData(prepareOpts). 363 Return(persist.PreparedDataPersist{ 364 Persist: func(metadata persist.Metadata, segment ts.Segment, checksum uint32) error { 365 flushMut.Lock() 366 persists["foo"]++ 367 flushMut.Unlock() 368 assert.Equal(t, "foo", string(metadata.BytesID())) 369 assert.Equal(t, []byte{1, 2, 3}, segment.Head.Bytes()) 370 assertBlockChecksum(t, checksum, fooBlock) 371 return nil 372 }, 373 Close: func() error { 374 flushMut.Lock() 375 closes["foo"]++ 376 flushMut.Unlock() 377 return nil 378 }, 379 }, nil) 380 prepareOpts = xtest.CmpMatcher(persist.DataPrepareOptions{ 381 NamespaceMetadata: testNsMd, 382 Shard: uint32(0), 383 BlockStart: start.Add(ropts.BlockSize()), 384 DeleteIfExists: true, 385 }) 386 flushPreparer.EXPECT(). 387 PrepareData(prepareOpts). 388 Return(persist.PreparedDataPersist{ 389 Persist: func(metadata persist.Metadata, segment ts.Segment, checksum uint32) error { 390 flushMut.Lock() 391 persists["bar"]++ 392 flushMut.Unlock() 393 assert.Equal(t, "bar", string(metadata.BytesID())) 394 assert.Equal(t, []byte{4, 5, 6}, segment.Head.Bytes()) 395 assertBlockChecksum(t, checksum, barBlock) 396 return nil 397 }, 398 Close: func() error { 399 flushMut.Lock() 400 closes["bar"]++ 401 flushMut.Unlock() 402 return nil 403 }, 404 }, nil) 405 prepareOpts = xtest.CmpMatcher(persist.DataPrepareOptions{ 406 NamespaceMetadata: testNsMd, 407 Shard: uint32(1), 408 BlockStart: start, 409 DeleteIfExists: true, 410 }) 411 flushPreparer.EXPECT(). 412 PrepareData(prepareOpts). 413 Return(persist.PreparedDataPersist{ 414 Persist: func(metadata persist.Metadata, segment ts.Segment, checksum uint32) error { 415 flushMut.Lock() 416 persists["baz"]++ 417 flushMut.Unlock() 418 assert.Equal(t, "baz", string(metadata.BytesID())) 419 assert.Equal(t, []byte{7, 8, 9}, segment.Head.Bytes()) 420 assertBlockChecksum(t, checksum, bazBlock) 421 return nil 422 }, 423 Close: func() error { 424 flushMut.Lock() 425 closes["baz"]++ 426 flushMut.Unlock() 427 return nil 428 }, 429 }, nil) 430 prepareOpts = xtest.CmpMatcher(persist.DataPrepareOptions{ 431 NamespaceMetadata: testNsMd, 432 Shard: uint32(1), 433 BlockStart: start.Add(ropts.BlockSize()), 434 DeleteIfExists: true, 435 }) 436 flushPreparer.EXPECT(). 437 PrepareData(prepareOpts). 438 Return(persist.PreparedDataPersist{ 439 Persist: func(metadata persist.Metadata, segment ts.Segment, checksum uint32) error { 440 assert.Fail(t, "no expected shard 1 second block") 441 return nil 442 }, 443 Close: func() error { 444 flushMut.Lock() 445 closes["empty"]++ 446 flushMut.Unlock() 447 return nil 448 }, 449 }, nil) 450 451 mockPersistManager := persist.NewMockManager(ctrl) 452 mockPersistManager.EXPECT().StartFlushPersist().Return(flushPreparer, nil). 453 Times(DefaultShardPersistenceFlushConcurrency) 454 455 src, err := newPeersSource(opts) 456 require.NoError(t, err) 457 458 src.(*peersSource).newPersistManager = func() (persist.Manager, error) { 459 return mockPersistManager, nil 460 } 461 462 target := result.NewShardTimeRanges().Set( 463 0, 464 xtime.NewRanges(xtime.Range{Start: start, End: end}), 465 ).Set( 466 1, 467 xtime.NewRanges(xtime.Range{Start: start, End: end}), 468 ) 469 470 tester := bootstrap.BuildNamespacesTester(t, testRunOptsWithPersist, target, testNsMd) 471 defer tester.Finish() 472 tester.TestReadWith(src) 473 tester.TestUnfulfilledForNamespaceIsEmpty(testNsMd) 474 assert.Equal(t, map[string]int{ 475 "foo": 1, "bar": 1, "baz": 1, 476 }, persists) 477 478 assert.Equal(t, map[string]int{ 479 "foo": 1, "bar": 1, "baz": 1, "empty": 1, 480 }, closes) 481 482 tester.EnsureNoLoadedBlocks() 483 tester.EnsureNoWrites() 484 } 485 } 486 487 func TestPeersSourceMarksUnfulfilledOnPersistenceErrors(t *testing.T) { 488 ctrl := xtest.NewController(t) 489 defer ctrl.Finish() 490 491 opts := newTestDefaultOpts(t, ctrl). 492 SetResultOptions(newTestDefaultOpts(t, ctrl). 493 ResultOptions(). 494 SetSeriesCachePolicy(series.CacheRecentlyRead), 495 ) 496 testNsMd := testNamespaceMetadataNoIndex(t) 497 ropts := testNsMd.Options().RetentionOptions() 498 499 start := xtime.Now().Add(-ropts.RetentionPeriod()).Truncate(ropts.BlockSize()) 500 midway := start.Add(ropts.BlockSize()) 501 end := start.Add(2 * ropts.BlockSize()) 502 503 type resultsKey struct { 504 shard uint32 505 start xtime.UnixNano 506 end xtime.UnixNano 507 expectedErr bool 508 } 509 510 results := make(map[resultsKey]result.ShardResult) 511 addResult := func(shard uint32, id string, b block.DatabaseBlock, expectedErr bool) { 512 r := result.NewShardResult(opts.ResultOptions()) 513 r.AddBlock(ident.StringID(id), ident.NewTags(ident.StringTag(id, id)), b) 514 start := b.StartTime() 515 end := start.Add(ropts.BlockSize()) 516 results[resultsKey{shard, start, end, expectedErr}] = r 517 } 518 519 segmentError := errors.New("segment err") 520 521 // foo results 522 var fooBlocks [2]block.DatabaseBlock 523 fooBlocks[0] = block.NewMockDatabaseBlock(ctrl) 524 fooBlocks[0].(*block.MockDatabaseBlock).EXPECT().StartTime().Return(start).AnyTimes() 525 fooBlocks[0].(*block.MockDatabaseBlock).EXPECT().Checksum().Return(uint32(0), errors.New("stream err")) 526 addResult(0, "foo", fooBlocks[0], true) 527 528 fooBlocks[1] = block.NewDatabaseBlock(midway, ropts.BlockSize(), 529 ts.NewSegment(checked.NewBytes([]byte{1, 2, 3}, nil), nil, 1, ts.FinalizeNone), 530 testBlockOpts, namespace.Context{}) 531 addResult(0, "foo", fooBlocks[1], false) 532 533 // bar results 534 var barBlocks [2]block.DatabaseBlock 535 barBlocks[0] = block.NewMockDatabaseBlock(ctrl) 536 barBlocks[0].(*block.MockDatabaseBlock).EXPECT().StartTime().Return(start).AnyTimes() 537 barBlocks[0].(*block.MockDatabaseBlock).EXPECT().Checksum().Return(uint32(0), errors.New("stream err")) 538 addResult(1, "bar", barBlocks[0], false) 539 540 barBlocks[1] = block.NewDatabaseBlock(midway, ropts.BlockSize(), 541 ts.NewSegment(checked.NewBytes([]byte{4, 5, 6}, nil), nil, 2, ts.FinalizeNone), 542 testBlockOpts, namespace.Context{}) 543 addResult(1, "bar", barBlocks[1], false) 544 545 // baz results 546 var bazBlocks [2]block.DatabaseBlock 547 bazBlocks[0] = block.NewDatabaseBlock(start, ropts.BlockSize(), 548 ts.NewSegment(checked.NewBytes([]byte{7, 8, 9}, nil), nil, 3, ts.FinalizeNone), 549 testBlockOpts, namespace.Context{}) 550 addResult(2, "baz", bazBlocks[0], false) 551 552 bazBlocks[1] = block.NewDatabaseBlock(midway, ropts.BlockSize(), 553 ts.NewSegment(checked.NewBytes([]byte{10, 11, 12}, nil), nil, 4, ts.FinalizeNone), 554 testBlockOpts, namespace.Context{}) 555 addResult(2, "baz", bazBlocks[1], false) 556 557 // qux results 558 var quxBlocks [2]block.DatabaseBlock 559 quxBlocks[0] = block.NewDatabaseBlock(start, ropts.BlockSize(), 560 ts.NewSegment(checked.NewBytes([]byte{13, 14, 15}, nil), nil, 5, ts.FinalizeNone), 561 testBlockOpts, namespace.Context{}) 562 addResult(3, "qux", quxBlocks[0], false) 563 564 quxBlocks[1] = block.NewDatabaseBlock(midway, ropts.BlockSize(), 565 ts.NewSegment(checked.NewBytes([]byte{16, 17, 18}, nil), nil, 6, ts.FinalizeNone), 566 testBlockOpts, namespace.Context{}) 567 addResult(3, "qux", quxBlocks[1], false) 568 569 mockAdminSession := client.NewMockAdminSession(ctrl) 570 571 for key, result := range results { 572 mockAdminSession.EXPECT(). 573 FetchBootstrapBlocksFromPeers(namespace.NewMetadataMatcher(testNsMd), 574 key.shard, key.start, key.end, 575 gomock.Any()). 576 Return(result, nil) 577 578 peerError := segmentError 579 if !key.expectedErr { 580 peerError = nil 581 } 582 583 peerMetaIter := client.NewMockPeerBlockMetadataIter(ctrl) 584 peerMetaIter.EXPECT().Next().Return(false).AnyTimes() 585 peerMetaIter.EXPECT().Err().Return(nil).AnyTimes() 586 mockAdminSession.EXPECT(). 587 FetchBootstrapBlocksMetadataFromPeers(testNsMd.ID(), 588 key.shard, key.start, key.end, gomock.Any()). 589 Return(peerMetaIter, peerError).AnyTimes() 590 } 591 592 mockAdminClient := client.NewMockAdminClient(ctrl) 593 mockAdminClient.EXPECT().DefaultAdminSession(). 594 Return(mockAdminSession, nil).AnyTimes() 595 596 opts = opts.SetAdminClient(mockAdminClient) 597 598 flushPreparer := persist.NewMockFlushPreparer(ctrl) 599 flushPreparer.EXPECT().DoneFlush().Times(DefaultShardPersistenceFlushConcurrency) 600 601 var ( 602 flushMut sync.Mutex 603 persists = make(map[string]int) 604 closes = make(map[string]int) 605 ) 606 607 // expect foo 608 prepareOpts := xtest.CmpMatcher(persist.DataPrepareOptions{ 609 NamespaceMetadata: testNsMd, 610 Shard: uint32(0), 611 BlockStart: start, 612 DeleteIfExists: true, 613 }) 614 flushPreparer.EXPECT(). 615 PrepareData(prepareOpts). 616 Return(persist.PreparedDataPersist{ 617 Persist: func(metadata persist.Metadata, segment ts.Segment, checksum uint32) error { 618 assert.Fail(t, "not expecting to flush shard 0 at start") 619 return nil 620 }, 621 Close: func() error { 622 flushMut.Lock() 623 closes["foo"]++ 624 flushMut.Unlock() 625 return nil 626 }, 627 }, nil) 628 prepareOpts = xtest.CmpMatcher(persist.DataPrepareOptions{ 629 NamespaceMetadata: testNsMd, 630 Shard: uint32(0), 631 BlockStart: midway, 632 DeleteIfExists: true, 633 }) 634 flushPreparer.EXPECT(). 635 PrepareData(prepareOpts). 636 Return(persist.PreparedDataPersist{ 637 Persist: func(metadata persist.Metadata, segment ts.Segment, checksum uint32) error { 638 flushMut.Lock() 639 persists["foo"]++ 640 flushMut.Unlock() 641 return nil 642 }, 643 Close: func() error { 644 flushMut.Lock() 645 closes["foo"]++ 646 flushMut.Unlock() 647 return nil 648 }, 649 }, nil) 650 651 // expect bar 652 prepareOpts = xtest.CmpMatcher(persist.DataPrepareOptions{ 653 NamespaceMetadata: testNsMd, 654 Shard: uint32(1), 655 BlockStart: start, 656 DeleteIfExists: true, 657 }) 658 flushPreparer.EXPECT(). 659 PrepareData(prepareOpts). 660 Return(persist.PreparedDataPersist{ 661 Persist: func(metadata persist.Metadata, segment ts.Segment, checksum uint32) error { 662 assert.Fail(t, "not expecting to flush shard 0 at start + block size") 663 return nil 664 }, 665 Close: func() error { 666 flushMut.Lock() 667 closes["bar"]++ 668 flushMut.Unlock() 669 return nil 670 }, 671 }, nil) 672 prepareOpts = xtest.CmpMatcher(persist.DataPrepareOptions{ 673 NamespaceMetadata: testNsMd, 674 Shard: uint32(1), 675 BlockStart: midway, 676 DeleteIfExists: true, 677 }) 678 flushPreparer.EXPECT(). 679 PrepareData(prepareOpts). 680 Return(persist.PreparedDataPersist{ 681 Persist: func(metadata persist.Metadata, segment ts.Segment, checksum uint32) error { 682 flushMut.Lock() 683 persists["bar"]++ 684 flushMut.Unlock() 685 return nil 686 }, 687 Close: func() error { 688 flushMut.Lock() 689 closes["bar"]++ 690 flushMut.Unlock() 691 return nil 692 }, 693 }, nil) 694 695 // expect baz 696 prepareOpts = xtest.CmpMatcher(persist.DataPrepareOptions{ 697 NamespaceMetadata: testNsMd, 698 Shard: uint32(2), 699 BlockStart: start, 700 DeleteIfExists: true, 701 }) 702 flushPreparer.EXPECT(). 703 PrepareData(prepareOpts). 704 Return(persist.PreparedDataPersist{ 705 Persist: func(metadata persist.Metadata, segment ts.Segment, checksum uint32) error { 706 flushMut.Lock() 707 persists["baz"]++ 708 flushMut.Unlock() 709 return fmt.Errorf("a persist error") 710 }, 711 Close: func() error { 712 flushMut.Lock() 713 closes["baz"]++ 714 flushMut.Unlock() 715 return nil 716 }, 717 }, nil) 718 prepareOpts = xtest.CmpMatcher(persist.DataPrepareOptions{ 719 NamespaceMetadata: testNsMd, 720 Shard: uint32(2), 721 BlockStart: midway, 722 DeleteIfExists: true, 723 }) 724 flushPreparer.EXPECT(). 725 PrepareData(prepareOpts). 726 Return(persist.PreparedDataPersist{ 727 Persist: func(metadata persist.Metadata, segment ts.Segment, checksum uint32) error { 728 flushMut.Lock() 729 persists["baz"]++ 730 flushMut.Unlock() 731 return nil 732 }, 733 Close: func() error { 734 flushMut.Lock() 735 closes["baz"]++ 736 flushMut.Unlock() 737 return nil 738 }, 739 }, nil) 740 741 // expect qux 742 prepareOpts = xtest.CmpMatcher(persist.DataPrepareOptions{ 743 NamespaceMetadata: testNsMd, 744 Shard: uint32(3), 745 BlockStart: start, 746 DeleteIfExists: true, 747 }) 748 flushPreparer.EXPECT(). 749 PrepareData(prepareOpts). 750 Return(persist.PreparedDataPersist{ 751 Persist: func(metadata persist.Metadata, segment ts.Segment, checksum uint32) error { 752 flushMut.Lock() 753 persists["qux"]++ 754 flushMut.Unlock() 755 return nil 756 }, 757 Close: func() error { 758 flushMut.Lock() 759 closes["qux"]++ 760 flushMut.Unlock() 761 return fmt.Errorf("a persist close error") 762 }, 763 }, nil) 764 prepareOpts = xtest.CmpMatcher(persist.DataPrepareOptions{ 765 NamespaceMetadata: testNsMd, 766 Shard: uint32(3), 767 BlockStart: midway, 768 DeleteIfExists: true, 769 }) 770 flushPreparer.EXPECT(). 771 PrepareData(prepareOpts). 772 Return(persist.PreparedDataPersist{ 773 Persist: func(metadata persist.Metadata, segment ts.Segment, checksum uint32) error { 774 flushMut.Lock() 775 persists["qux"]++ 776 flushMut.Unlock() 777 return nil 778 }, 779 Close: func() error { 780 flushMut.Lock() 781 closes["qux"]++ 782 flushMut.Unlock() 783 return nil 784 }, 785 }, nil) 786 787 mockPersistManager := persist.NewMockManager(ctrl) 788 mockPersistManager.EXPECT().StartFlushPersist().Return(flushPreparer, nil). 789 Times(DefaultShardPersistenceFlushConcurrency) 790 791 src, err := newPeersSource(opts) 792 require.NoError(t, err) 793 794 src.(*peersSource).newPersistManager = func() (persist.Manager, error) { 795 return mockPersistManager, nil 796 } 797 798 target := result.NewShardTimeRanges().Set( 799 0, 800 xtime.NewRanges( 801 xtime.Range{Start: start, End: midway}, 802 xtime.Range{Start: midway, End: end}), 803 ).Set( 804 1, 805 xtime.NewRanges( 806 xtime.Range{Start: start, End: midway}, 807 xtime.Range{Start: midway, End: end}), 808 ).Set( 809 2, 810 xtime.NewRanges( 811 xtime.Range{Start: start, End: midway}, 812 xtime.Range{Start: midway, End: end}), 813 ).Set( 814 3, 815 xtime.NewRanges( 816 xtime.Range{Start: start, End: midway}, 817 xtime.Range{Start: midway, End: end}), 818 ) 819 820 tester := bootstrap.BuildNamespacesTester(t, testRunOptsWithPersist, target, testNsMd) 821 defer tester.Finish() 822 tester.TestReadWith(src) 823 824 expectedRanges := result.NewShardTimeRanges().Set( 825 0, 826 xtime.NewRanges(xtime.Range{Start: start, End: midway}), 827 ).Set( 828 1, 829 xtime.NewRanges(xtime.Range{Start: start, End: midway}), 830 ).Set( 831 2, 832 xtime.NewRanges(xtime.Range{Start: start, End: midway}), 833 ).Set( 834 3, 835 xtime.NewRanges(xtime.Range{Start: start, End: midway}), 836 ) 837 838 // NB(bodu): There is no time series data written to disk so all ranges fail to be fulfilled. 839 expectedIndexRanges := target 840 841 tester.TestUnfulfilledForNamespace(testNsMd, expectedRanges, expectedIndexRanges) 842 assert.Equal(t, map[string]int{ 843 "foo": 1, "bar": 1, "baz": 2, "qux": 2, 844 }, persists) 845 846 assert.Equal(t, map[string]int{ 847 "foo": 2, "bar": 2, "baz": 2, "qux": 2, 848 }, closes) 849 850 tester.EnsureNoLoadedBlocks() 851 tester.EnsureNoWrites() 852 } 853 854 func assertBlockChecksum(t *testing.T, expectedChecksum uint32, block block.DatabaseBlock) { 855 checksum, err := block.Checksum() 856 require.NoError(t, err) 857 require.Equal(t, expectedChecksum, checksum) 858 }