@@ -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/v4"
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 ) {
@@ -60,13 +66,88 @@ func TestBadgerGarbageCollectorDoesStop(t *testing.T) {
6066 }
6167}
6268
69+ func TestBadgerGCLoad (t * testing.T ) {
70+ badger , db := createBadgerDatabaseForGC (t )
71+ ctx , cancel := context .WithCancel (
72+ logr .NewContext (context .Background (), testr .NewWithOptions (t , testr.Options {Verbosity : 1 , LogTimestamp : true })))
73+
74+ stop := make (chan struct {})
75+ go func () {
76+ gc := NewBadgerGarbageCollector ("loaded-badger-gc" , badger , time .Second * 20 , 0.7 )
77+ gc .Start (ctx )
78+ stop <- struct {}{}
79+ }()
80+
81+ repos := []string {"alpine" , "node" , "postgres" , "debian" }
82+ for i := 5 ; i >= 0 ; i -- {
83+ for _ , repo := range repos {
84+ ref , err := name .ParseReference (repo )
85+ fatalIfError (t , err )
86+ tags , err := remote .List (ref .Context ())
87+ iter := (i + 3 ) * 15000
88+ for r := 0 ; r <= iter ; r ++ {
89+ fatalIfError (t , err )
90+ db .SetTags (fmt .Sprintf ("%s-%d" , repo , r ), tags [0 :len (tags )- i ])
91+ // time.Sleep(time.Millisecond)
92+ }
93+ t .Logf ("%s %d: %d repos" , repo , i , iter )
94+ }
95+ time .Sleep (time .Millisecond * 100 )
96+ }
97+
98+ cancel ()
99+ t .Log ("waiting for GC stop" )
100+ select {
101+ case <- time .NewTimer (30 * time .Second ).C :
102+ t .Fatalf ("GC did not stop" )
103+ case <- stop :
104+ t .Log ("GC Stopped" )
105+ }
106+ }
107+
108+ type badgerTestLogger struct {
109+ logger logr.Logger
110+ }
111+
112+ func (l * badgerTestLogger ) Errorf (f string , v ... interface {}) {
113+ l .logger .Error (errors .Errorf ("ERROR: " + f , v ... ), f )
114+ }
115+ func (l * badgerTestLogger ) Infof (f string , v ... interface {}) {
116+ l .log ("INFO" , f , v ... )
117+ }
118+ func (l * badgerTestLogger ) Warningf (f string , v ... interface {}) {
119+ l .log ("WARNING" , f , v ... )
120+ }
121+ func (l * badgerTestLogger ) Debugf (f string , v ... interface {}) {
122+ l .log ("DEBUG" , f , v ... )
123+ }
124+
125+ 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` )
126+
127+ func (l * badgerTestLogger ) log (lvl string , f string , v ... interface {}) {
128+ str := fmt .Sprintf (lvl + ": " + f , v ... )
129+ if filter .MatchString (str ) {
130+ return
131+ }
132+ l .logger .Info (str )
133+ }
134+
63135func createBadgerDatabaseForGC (t * testing.T ) (* badger.DB , * BadgerDatabase ) {
64136 t .Helper ()
65137 dir , err := os .MkdirTemp (os .TempDir (), t .Name ())
66138 if err != nil {
67139 t .Fatal (err )
68140 }
69- db , err := badger .Open (badger .DefaultOptions (dir ))
141+ opts := badger .DefaultOptions (dir )
142+ opts = opts .WithValueThreshold (100 ) // force values into the vlog files
143+ opts = opts .WithValueLogMaxEntries (1000 ) // force many vlogs to be created
144+ opts = opts .WithValueLogFileSize (32 << 19 )
145+ opts = opts .WithMemTableSize (16 << 19 ) // fill up memtables quickly
146+ opts = opts .WithNumMemtables (1 )
147+ opts = opts .WithNumLevelZeroTables (1 ) // hold fewer memtables in memory
148+ opts = opts .WithLogger (& badgerTestLogger {logger : testr .NewWithOptions (t , testr.Options {LogTimestamp : true })})
149+ // opts = opts.WithLoggingLevel(badger.DEBUG)
150+ db , err := badger .Open (opts )
70151 if err != nil {
71152 t .Fatal (err )
72153 }
0 commit comments