github.com/m3db/m3@v1.5.1-0.20231129193456-75a402aa583b/src/dbnode/persist/fs/persist_manager_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 fs 22 23 import ( 24 "errors" 25 "io" 26 "io/ioutil" 27 "os" 28 "testing" 29 "time" 30 31 "github.com/m3db/m3/src/dbnode/digest" 32 "github.com/m3db/m3/src/dbnode/persist" 33 "github.com/m3db/m3/src/dbnode/ts" 34 "github.com/m3db/m3/src/m3ninx/index/segment" 35 m3ninxfs "github.com/m3db/m3/src/m3ninx/index/segment/fst" 36 m3ninxpersist "github.com/m3db/m3/src/m3ninx/persist" 37 "github.com/m3db/m3/src/x/checked" 38 "github.com/m3db/m3/src/x/ident" 39 "github.com/m3db/m3/src/x/instrument" 40 m3test "github.com/m3db/m3/src/x/test" 41 xtest "github.com/m3db/m3/src/x/test" 42 xtime "github.com/m3db/m3/src/x/time" 43 44 "github.com/golang/mock/gomock" 45 "github.com/stretchr/testify/assert" 46 "github.com/stretchr/testify/require" 47 ) 48 49 func TestPersistenceManagerPrepareDataFileExistsNoDelete(t *testing.T) { 50 ctrl := gomock.NewController(t) 51 defer ctrl.Finish() 52 53 pm, _, _, _ := testDataPersistManager(t, ctrl) 54 defer os.RemoveAll(pm.filePathPrefix) 55 56 var ( 57 shard = uint32(0) 58 blockStart = xtime.FromSeconds(1000) 59 shardDir = createDataShardDir(t, pm.filePathPrefix, testNs1ID, shard) 60 checkpointFilePath = filesetPathFromTimeLegacy(shardDir, blockStart, CheckpointFileSuffix) 61 checkpointFileBuf = make([]byte, CheckpointFileSizeBytes) 62 ) 63 createFile(t, checkpointFilePath, checkpointFileBuf) 64 65 flush, err := pm.StartFlushPersist() 66 require.NoError(t, err) 67 68 defer func() { 69 assert.NoError(t, flush.DoneFlush()) 70 }() 71 72 prepareOpts := persist.DataPrepareOptions{ 73 NamespaceMetadata: testNs1Metadata(t), 74 Shard: shard, 75 BlockStart: blockStart, 76 } 77 78 defer instrument.SetShouldPanicEnvironmentVariable(true)() 79 require.Panics(t, func() { _, _ = flush.PrepareData(prepareOpts) }) 80 } 81 82 func TestPersistenceManagerPrepareDataFileExistsWithDelete(t *testing.T) { 83 ctrl := gomock.NewController(t) 84 defer ctrl.Finish() 85 86 pm, writer, _, _ := testDataPersistManager(t, ctrl) 87 defer os.RemoveAll(pm.filePathPrefix) 88 89 var ( 90 shard = uint32(0) 91 blockStart = xtime.FromSeconds(1000) 92 ) 93 94 writerOpts := xtest.CmpMatcher(DataWriterOpenOptions{ 95 Identifier: FileSetFileIdentifier{ 96 Namespace: testNs1ID, 97 Shard: shard, 98 BlockStart: blockStart, 99 }, 100 BlockSize: testBlockSize, 101 }, m3test.IdentTransformer) 102 writer.EXPECT().Open(writerOpts).Return(nil) 103 104 var ( 105 shardDir = createDataShardDir(t, pm.filePathPrefix, testNs1ID, shard) 106 checkpointFilePath = filesetPathFromTimeLegacy(shardDir, blockStart, CheckpointFileSuffix) 107 checkpointFileBuf = make([]byte, CheckpointFileSizeBytes) 108 ) 109 createFile(t, checkpointFilePath, checkpointFileBuf) 110 111 flush, err := pm.StartFlushPersist() 112 require.NoError(t, err) 113 114 defer func() { 115 assert.NoError(t, flush.DoneFlush()) 116 }() 117 118 prepareOpts := persist.DataPrepareOptions{ 119 NamespaceMetadata: testNs1Metadata(t), 120 Shard: shard, 121 BlockStart: blockStart, 122 DeleteIfExists: true, 123 } 124 prepared, err := flush.PrepareData(prepareOpts) 125 require.NoError(t, err) 126 require.NotNil(t, prepared.Persist) 127 require.NotNil(t, prepared.Close) 128 129 _, err = os.Open(checkpointFilePath) 130 require.True(t, os.IsNotExist(err)) 131 } 132 133 func TestPersistenceManagerPrepareOpenError(t *testing.T) { 134 ctrl := gomock.NewController(t) 135 defer ctrl.Finish() 136 137 pm, writer, _, _ := testDataPersistManager(t, ctrl) 138 defer os.RemoveAll(pm.filePathPrefix) 139 140 ns1Md := testNs1Metadata(t) 141 shard := uint32(0) 142 blockStart := xtime.FromSeconds(1000) 143 expectedErr := errors.New("foo") 144 145 writerOpts := xtest.CmpMatcher(DataWriterOpenOptions{ 146 Identifier: FileSetFileIdentifier{ 147 Namespace: testNs1ID, 148 Shard: shard, 149 BlockStart: blockStart, 150 }, 151 BlockSize: testBlockSize, 152 }, m3test.IdentTransformer) 153 writer.EXPECT().Open(writerOpts).Return(expectedErr) 154 155 flush, err := pm.StartFlushPersist() 156 require.NoError(t, err) 157 158 defer func() { 159 assert.NoError(t, flush.DoneFlush()) 160 }() 161 162 prepareOpts := persist.DataPrepareOptions{ 163 NamespaceMetadata: ns1Md, 164 Shard: shard, 165 BlockStart: blockStart, 166 } 167 prepared, err := flush.PrepareData(prepareOpts) 168 require.Equal(t, expectedErr, err) 169 require.Nil(t, prepared.Persist) 170 require.Nil(t, prepared.Close) 171 } 172 173 func TestPersistenceManagerPrepareSuccess(t *testing.T) { 174 ctrl := gomock.NewController(t) 175 defer ctrl.Finish() 176 177 pm, writer, _, _ := testDataPersistManager(t, ctrl) 178 defer os.RemoveAll(pm.filePathPrefix) 179 180 shard := uint32(0) 181 blockStart := xtime.FromSeconds(1000) 182 writerOpts := xtest.CmpMatcher(DataWriterOpenOptions{ 183 Identifier: FileSetFileIdentifier{ 184 Namespace: testNs1ID, 185 Shard: shard, 186 BlockStart: blockStart, 187 }, 188 BlockSize: testBlockSize, 189 }, m3test.IdentTransformer) 190 writer.EXPECT().Open(writerOpts).Return(nil) 191 192 var ( 193 id = ident.StringID("foo") 194 tags = ident.NewTags(ident.StringTag("bar", "baz")) 195 head = checked.NewBytes([]byte{0x1, 0x2}, nil) 196 tail = checked.NewBytes([]byte{0x3, 0x4}, nil) 197 segment = ts.NewSegment(head, tail, 0, ts.FinalizeNone) 198 checksum = segment.CalculateChecksum() 199 ) 200 metadata := persist.NewMetadataFromIDAndTags(id, tags, 201 persist.MetadataOptions{}) 202 writer.EXPECT().WriteAll(metadata, gomock.Any(), checksum).Return(nil) 203 writer.EXPECT().Close() 204 205 flush, err := pm.StartFlushPersist() 206 require.NoError(t, err) 207 208 defer func() { 209 assert.NoError(t, flush.DoneFlush()) 210 }() 211 212 now := time.Now() 213 pm.start = now 214 pm.count = 123 215 pm.bytesWritten = 100 216 217 prepareOpts := persist.DataPrepareOptions{ 218 NamespaceMetadata: testNs1Metadata(t), 219 Shard: shard, 220 BlockStart: blockStart, 221 } 222 prepared, err := flush.PrepareData(prepareOpts) 223 defer prepared.Close() 224 225 require.Nil(t, err) 226 227 require.Nil(t, prepared.Persist(metadata, segment, checksum)) 228 229 require.True(t, pm.start.Equal(now)) 230 require.Equal(t, 124, pm.count) 231 require.Equal(t, int64(104), pm.bytesWritten) 232 } 233 234 func TestPersistenceManagerPrepareSnapshotSuccess(t *testing.T) { 235 ctrl := gomock.NewController(t) 236 defer ctrl.Finish() 237 238 pm, writer, snapshotMetadataWriter, _ := testDataPersistManager(t, ctrl) 239 defer os.RemoveAll(pm.filePathPrefix) 240 241 shard := uint32(0) 242 blockStart := xtime.FromSeconds(1000) 243 writerOpts := xtest.CmpMatcher(DataWriterOpenOptions{ 244 Identifier: FileSetFileIdentifier{ 245 Namespace: testNs1ID, 246 Shard: shard, 247 BlockStart: blockStart, 248 }, 249 BlockSize: testBlockSize, 250 Snapshot: DataWriterSnapshotOptions{ 251 SnapshotID: testSnapshotID, 252 }, 253 }, m3test.IdentTransformer) 254 writer.EXPECT().Open(writerOpts).Return(nil) 255 256 snapshotMetadataWriter.EXPECT().Write(SnapshotMetadataWriteArgs{ 257 ID: SnapshotMetadataIdentifier{ 258 Index: 0, 259 UUID: nil, 260 }, 261 CommitlogIdentifier: persist.CommitLogFile{}, 262 }).Return(nil) 263 264 var ( 265 id = ident.StringID("foo") 266 tags = ident.NewTags(ident.StringTag("bar", "baz")) 267 head = checked.NewBytes([]byte{0x1, 0x2}, nil) 268 tail = checked.NewBytes([]byte{0x3, 0x4}, nil) 269 segment = ts.NewSegment(head, tail, 0, ts.FinalizeNone) 270 checksum = segment.CalculateChecksum() 271 ) 272 metadata := persist.NewMetadataFromIDAndTags(id, tags, 273 persist.MetadataOptions{}) 274 writer.EXPECT().WriteAll(metadata, gomock.Any(), checksum).Return(nil) 275 writer.EXPECT().Close() 276 277 flush, err := pm.StartSnapshotPersist(testSnapshotID) 278 require.NoError(t, err) 279 280 defer func() { 281 assert.NoError(t, flush.DoneSnapshot(nil, persist.CommitLogFile{})) 282 }() 283 284 now := time.Now() 285 pm.start = now 286 pm.count = 123 287 pm.bytesWritten = 100 288 289 prepareOpts := persist.DataPrepareOptions{ 290 NamespaceMetadata: testNs1Metadata(t), 291 Shard: shard, 292 BlockStart: blockStart, 293 } 294 prepared, err := flush.PrepareData(prepareOpts) 295 defer prepared.Close() 296 297 require.Nil(t, err) 298 299 require.Nil(t, prepared.Persist(metadata, segment, checksum)) 300 301 require.True(t, pm.start.Equal(now)) 302 require.Equal(t, 124, pm.count) 303 require.Equal(t, int64(104), pm.bytesWritten) 304 } 305 306 func TestPersistenceManagerCloseData(t *testing.T) { 307 ctrl := gomock.NewController(t) 308 defer ctrl.Finish() 309 310 pm, writer, _, _ := testDataPersistManager(t, ctrl) 311 defer os.RemoveAll(pm.filePathPrefix) 312 313 writer.EXPECT().Close() 314 pm.closeData() 315 } 316 317 func TestPersistenceManagerPrepareIndexFileExists(t *testing.T) { 318 ctrl := gomock.NewController(xtest.Reporter{T: t}) 319 defer ctrl.Finish() 320 321 pm, writer, segWriter, _ := testIndexPersistManager(t, ctrl) 322 defer os.RemoveAll(pm.filePathPrefix) 323 324 blockStart := xtime.FromSeconds(1000) 325 indexDir := createIndexDataDir(t, pm.filePathPrefix, testNs1ID) 326 checkpointFilePath := FilesetPathFromTimeAndIndex(indexDir, blockStart, 0, CheckpointFileSuffix) 327 328 digestBuf := digest.NewBuffer() 329 digestBuf.WriteDigest(digest.Checksum([]byte("foo"))) 330 331 err := ioutil.WriteFile(checkpointFilePath, digestBuf, defaultNewFileMode) 332 require.NoError(t, err) 333 334 flush, err := pm.StartIndexPersist() 335 require.NoError(t, err) 336 337 defer func() { 338 segWriter.EXPECT().Reset(nil) 339 assert.NoError(t, flush.DoneIndex()) 340 }() 341 volumeIndex, err := NextIndexFileSetVolumeIndex( 342 pm.filePathPrefix, 343 testNs1ID, 344 blockStart, 345 ) 346 require.NoError(t, err) 347 348 prepareOpts := persist.IndexPrepareOptions{ 349 NamespaceMetadata: testNs1Metadata(t), 350 BlockStart: blockStart, 351 VolumeIndex: volumeIndex, 352 } 353 writer.EXPECT().Open(xtest.CmpMatcher( 354 IndexWriterOpenOptions{ 355 BlockSize: testBlockSize, 356 Identifier: FileSetFileIdentifier{ 357 FileSetContentType: persist.FileSetIndexContentType, 358 BlockStart: blockStart, 359 Namespace: testNs1ID, 360 VolumeIndex: 1, 361 }, 362 }, m3test.IdentTransformer), 363 ).Return(nil) 364 prepared, err := flush.PrepareIndex(prepareOpts) 365 require.NoError(t, err) 366 require.NotNil(t, prepared.Persist) 367 require.NotNil(t, prepared.Close) 368 } 369 370 func TestPersistenceManagerPrepareIndexOpenError(t *testing.T) { 371 ctrl := gomock.NewController(t) 372 defer ctrl.Finish() 373 374 pm, writer, segWriter, _ := testIndexPersistManager(t, ctrl) 375 defer os.RemoveAll(pm.filePathPrefix) 376 377 ns1Md := testNs1Metadata(t) 378 blockStart := xtime.FromSeconds(1000) 379 expectedErr := errors.New("foo") 380 381 writerOpts := xtest.CmpMatcher(IndexWriterOpenOptions{ 382 Identifier: FileSetFileIdentifier{ 383 FileSetContentType: persist.FileSetIndexContentType, 384 Namespace: testNs1ID, 385 BlockStart: blockStart, 386 }, 387 BlockSize: testBlockSize, 388 }, m3test.IdentTransformer) 389 writer.EXPECT().Open(writerOpts).Return(expectedErr) 390 391 flush, err := pm.StartIndexPersist() 392 require.NoError(t, err) 393 394 defer func() { 395 segWriter.EXPECT().Reset(nil) 396 assert.NoError(t, flush.DoneIndex()) 397 }() 398 399 prepareOpts := persist.IndexPrepareOptions{ 400 NamespaceMetadata: ns1Md, 401 BlockStart: blockStart, 402 } 403 prepared, err := flush.PrepareIndex(prepareOpts) 404 require.Equal(t, expectedErr, err) 405 require.Nil(t, prepared.Persist) 406 require.Nil(t, prepared.Close) 407 } 408 409 func TestPersistenceManagerPrepareIndexSuccess(t *testing.T) { 410 ctrl := gomock.NewController(xtest.Reporter{T: t}) 411 defer ctrl.Finish() 412 413 pm, writer, segWriter, _ := testIndexPersistManager(t, ctrl) 414 defer os.RemoveAll(pm.filePathPrefix) 415 416 flush, err := pm.StartIndexPersist() 417 require.NoError(t, err) 418 419 defer func() { 420 segWriter.EXPECT().Reset(nil) 421 assert.NoError(t, flush.DoneIndex()) 422 }() 423 424 // We support preparing multiple index block writers for an index persist. 425 numBlocks := 10 426 blockStart := xtime.FromSeconds(1000) 427 for i := 1; i < numBlocks; i++ { 428 blockStart = blockStart.Add(time.Duration(i) * testBlockSize) 429 writerOpts := IndexWriterOpenOptions{ 430 Identifier: FileSetFileIdentifier{ 431 FileSetContentType: persist.FileSetIndexContentType, 432 Namespace: testNs1ID, 433 BlockStart: blockStart, 434 }, 435 BlockSize: testBlockSize, 436 } 437 writer.EXPECT().Open(xtest.CmpMatcher(writerOpts, m3test.IdentTransformer)).Return(nil) 438 439 prepareOpts := persist.IndexPrepareOptions{ 440 NamespaceMetadata: testNs1Metadata(t), 441 BlockStart: blockStart, 442 } 443 prepared, err := flush.PrepareIndex(prepareOpts) 444 require.NoError(t, err) 445 446 seg := segment.NewMockMutableSegment(ctrl) 447 segWriter.EXPECT().Reset(seg).Return(nil) 448 writer.EXPECT().WriteSegmentFileSet(segWriter).Return(nil) 449 require.NoError(t, prepared.Persist(seg)) 450 451 reader := NewMockIndexFileSetReader(ctrl) 452 pm.indexPM.newReaderFn = func(Options) (IndexFileSetReader, error) { 453 return reader, nil 454 } 455 456 reader.EXPECT().Open(xtest.CmpMatcher(IndexReaderOpenOptions{ 457 Identifier: writerOpts.Identifier, 458 }, m3test.IdentTransformer)).Return(IndexReaderOpenResult{}, nil) 459 460 file := NewMockIndexSegmentFile(ctrl) 461 gomock.InOrder( 462 reader.EXPECT().SegmentFileSets().Return(1), 463 reader.EXPECT().ReadSegmentFileSet().Return(file, nil), 464 reader.EXPECT().ReadSegmentFileSet().Return(nil, io.EOF), 465 ) 466 fsSeg := m3ninxfs.NewMockSegment(ctrl) 467 pm.indexPM.newPersistentSegmentFn = func( 468 fset m3ninxpersist.IndexSegmentFileSet, opts m3ninxfs.Options, 469 ) (m3ninxfs.Segment, error) { 470 require.Equal(t, file, fset) 471 return fsSeg, nil 472 } 473 474 writer.EXPECT().Close().Return(nil) 475 segs, err := prepared.Close() 476 require.NoError(t, err) 477 require.Len(t, segs, 1) 478 require.Equal(t, fsSeg, segs[0]) 479 } 480 } 481 482 func TestPersistenceManagerNoRateLimit(t *testing.T) { 483 ctrl := gomock.NewController(t) 484 defer ctrl.Finish() 485 486 pm, writer, _, _ := testDataPersistManager(t, ctrl) 487 defer os.RemoveAll(pm.filePathPrefix) 488 489 shard := uint32(0) 490 blockStart := xtime.FromSeconds(1000) 491 writerOpts := xtest.CmpMatcher(DataWriterOpenOptions{ 492 Identifier: FileSetFileIdentifier{ 493 Namespace: testNs1ID, 494 Shard: shard, 495 BlockStart: blockStart, 496 }, 497 BlockSize: testBlockSize, 498 }, m3test.IdentTransformer) 499 writer.EXPECT().Open(writerOpts).Return(nil) 500 501 var ( 502 now time.Time 503 slept time.Duration 504 id = ident.StringID("foo") 505 tags = ident.NewTags(ident.StringTag("bar", "baz")) 506 head = checked.NewBytes([]byte{0x1, 0x2}, nil) 507 tail = checked.NewBytes([]byte{0x3}, nil) 508 segment = ts.NewSegment(head, tail, 0, ts.FinalizeNone) 509 checksum = segment.CalculateChecksum() 510 ) 511 512 pm.nowFn = func() time.Time { return now } 513 pm.sleepFn = func(d time.Duration) { slept += d } 514 515 metadata := persist.NewMetadataFromIDAndTags(id, tags, 516 persist.MetadataOptions{}) 517 writer.EXPECT(). 518 WriteAll(metadata, pm.dataPM.segmentHolder, checksum). 519 Return(nil). 520 Times(2) 521 522 flush, err := pm.StartFlushPersist() 523 require.NoError(t, err) 524 525 defer func() { 526 assert.NoError(t, flush.DoneFlush()) 527 }() 528 529 // prepare the flush 530 prepareOpts := persist.DataPrepareOptions{ 531 NamespaceMetadata: testNs1Metadata(t), 532 Shard: shard, 533 BlockStart: blockStart, 534 } 535 prepared, err := flush.PrepareData(prepareOpts) 536 require.NoError(t, err) 537 538 // Start persistence 539 now = time.Now() 540 require.NoError(t, prepared.Persist(metadata, segment, checksum)) 541 542 // Advance time and write again 543 now = now.Add(time.Millisecond) 544 require.NoError(t, prepared.Persist(metadata, segment, checksum)) 545 546 // Check there is no rate limiting 547 require.Equal(t, time.Duration(0), slept) 548 require.Equal(t, int64(6), pm.bytesWritten) 549 } 550 551 func TestPersistenceManagerWithRateLimit(t *testing.T) { 552 ctrl := gomock.NewController(t) 553 defer ctrl.Finish() 554 555 pm, writer, _, opts := testDataPersistManager(t, ctrl) 556 defer os.RemoveAll(pm.filePathPrefix) 557 558 shard := uint32(0) 559 blockStart := xtime.FromSeconds(1000) 560 561 var ( 562 now time.Time 563 slept time.Duration 564 iter = 2 565 id = ident.StringID("foo") 566 head = checked.NewBytes([]byte{0x1, 0x2}, nil) 567 tail = checked.NewBytes([]byte{0x3}, nil) 568 segment = ts.NewSegment(head, tail, 0, ts.FinalizeNone) 569 checksum = segment.CalculateChecksum() 570 ) 571 572 pm.nowFn = func() time.Time { return now } 573 pm.sleepFn = func(d time.Duration) { slept += d } 574 575 writerOpts := xtest.CmpMatcher(DataWriterOpenOptions{ 576 Identifier: FileSetFileIdentifier{ 577 Namespace: testNs1ID, 578 Shard: shard, 579 BlockStart: blockStart, 580 }, 581 BlockSize: testBlockSize, 582 }, m3test.IdentTransformer) 583 metadata := persist.NewMetadataFromIDAndTags(id, ident.Tags{}, 584 persist.MetadataOptions{}) 585 writer.EXPECT().Open(writerOpts).Return(nil).Times(iter) 586 writer.EXPECT(). 587 WriteAll(metadata, pm.dataPM.segmentHolder, checksum). 588 Return(nil). 589 AnyTimes() 590 writer.EXPECT().Close().Times(iter) 591 592 // Enable rate limiting 593 runtimeOpts := opts.RuntimeOptionsManager().Get() 594 opts.RuntimeOptionsManager().Update( 595 runtimeOpts.SetPersistRateLimitOptions( 596 runtimeOpts.PersistRateLimitOptions(). 597 SetLimitEnabled(true). 598 SetLimitCheckEvery(2). 599 SetLimitMbps(16.0))) 600 601 // Wait until enabled 602 for func() bool { 603 pm.Lock() 604 defer pm.Unlock() 605 return !pm.currRateLimitOpts.LimitEnabled() 606 }() { 607 time.Sleep(10 * time.Millisecond) 608 } 609 610 for i := 0; i < iter; i++ { 611 // Reset 612 slept = time.Duration(0) 613 614 flush, err := pm.StartFlushPersist() 615 require.NoError(t, err) 616 617 // prepare the flush 618 prepareOpts := persist.DataPrepareOptions{ 619 NamespaceMetadata: testNs1Metadata(t), 620 Shard: shard, 621 BlockStart: blockStart, 622 } 623 prepared, err := flush.PrepareData(prepareOpts) 624 require.NoError(t, err) 625 626 // Start persistence 627 now = time.Now() 628 require.NoError(t, prepared.Persist(metadata, segment, checksum)) 629 630 // Assert we don't rate limit if the count is not enough yet 631 require.NoError(t, prepared.Persist(metadata, segment, checksum)) 632 require.Equal(t, time.Duration(0), slept) 633 634 // Advance time and check we rate limit if the disk throughput exceeds the limit 635 now = now.Add(time.Microsecond) 636 require.NoError(t, prepared.Persist(metadata, segment, checksum)) 637 require.Equal(t, time.Duration(1861), slept) 638 639 // Advance time and check we don't rate limit if the disk throughput is below the limit 640 require.NoError(t, prepared.Persist(metadata, segment, checksum)) 641 now = now.Add(time.Second - time.Microsecond) 642 require.NoError(t, prepared.Persist(metadata, segment, checksum)) 643 require.Equal(t, time.Duration(1861), slept) 644 645 require.Equal(t, int64(15), pm.bytesWritten) 646 647 require.NoError(t, prepared.Close()) 648 649 assert.NoError(t, flush.DoneFlush()) 650 } 651 } 652 653 func TestPersistenceManagerNamespaceSwitch(t *testing.T) { 654 ctrl := gomock.NewController(t) 655 defer ctrl.Finish() 656 657 pm, writer, _, _ := testDataPersistManager(t, ctrl) 658 defer os.RemoveAll(pm.filePathPrefix) 659 660 shard := uint32(0) 661 blockStart := xtime.FromSeconds(1000) 662 663 flush, err := pm.StartFlushPersist() 664 require.NoError(t, err) 665 666 defer func() { 667 assert.NoError(t, flush.DoneFlush()) 668 }() 669 670 writerOpts := xtest.CmpMatcher(DataWriterOpenOptions{ 671 Identifier: FileSetFileIdentifier{ 672 Namespace: testNs1ID, 673 Shard: shard, 674 BlockStart: blockStart, 675 }, 676 BlockSize: testBlockSize, 677 }, m3test.IdentTransformer) 678 writer.EXPECT().Open(writerOpts).Return(nil) 679 prepareOpts := persist.DataPrepareOptions{ 680 NamespaceMetadata: testNs1Metadata(t), 681 Shard: shard, 682 BlockStart: blockStart, 683 } 684 prepared, err := flush.PrepareData(prepareOpts) 685 require.NoError(t, err) 686 require.NotNil(t, prepared.Persist) 687 require.NotNil(t, prepared.Close) 688 689 writerOpts = xtest.CmpMatcher(DataWriterOpenOptions{ 690 Identifier: FileSetFileIdentifier{ 691 Namespace: testNs2ID, 692 Shard: shard, 693 BlockStart: blockStart, 694 }, 695 BlockSize: testBlockSize, 696 }, m3test.IdentTransformer) 697 writer.EXPECT().Open(writerOpts).Return(nil) 698 prepareOpts = persist.DataPrepareOptions{ 699 NamespaceMetadata: testNs2Metadata(t), 700 Shard: shard, 701 BlockStart: blockStart, 702 } 703 prepared, err = flush.PrepareData(prepareOpts) 704 require.NoError(t, err) 705 require.NotNil(t, prepared.Persist) 706 require.NotNil(t, prepared.Close) 707 } 708 709 func createDataShardDir(t *testing.T, prefix string, namespace ident.ID, shard uint32) string { 710 shardDirPath := ShardDataDirPath(prefix, namespace, shard) 711 err := os.MkdirAll(shardDirPath, os.ModeDir|os.FileMode(0755)) 712 require.Nil(t, err) 713 return shardDirPath 714 } 715 716 func createIndexDataDir(t *testing.T, prefix string, namespace ident.ID) string { 717 path := NamespaceIndexDataDirPath(prefix, namespace) 718 err := os.MkdirAll(path, os.ModeDir|os.FileMode(0755)) 719 require.Nil(t, err) 720 return path 721 } 722 723 func testDataPersistManager( 724 t *testing.T, 725 ctrl *gomock.Controller, 726 ) (*persistManager, *MockDataFileSetWriter, *MockSnapshotMetadataFileWriter, Options) { 727 dir := createTempDir(t) 728 729 opts := testDefaultOpts. 730 SetFilePathPrefix(dir). 731 SetWriterBufferSize(10) 732 733 var ( 734 fileSetWriter = NewMockDataFileSetWriter(ctrl) 735 snapshotMetadataWriter = NewMockSnapshotMetadataFileWriter(ctrl) 736 ) 737 738 mgr, err := NewPersistManager(opts) 739 require.NoError(t, err) 740 741 manager := mgr.(*persistManager) 742 manager.dataPM.writer = fileSetWriter 743 manager.dataPM.snapshotMetadataWriter = snapshotMetadataWriter 744 manager.dataPM.nextSnapshotMetadataFileIndex = func(Options) (int64, error) { 745 return 0, nil 746 } 747 748 return manager, fileSetWriter, snapshotMetadataWriter, opts 749 } 750 751 func testIndexPersistManager(t *testing.T, ctrl *gomock.Controller, 752 ) (*persistManager, *MockIndexFileSetWriter, *m3ninxpersist.MockMutableSegmentFileSetWriter, Options) { 753 dir := createTempDir(t) 754 755 opts := testDefaultOpts. 756 SetFilePathPrefix(dir). 757 SetWriterBufferSize(10) 758 759 writer := NewMockIndexFileSetWriter(ctrl) 760 segmentWriter := m3ninxpersist.NewMockMutableSegmentFileSetWriter(ctrl) 761 762 mgr, err := NewPersistManager(opts) 763 require.NoError(t, err) 764 765 manager := mgr.(*persistManager) 766 manager.indexPM.newIndexWriterFn = func(opts Options) (IndexFileSetWriter, error) { 767 return writer, nil 768 } 769 manager.indexPM.segmentWriter = segmentWriter 770 return manager, writer, segmentWriter, opts 771 }