github.com/m3db/m3@v1.5.0/src/dbnode/storage/flush_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 storage 22 23 import ( 24 "errors" 25 "sort" 26 "strings" 27 "sync" 28 "testing" 29 "time" 30 31 "github.com/m3db/m3/src/dbnode/namespace" 32 "github.com/m3db/m3/src/dbnode/persist" 33 "github.com/m3db/m3/src/dbnode/persist/fs/commitlog" 34 "github.com/m3db/m3/src/dbnode/retention" 35 "github.com/m3db/m3/src/x/ident" 36 xtest "github.com/m3db/m3/src/x/test" 37 xtime "github.com/m3db/m3/src/x/time" 38 39 "github.com/golang/mock/gomock" 40 "github.com/stretchr/testify/require" 41 "github.com/uber-go/tally" 42 ) 43 44 var testCommitlogFile = persist.CommitLogFile{ 45 FilePath: "/var/lib/m3db/commitlogs/commitlog-0-0.db", 46 Index: 0, 47 } 48 49 func newMultipleFlushManagerNeedsFlush(t *testing.T, ctrl *gomock.Controller) ( 50 *flushManager, 51 *MockdatabaseNamespace, 52 *MockdatabaseNamespace, 53 *commitlog.MockCommitLog, 54 ) { 55 options := namespace.NewOptions() 56 namespace := NewMockdatabaseNamespace(ctrl) 57 namespace.EXPECT().Options().Return(options).AnyTimes() 58 namespace.EXPECT().ID().Return(defaultTestNs1ID).AnyTimes() 59 otherNamespace := NewMockdatabaseNamespace(ctrl) 60 otherNamespace.EXPECT().Options().Return(options).AnyTimes() 61 otherNamespace.EXPECT().ID().Return(ident.StringID("someString")).AnyTimes() 62 63 db := newMockdatabase(ctrl, namespace, otherNamespace) 64 65 cl := commitlog.NewMockCommitLog(ctrl) 66 cl.EXPECT().RotateLogs().Return(testCommitlogFile, nil).AnyTimes() 67 68 fm := newFlushManager(db, cl, tally.NoopScope).(*flushManager) 69 70 return fm, namespace, otherNamespace, cl 71 } 72 73 func TestFlushManagerFlushAlreadyInProgress(t *testing.T) { 74 ctrl := xtest.NewController(t) 75 defer ctrl.Finish() 76 77 var ( 78 // Channels used to coordinate flushing / snapshotting 79 startCh = make(chan struct{}, 1) 80 doneCh = make(chan struct{}, 1) 81 ) 82 defer func() { 83 close(startCh) 84 close(doneCh) 85 }() 86 87 var ( 88 mockPersistManager = persist.NewMockManager(ctrl) 89 mockFlushPerist = persist.NewMockFlushPreparer(ctrl) 90 mockSnapshotPersist = persist.NewMockSnapshotPreparer(ctrl) 91 ) 92 93 mockFlushPerist.EXPECT().DoneFlush().Return(nil).AnyTimes() 94 mockPersistManager.EXPECT().StartFlushPersist().Do(func() { 95 startCh <- struct{}{} 96 <-doneCh 97 }).Return(mockFlushPerist, nil).AnyTimes() 98 99 mockSnapshotPersist.EXPECT().DoneSnapshot(gomock.Any(), gomock.Any()).Return(nil).AnyTimes() 100 mockPersistManager.EXPECT().StartSnapshotPersist(gomock.Any()).Do(func(_ interface{}) { 101 startCh <- struct{}{} 102 <-doneCh 103 }).Return(mockSnapshotPersist, nil).AnyTimes() 104 105 mockIndexFlusher := persist.NewMockIndexFlush(ctrl) 106 mockIndexFlusher.EXPECT().DoneIndex().Return(nil).AnyTimes() 107 mockPersistManager.EXPECT().StartIndexPersist().Return(mockIndexFlusher, nil).AnyTimes() 108 109 testOpts := DefaultTestOptions().SetPersistManager(mockPersistManager) 110 db := newMockdatabase(ctrl) 111 db.EXPECT().Options().Return(testOpts).AnyTimes() 112 db.EXPECT().OwnedNamespaces().Return(nil, nil).AnyTimes() 113 114 cl := commitlog.NewMockCommitLog(ctrl) 115 cl.EXPECT().RotateLogs().Return(testCommitlogFile, nil).AnyTimes() 116 117 fm := newFlushManager(db, cl, tally.NoopScope).(*flushManager) 118 fm.pm = mockPersistManager 119 120 now := xtime.UnixNano(0) 121 var wg sync.WaitGroup 122 wg.Add(2) 123 124 // Goroutine 1 should successfully flush. 125 go func() { 126 defer wg.Done() 127 require.NoError(t, fm.Flush(now)) 128 }() 129 130 // Goroutine 2 should indicate already flushing. 131 go func() { 132 defer wg.Done() 133 134 // Wait until we start the flushing process. 135 <-startCh 136 137 // Ensure it doesn't allow a parallel flush. 138 require.Equal(t, errFlushOperationsInProgress, fm.Flush(now)) 139 140 // Allow the flush to finish. 141 doneCh <- struct{}{} 142 143 // Allow the snapshot to begin and finish. 144 <-startCh 145 146 // Ensure it doesn't allow a parallel flush. 147 require.Equal(t, errFlushOperationsInProgress, fm.Flush(now)) 148 149 doneCh <- struct{}{} 150 }() 151 152 wg.Wait() 153 } 154 155 // TestFlushManagerFlushDoneFlushError makes sure that flush errors do not 156 // impact snapshotting or index operations. 157 func TestFlushManagerFlushDoneFlushError(t *testing.T) { 158 ctrl := xtest.NewController(t) 159 defer ctrl.Finish() 160 161 var ( 162 fakeErr = errors.New("fake error while marking flush done") 163 mockPersistManager = persist.NewMockManager(ctrl) 164 mockFlushPersist = persist.NewMockFlushPreparer(ctrl) 165 mockSnapshotPersist = persist.NewMockSnapshotPreparer(ctrl) 166 ) 167 168 mockFlushPersist.EXPECT().DoneFlush().Return(fakeErr) 169 mockPersistManager.EXPECT().StartFlushPersist().Return(mockFlushPersist, nil) 170 171 mockSnapshotPersist.EXPECT().DoneSnapshot(gomock.Any(), testCommitlogFile).Return(nil) 172 mockPersistManager.EXPECT().StartSnapshotPersist(gomock.Any()).Return(mockSnapshotPersist, nil) 173 174 mockIndexFlusher := persist.NewMockIndexFlush(ctrl) 175 mockIndexFlusher.EXPECT().DoneIndex().Return(nil) 176 mockPersistManager.EXPECT().StartIndexPersist().Return(mockIndexFlusher, nil) 177 178 testOpts := DefaultTestOptions().SetPersistManager(mockPersistManager) 179 db := newMockdatabase(ctrl) 180 db.EXPECT().Options().Return(testOpts).AnyTimes() 181 db.EXPECT().OwnedNamespaces().Return(nil, nil) 182 183 cl := commitlog.NewMockCommitLog(ctrl) 184 cl.EXPECT().RotateLogs().Return(testCommitlogFile, nil).AnyTimes() 185 186 fm := newFlushManager(db, cl, tally.NoopScope).(*flushManager) 187 fm.pm = mockPersistManager 188 189 now := xtime.UnixNano(0) 190 require.EqualError(t, fakeErr, fm.Flush(now).Error()) 191 } 192 193 // TestFlushManagerNamespaceFlushTimesErr makes sure that namespaceFlushTimes errors do 194 // not leave the persist manager in an invalid state. 195 func TestFlushManagerNamespaceFlushTimesErr(t *testing.T) { 196 ctrl := xtest.NewController(t) 197 defer ctrl.Finish() 198 199 var ( 200 fakeErr = errors.New("some-err") 201 mockPersistManager = persist.NewMockManager(ctrl) 202 mockFlushPersist = persist.NewMockFlushPreparer(ctrl) 203 mockSnapshotPersist = persist.NewMockSnapshotPreparer(ctrl) 204 ) 205 206 // Make sure DoneFlush is called despite encountering an error, once for snapshot and once for warm flush. 207 mockFlushPersist.EXPECT().DoneFlush().Return(nil) 208 mockPersistManager.EXPECT().StartFlushPersist().Return(mockFlushPersist, nil) 209 210 mockSnapshotPersist.EXPECT().DoneSnapshot(gomock.Any(), testCommitlogFile).Return(nil) 211 mockPersistManager.EXPECT().StartSnapshotPersist(gomock.Any()).Return(mockSnapshotPersist, nil) 212 213 mockIndexFlusher := persist.NewMockIndexFlush(ctrl) 214 mockIndexFlusher.EXPECT().DoneIndex().Return(nil) 215 mockPersistManager.EXPECT().StartIndexPersist().Return(mockIndexFlusher, nil) 216 217 testOpts := DefaultTestOptions().SetPersistManager(mockPersistManager) 218 db := newMockdatabase(ctrl) 219 db.EXPECT().Options().Return(testOpts).AnyTimes() 220 221 nsOpts := defaultTestNs1Opts.SetIndexOptions(namespace.NewIndexOptions().SetEnabled(false)) 222 ns := NewMockdatabaseNamespace(ctrl) 223 ns.EXPECT().Options().Return(nsOpts).AnyTimes() 224 ns.EXPECT().ID().Return(defaultTestNs1ID).AnyTimes() 225 ns.EXPECT().NeedsFlush(gomock.Any(), gomock.Any()).Return(false, fakeErr).AnyTimes() 226 ns.EXPECT().Snapshot(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil).AnyTimes() 227 db.EXPECT().OwnedNamespaces().Return([]databaseNamespace{ns}, nil) 228 229 cl := commitlog.NewMockCommitLog(ctrl) 230 cl.EXPECT().RotateLogs().Return(testCommitlogFile, nil).AnyTimes() 231 232 fm := newFlushManager(db, cl, tally.NoopScope).(*flushManager) 233 fm.pm = mockPersistManager 234 235 now := xtime.UnixNano(0) 236 require.True(t, strings.Contains(fm.Flush(now).Error(), fakeErr.Error())) 237 } 238 239 // TestFlushManagerFlushDoneSnapshotError makes sure that snapshot errors do not 240 // impact flushing or index operations. 241 func TestFlushManagerFlushDoneSnapshotError(t *testing.T) { 242 ctrl := xtest.NewController(t) 243 defer ctrl.Finish() 244 245 var ( 246 fakeErr = errors.New("fake error while marking snapshot done") 247 mockPersistManager = persist.NewMockManager(ctrl) 248 mockFlushPersist = persist.NewMockFlushPreparer(ctrl) 249 mockSnapshotPersist = persist.NewMockSnapshotPreparer(ctrl) 250 ) 251 252 mockFlushPersist.EXPECT().DoneFlush().Return(nil) 253 mockPersistManager.EXPECT().StartFlushPersist().Return(mockFlushPersist, nil) 254 255 mockSnapshotPersist.EXPECT().DoneSnapshot(gomock.Any(), testCommitlogFile).Return(fakeErr) 256 mockPersistManager.EXPECT().StartSnapshotPersist(gomock.Any()).Return(mockSnapshotPersist, nil) 257 258 mockIndexFlusher := persist.NewMockIndexFlush(ctrl) 259 mockIndexFlusher.EXPECT().DoneIndex().Return(nil) 260 mockPersistManager.EXPECT().StartIndexPersist().Return(mockIndexFlusher, nil) 261 262 testOpts := DefaultTestOptions().SetPersistManager(mockPersistManager) 263 db := newMockdatabase(ctrl) 264 db.EXPECT().Options().Return(testOpts).AnyTimes() 265 db.EXPECT().OwnedNamespaces().Return(nil, nil) 266 267 cl := commitlog.NewMockCommitLog(ctrl) 268 cl.EXPECT().RotateLogs().Return(testCommitlogFile, nil).AnyTimes() 269 270 fm := newFlushManager(db, cl, tally.NoopScope).(*flushManager) 271 fm.pm = mockPersistManager 272 273 now := xtime.UnixNano(0) 274 require.EqualError(t, fakeErr, fm.Flush(now).Error()) 275 } 276 277 func TestFlushManagerFlushDoneIndexError(t *testing.T) { 278 ctrl := xtest.NewController(t) 279 defer ctrl.Finish() 280 281 var ( 282 mockFlushPersist = persist.NewMockFlushPreparer(ctrl) 283 mockSnapshotPersist = persist.NewMockSnapshotPreparer(ctrl) 284 mockPersistManager = persist.NewMockManager(ctrl) 285 ) 286 287 mockFlushPersist.EXPECT().DoneFlush().Return(nil) 288 mockPersistManager.EXPECT().StartFlushPersist().Return(mockFlushPersist, nil) 289 290 mockSnapshotPersist.EXPECT().DoneSnapshot(gomock.Any(), testCommitlogFile).Return(nil) 291 mockPersistManager.EXPECT().StartSnapshotPersist(gomock.Any()).Return(mockSnapshotPersist, nil) 292 293 fakeErr := errors.New("fake error while marking flush done") 294 mockIndexFlusher := persist.NewMockIndexFlush(ctrl) 295 mockIndexFlusher.EXPECT().DoneIndex().Return(fakeErr) 296 mockPersistManager.EXPECT().StartIndexPersist().Return(mockIndexFlusher, nil) 297 298 testOpts := DefaultTestOptions().SetPersistManager(mockPersistManager) 299 db := newMockdatabase(ctrl) 300 db.EXPECT().Options().Return(testOpts).AnyTimes() 301 db.EXPECT().OwnedNamespaces().Return(nil, nil) 302 303 cl := commitlog.NewMockCommitLog(ctrl) 304 cl.EXPECT().RotateLogs().Return(testCommitlogFile, nil).AnyTimes() 305 306 fm := newFlushManager(db, cl, tally.NoopScope).(*flushManager) 307 fm.pm = mockPersistManager 308 309 now := xtime.UnixNano(0) 310 require.EqualError(t, fakeErr, fm.Flush(now).Error()) 311 } 312 313 func TestFlushManagerSkipNamespaceIndexingDisabled(t *testing.T) { 314 ctrl := xtest.NewController(t) 315 defer ctrl.Finish() 316 317 nsOpts := defaultTestNs1Opts.SetIndexOptions(namespace.NewIndexOptions().SetEnabled(false)) 318 s1 := NewMockdatabaseShard(ctrl) 319 s2 := NewMockdatabaseShard(ctrl) 320 ns := NewMockdatabaseNamespace(ctrl) 321 ns.EXPECT().Options().Return(nsOpts).AnyTimes() 322 ns.EXPECT().ID().Return(defaultTestNs1ID).AnyTimes() 323 ns.EXPECT().NeedsFlush(gomock.Any(), gomock.Any()).Return(true, nil).AnyTimes() 324 ns.EXPECT().WarmFlush(gomock.Any(), gomock.Any()).Return(nil).AnyTimes() 325 ns.EXPECT().Snapshot(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil).AnyTimes() 326 s1.EXPECT().ID().Return(uint32(1)).AnyTimes() 327 s2.EXPECT().ID().Return(uint32(2)).AnyTimes() 328 329 var ( 330 mockFlushPersist = persist.NewMockFlushPreparer(ctrl) 331 mockSnapshotPersist = persist.NewMockSnapshotPreparer(ctrl) 332 mockPersistManager = persist.NewMockManager(ctrl) 333 ) 334 335 mockFlushPersist.EXPECT().DoneFlush().Return(nil) 336 mockPersistManager.EXPECT().StartFlushPersist().Return(mockFlushPersist, nil) 337 338 mockSnapshotPersist.EXPECT().DoneSnapshot(gomock.Any(), testCommitlogFile).Return(nil) 339 mockPersistManager.EXPECT().StartSnapshotPersist(gomock.Any()).Return(mockSnapshotPersist, nil) 340 341 mockIndexFlusher := persist.NewMockIndexFlush(ctrl) 342 mockIndexFlusher.EXPECT().DoneIndex().Return(nil) 343 mockPersistManager.EXPECT().StartIndexPersist().Return(mockIndexFlusher, nil) 344 345 testOpts := DefaultTestOptions().SetPersistManager(mockPersistManager) 346 db := newMockdatabase(ctrl) 347 db.EXPECT().Options().Return(testOpts).AnyTimes() 348 db.EXPECT().OwnedNamespaces().Return([]databaseNamespace{ns}, nil) 349 350 cl := commitlog.NewMockCommitLog(ctrl) 351 cl.EXPECT().RotateLogs().Return(testCommitlogFile, nil).AnyTimes() 352 353 fm := newFlushManager(db, cl, tally.NoopScope).(*flushManager) 354 fm.pm = mockPersistManager 355 356 now := xtime.UnixNano(0) 357 require.NoError(t, fm.Flush(now)) 358 } 359 360 func TestFlushManagerNamespaceIndexingEnabled(t *testing.T) { 361 ctrl := xtest.NewController(t) 362 defer ctrl.Finish() 363 364 nsOpts := defaultTestNs1Opts.SetIndexOptions(namespace.NewIndexOptions().SetEnabled(true)) 365 s1 := NewMockdatabaseShard(ctrl) 366 s2 := NewMockdatabaseShard(ctrl) 367 ns := NewMockdatabaseNamespace(ctrl) 368 ns.EXPECT().Options().Return(nsOpts).AnyTimes() 369 ns.EXPECT().ID().Return(defaultTestNs1ID).AnyTimes() 370 ns.EXPECT().NeedsFlush(gomock.Any(), gomock.Any()).Return(true, nil).AnyTimes() 371 s1.EXPECT().ID().Return(uint32(1)).AnyTimes() 372 s2.EXPECT().ID().Return(uint32(2)).AnyTimes() 373 374 // Validate that the flush state is marked as successful only AFTER all prequisite steps have been run. 375 // Order is important to avoid any edge case where data is GCed from memory without all flushing operations 376 // being completed. 377 gomock.InOrder( 378 ns.EXPECT().WarmFlush(gomock.Any(), gomock.Any()).Return(nil).AnyTimes(), 379 ns.EXPECT().Snapshot(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil).AnyTimes(), 380 ns.EXPECT().FlushIndex(gomock.Any()).Return(nil), 381 ) 382 383 var ( 384 mockFlushPersist = persist.NewMockFlushPreparer(ctrl) 385 mockSnapshotPersist = persist.NewMockSnapshotPreparer(ctrl) 386 mockPersistManager = persist.NewMockManager(ctrl) 387 ) 388 389 mockFlushPersist.EXPECT().DoneFlush().Return(nil) 390 mockPersistManager.EXPECT().StartFlushPersist().Return(mockFlushPersist, nil) 391 392 mockSnapshotPersist.EXPECT().DoneSnapshot(gomock.Any(), testCommitlogFile).Return(nil) 393 mockPersistManager.EXPECT().StartSnapshotPersist(gomock.Any()).Return(mockSnapshotPersist, nil) 394 395 mockIndexFlusher := persist.NewMockIndexFlush(ctrl) 396 mockIndexFlusher.EXPECT().DoneIndex().Return(nil) 397 mockPersistManager.EXPECT().StartIndexPersist().Return(mockIndexFlusher, nil) 398 399 testOpts := DefaultTestOptions().SetPersistManager(mockPersistManager) 400 db := newMockdatabase(ctrl) 401 db.EXPECT().Options().Return(testOpts).AnyTimes() 402 db.EXPECT().OwnedNamespaces().Return([]databaseNamespace{ns}, nil) 403 404 cl := commitlog.NewMockCommitLog(ctrl) 405 cl.EXPECT().RotateLogs().Return(testCommitlogFile, nil).AnyTimes() 406 407 fm := newFlushManager(db, cl, tally.NoopScope).(*flushManager) 408 fm.pm = mockPersistManager 409 410 now := xtime.UnixNano(0) 411 require.NoError(t, fm.Flush(now)) 412 } 413 414 func TestFlushManagerFlushTimeStart(t *testing.T) { 415 ctrl := xtest.NewController(t) 416 defer ctrl.Finish() 417 418 inputs := []struct { 419 ts xtime.UnixNano 420 expected xtime.UnixNano 421 }{ 422 { 423 ts: xtime.FromSeconds(86400 * 2), 424 expected: xtime.UnixNano(0), 425 }, 426 { 427 ts: xtime.FromSeconds(86400*2 + 7200), 428 expected: xtime.FromSeconds(7200), 429 }, 430 { 431 ts: xtime.FromSeconds(86400*2 + 10800), 432 expected: xtime.FromSeconds(7200), 433 }, 434 } 435 436 fm, _, _, _ := newMultipleFlushManagerNeedsFlush(t, ctrl) 437 for _, input := range inputs { 438 start, _ := fm.flushRange(defaultTestRetentionOpts, input.ts) 439 require.Equal(t, input.expected, start) 440 } 441 } 442 443 func TestFlushManagerFlushTimeEnd(t *testing.T) { 444 ctrl := xtest.NewController(t) 445 defer ctrl.Finish() 446 447 inputs := []struct { 448 ts xtime.UnixNano 449 expected xtime.UnixNano 450 }{ 451 { 452 ts: xtime.FromSeconds(7800), 453 expected: xtime.UnixNano(0), 454 }, 455 { 456 ts: xtime.FromSeconds(8000), 457 expected: xtime.UnixNano(0), 458 }, 459 { 460 ts: xtime.FromSeconds(15200), 461 expected: xtime.FromSeconds(7200), 462 }, 463 } 464 465 fm, _, _, _ := newMultipleFlushManagerNeedsFlush(t, ctrl) 466 for _, input := range inputs { 467 _, end := fm.flushRange(defaultTestRetentionOpts, input.ts) 468 require.Equal(t, input.expected, end) 469 } 470 } 471 472 func TestFlushManagerNamespaceFlushTimesNoNeedFlush(t *testing.T) { 473 ctrl := xtest.NewController(t) 474 defer ctrl.Finish() 475 476 fm, ns1, _, _ := newMultipleFlushManagerNeedsFlush(t, ctrl) 477 now := xtime.Now() 478 479 ns1.EXPECT().NeedsFlush(gomock.Any(), gomock.Any()).Return(false, nil).AnyTimes() 480 flushTimes, err := fm.namespaceFlushTimes(ns1, now) 481 require.NoError(t, err) 482 require.Empty(t, flushTimes) 483 } 484 485 func TestFlushManagerNamespaceFlushTimesError(t *testing.T) { 486 ctrl := xtest.NewController(t) 487 defer ctrl.Finish() 488 489 fm, ns1, _, _ := newMultipleFlushManagerNeedsFlush(t, ctrl) 490 now := xtime.Now() 491 492 ns1.EXPECT(). 493 NeedsFlush(gomock.Any(), gomock.Any()). 494 Return(false, errors.New("an error")). 495 AnyTimes() 496 _, err := fm.namespaceFlushTimes(ns1, now) 497 require.Error(t, err) 498 } 499 500 func TestFlushManagerNamespaceFlushTimesAllNeedFlush(t *testing.T) { 501 ctrl := xtest.NewController(t) 502 defer ctrl.Finish() 503 504 fm, ns1, _, _ := newMultipleFlushManagerNeedsFlush(t, ctrl) 505 now := xtime.Now() 506 507 ns1.EXPECT().NeedsFlush(gomock.Any(), gomock.Any()).Return(true, nil).AnyTimes() 508 times, err := fm.namespaceFlushTimes(ns1, now) 509 require.NoError(t, err) 510 sort.Sort(timesInOrder(times)) 511 512 blockSize := ns1.Options().RetentionOptions().BlockSize() 513 start := retention.FlushTimeStart(ns1.Options().RetentionOptions(), now) 514 end := retention.FlushTimeEnd(ns1.Options().RetentionOptions(), now) 515 516 require.Equal(t, numIntervals(start, end, blockSize), len(times)) 517 for i, ti := range times { 518 require.Equal(t, start.Add(time.Duration(i)*blockSize), ti) 519 } 520 } 521 522 func TestFlushManagerNamespaceFlushTimesSomeNeedFlush(t *testing.T) { 523 ctrl := xtest.NewController(t) 524 defer ctrl.Finish() 525 526 fm, ns1, _, _ := newMultipleFlushManagerNeedsFlush(t, ctrl) 527 now := xtime.Now() 528 529 blockSize := ns1.Options().RetentionOptions().BlockSize() 530 start := retention.FlushTimeStart(ns1.Options().RetentionOptions(), now) 531 end := retention.FlushTimeEnd(ns1.Options().RetentionOptions(), now) 532 num := numIntervals(start, end, blockSize) 533 534 var expectedTimes []xtime.UnixNano 535 for i := 0; i < num; i++ { 536 st := start.Add(time.Duration(i) * blockSize) 537 538 // skip 1/3 of input 539 if i%3 == 0 { 540 ns1.EXPECT().NeedsFlush(st, st).Return(false, nil) 541 continue 542 } 543 544 ns1.EXPECT().NeedsFlush(st, st).Return(true, nil) 545 expectedTimes = append(expectedTimes, st) 546 } 547 548 times, err := fm.namespaceFlushTimes(ns1, now) 549 require.NoError(t, err) 550 require.NotEmpty(t, times) 551 sort.Sort(timesInOrder(times)) 552 require.Equal(t, expectedTimes, times) 553 } 554 555 func TestFlushManagerFlushSnapshot(t *testing.T) { 556 ctrl := xtest.NewController(t) 557 defer ctrl.Finish() 558 559 fm, ns1, ns2, _ := newMultipleFlushManagerNeedsFlush(t, ctrl) 560 now := xtime.Now() 561 562 for _, ns := range []*MockdatabaseNamespace{ns1, ns2} { 563 rOpts := ns.Options().RetentionOptions() 564 blockSize := rOpts.BlockSize() 565 bufferFuture := rOpts.BufferFuture() 566 567 start := retention.FlushTimeStart(ns.Options().RetentionOptions(), now) 568 flushEnd := retention.FlushTimeEnd(ns.Options().RetentionOptions(), now) 569 num := numIntervals(start, flushEnd, blockSize) 570 571 for i := 0; i < num; i++ { 572 st := start.Add(time.Duration(i) * blockSize) 573 ns.EXPECT().NeedsFlush(st, st).Return(false, nil) 574 } 575 576 var ( 577 snapshotEnd = now.Add(bufferFuture).Truncate(blockSize) 578 snapshotBlocks []xtime.UnixNano 579 ) 580 num = numIntervals(start, snapshotEnd, blockSize) 581 for i := num - 1; i >= 0; i-- { 582 snapshotBlocks = append(snapshotBlocks, start.Add(time.Duration(i)*blockSize)) 583 } 584 ns.EXPECT().Snapshot(snapshotBlocks, now, gomock.Any()) 585 } 586 587 require.NoError(t, fm.Flush(now)) 588 589 lastSuccessfulSnapshot, ok := fm.LastSuccessfulSnapshotStartTime() 590 require.True(t, ok) 591 require.Equal(t, now, lastSuccessfulSnapshot) 592 } 593 594 type timesInOrder []xtime.UnixNano 595 596 func (a timesInOrder) Len() int { return len(a) } 597 func (a timesInOrder) Swap(i, j int) { a[i], a[j] = a[j], a[i] } 598 func (a timesInOrder) Less(i, j int) bool { return a[i].Before(a[j]) }