@@ -19,6 +19,7 @@ package badger
19
19
import (
20
20
"math"
21
21
"testing"
22
+ "time"
22
23
23
24
"github.com/dgraph-io/badger/v2/options"
24
25
"github.com/dgraph-io/badger/v2/pb"
@@ -439,3 +440,140 @@ func TestDiscardFirstVersion(t *testing.T) {
439
440
getAllAndCheck (t , db , ExpectedKeys )
440
441
})
441
442
}
443
+
444
+ // This test ensures we don't stall when L1's size is greater than opt.LevelOneSize.
445
+ // We should stall only when L0 tables more than the opt.NumLevelZeroTableStall.
446
+ func TestL1Stall (t * testing.T ) {
447
+ opt := DefaultOptions ("" )
448
+ // Disable all compactions.
449
+ opt .NumCompactors = 0
450
+ // Number of level zero tables.
451
+ opt .NumLevelZeroTables = 3
452
+ // Addition of new tables will stall if there are 4 or more L0 tables.
453
+ opt .NumLevelZeroTablesStall = 4
454
+ // Level 1 size is 10 bytes.
455
+ opt .LevelOneSize = 10
456
+
457
+ runBadgerTest (t , & opt , func (t * testing.T , db * DB ) {
458
+ // Level 0 has 4 tables.
459
+ db .lc .levels [0 ].Lock ()
460
+ db .lc .levels [0 ].tables = []* table.Table {createEmptyTable (db ), createEmptyTable (db ),
461
+ createEmptyTable (db ), createEmptyTable (db )}
462
+ db .lc .levels [0 ].Unlock ()
463
+
464
+ timeout := time .After (5 * time .Second )
465
+ done := make (chan bool )
466
+
467
+ // This is important. Set level 1 size more than the opt.LevelOneSize (we've set it to 10).
468
+ db .lc .levels [1 ].totalSize = 100
469
+ go func () {
470
+ tab := createEmptyTable (db )
471
+ db .lc .addLevel0Table (tab )
472
+ tab .DecrRef ()
473
+ done <- true
474
+ }()
475
+ time .Sleep (time .Second )
476
+
477
+ db .lc .levels [0 ].Lock ()
478
+ // Drop two tables from Level 0 so that addLevel0Table can make progress. Earlier table
479
+ // count was 4 which is equal to L0 stall count.
480
+ toDrop := db .lc .levels [0 ].tables [:2 ]
481
+ decrRefs (toDrop )
482
+ db .lc .levels [0 ].tables = db .lc .levels [0 ].tables [2 :]
483
+ db .lc .levels [0 ].Unlock ()
484
+
485
+ select {
486
+ case <- timeout :
487
+ t .Fatal ("Test didn't finish in time" )
488
+ case <- done :
489
+ }
490
+ })
491
+ }
492
+
493
+ func createEmptyTable (db * DB ) * table.Table {
494
+ opts := table.Options {
495
+ BloomFalsePositive : db .opt .BloomFalsePositive ,
496
+ LoadingMode : options .LoadToRAM ,
497
+ ChkMode : options .NoVerification ,
498
+ }
499
+ b := table .NewTableBuilder (opts )
500
+ // Add one key so that we can open this table.
501
+ b .Add (y .KeyWithTs ([]byte ("foo" ), 1 ), y.ValueStruct {}, 0 )
502
+ fd , err := y .CreateSyncedFile (table .NewFilename (db .lc .reserveFileID (), db .opt .Dir ), true )
503
+ if err != nil {
504
+ panic (err )
505
+ }
506
+
507
+ if _ , err := fd .Write (b .Finish ()); err != nil {
508
+ panic (err )
509
+ }
510
+ tab , err := table .OpenTable (fd , table.Options {})
511
+ if err != nil {
512
+ panic (err )
513
+ }
514
+ // Add dummy entry to manifest file so that it doesn't complain during compaction.
515
+ if err := db .manifest .addChanges ([]* pb.ManifestChange {
516
+ newCreateChange (tab .ID (), 0 , 0 , tab .CompressionType ()),
517
+ }); err != nil {
518
+ panic (err )
519
+ }
520
+
521
+ return tab
522
+ }
523
+
524
+ func TestL0Stall (t * testing.T ) {
525
+ test := func (t * testing.T , opt * Options ) {
526
+ runBadgerTest (t , opt , func (t * testing.T , db * DB ) {
527
+ db .lc .levels [0 ].Lock ()
528
+ // Add NumLevelZeroTableStall+1 number of tables to level 0. This would fill up level
529
+ // zero and all new additions are expected to stall if L0 is in memory.
530
+ for i := 0 ; i < opt .NumLevelZeroTablesStall + 1 ; i ++ {
531
+ db .lc .levels [0 ].tables = append (db .lc .levels [0 ].tables , createEmptyTable (db ))
532
+ }
533
+ db .lc .levels [0 ].Unlock ()
534
+
535
+ timeout := time .After (5 * time .Second )
536
+ done := make (chan bool )
537
+
538
+ go func () {
539
+ tab := createEmptyTable (db )
540
+ db .lc .addLevel0Table (tab )
541
+ tab .DecrRef ()
542
+ done <- true
543
+ }()
544
+ // Let it stall for a second.
545
+ time .Sleep (time .Second )
546
+
547
+ select {
548
+ case <- timeout :
549
+ if opt .KeepL0InMemory {
550
+ t .Log ("Timeout triggered" )
551
+ // Mark this test as successful since L0 is in memory and the
552
+ // addition of new table to L0 is supposed to stall.
553
+ } else {
554
+ t .Fatal ("Test didn't finish in time" )
555
+ }
556
+ case <- done :
557
+ // The test completed before 5 second timeout. Mark it as successful.
558
+ }
559
+ })
560
+ }
561
+
562
+ opt := DefaultOptions ("" )
563
+ opt .EventLogging = false
564
+ // Disable all compactions.
565
+ opt .NumCompactors = 0
566
+ // Number of level zero tables.
567
+ opt .NumLevelZeroTables = 3
568
+ // Addition of new tables will stall if there are 4 or more L0 tables.
569
+ opt .NumLevelZeroTablesStall = 4
570
+
571
+ t .Run ("with KeepL0InMemory" , func (t * testing.T ) {
572
+ opt .KeepL0InMemory = true
573
+ test (t , & opt )
574
+ })
575
+ t .Run ("with L0 on disk" , func (t * testing.T ) {
576
+ opt .KeepL0InMemory = false
577
+ test (t , & opt )
578
+ })
579
+ }
0 commit comments