@@ -19,6 +19,7 @@ package badger
1919import (
2020 "math"
2121 "testing"
22+ "time"
2223
2324 "github.com/dgraph-io/badger/v2/options"
2425 "github.com/dgraph-io/badger/v2/pb"
@@ -439,3 +440,140 @@ func TestDiscardFirstVersion(t *testing.T) {
439440 getAllAndCheck (t , db , ExpectedKeys )
440441 })
441442}
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