@@ -17,13 +17,19 @@ package database
1717
1818import (
1919 "context"
20+ "fmt"
2021 "os"
22+ "regexp"
2123 "testing"
2224 "time"
2325
2426 "github.com/dgraph-io/badger/v3"
2527 "github.com/go-logr/logr"
2628 "github.com/go-logr/logr/testr"
29+ "github.com/pkg/errors"
30+
31+ "github.com/google/go-containerregistry/pkg/name"
32+ "github.com/google/go-containerregistry/pkg/v1/remote"
2733)
2834
2935func TestBadgerGarbageCollectorDoesStop (t * testing.T ) {
@@ -59,13 +65,88 @@ func TestBadgerGarbageCollectorDoesStop(t *testing.T) {
5965 }
6066}
6167
68+ func TestBadgerGCLoad (t * testing.T ) {
69+ badger , db := createBadgerDatabaseForGC (t )
70+ ctx , cancel := context .WithCancel (
71+ logr .NewContext (context .Background (), testr .NewWithOptions (t , testr.Options {Verbosity : 1 , LogTimestamp : true })))
72+
73+ stop := make (chan struct {})
74+ go func () {
75+ gc := NewBadgerGarbageCollector ("loaded-badger-gc" , badger , time .Second * 20 , 0.7 )
76+ gc .Start (ctx )
77+ stop <- struct {}{}
78+ }()
79+
80+ repos := []string {"alpine" , "node" , "postgres" , "debian" }
81+ for i := 5 ; i >= 0 ; i -- {
82+ for _ , repo := range repos {
83+ ref , err := name .ParseReference (repo )
84+ fatalIfError (t , err )
85+ tags , err := remote .List (ref .Context ())
86+ iter := (i + 3 ) * 15000
87+ for r := 0 ; r <= iter ; r ++ {
88+ fatalIfError (t , err )
89+ db .SetTags (fmt .Sprintf ("%s-%d" , repo , r ), tags [0 :len (tags )- i ])
90+ // time.Sleep(time.Millisecond)
91+ }
92+ t .Logf ("%s %d: %d repos" , repo , i , iter )
93+ }
94+ time .Sleep (time .Millisecond * 100 )
95+ }
96+
97+ cancel ()
98+ t .Log ("waiting for GC stop" )
99+ select {
100+ case <- time .NewTimer (30 * time .Second ).C :
101+ t .Fatalf ("GC did not stop" )
102+ case <- stop :
103+ t .Log ("GC Stopped" )
104+ }
105+ }
106+
107+ type badgerTestLogger struct {
108+ logger logr.Logger
109+ }
110+
111+ func (l * badgerTestLogger ) Errorf (f string , v ... interface {}) {
112+ l .logger .Error (errors .Errorf ("ERROR: " + f , v ... ), f )
113+ }
114+ func (l * badgerTestLogger ) Infof (f string , v ... interface {}) {
115+ l .log ("INFO" , f , v ... )
116+ }
117+ func (l * badgerTestLogger ) Warningf (f string , v ... interface {}) {
118+ l .log ("WARNING" , f , v ... )
119+ }
120+ func (l * badgerTestLogger ) Debugf (f string , v ... interface {}) {
121+ l .log ("DEBUG" , f , v ... )
122+ }
123+
124+ var filter = regexp .MustCompile (`writeRequests called. Writing to value log|2 entries written|Writing to memtable|Sending updates to subscribers|Found value log max|fid:|Moved: 0|Processed 0 entries in 0 loops|Discard stats: map` )
125+
126+ func (l * badgerTestLogger ) log (lvl string , f string , v ... interface {}) {
127+ str := fmt .Sprintf (lvl + ": " + f , v ... )
128+ if filter .MatchString (str ) {
129+ return
130+ }
131+ l .logger .Info (str )
132+ }
133+
62134func createBadgerDatabaseForGC (t * testing.T ) (* badger.DB , * BadgerDatabase ) {
63135 t .Helper ()
64136 dir , err := os .MkdirTemp (os .TempDir (), t .Name ())
65137 if err != nil {
66138 t .Fatal (err )
67139 }
68- db , err := badger .Open (badger .DefaultOptions (dir ))
140+ opts := badger .DefaultOptions (dir )
141+ opts = opts .WithValueThreshold (100 ) // force values into the vlog files
142+ opts = opts .WithValueLogMaxEntries (1000 ) // force many vlogs to be created
143+ opts = opts .WithValueLogFileSize (32 << 19 )
144+ opts = opts .WithMemTableSize (16 << 19 ) // fill up memtables quickly
145+ opts = opts .WithNumMemtables (1 )
146+ opts = opts .WithNumLevelZeroTables (1 ) // hold fewer memtables in memory
147+ opts = opts .WithLogger (& badgerTestLogger {logger : testr .NewWithOptions (t , testr.Options {LogTimestamp : true })})
148+ // opts = opts.WithLoggingLevel(badger.DEBUG)
149+ db , err := badger .Open (opts )
69150 if err != nil {
70151 t .Fatal (err )
71152 }
0 commit comments