@@ -15,7 +15,12 @@ import (
1515)
1616
1717// Cleaner uses component.ComponentManager to implement module.Startable and module.ReadyDoneAware
18- // to run an internal goroutine which run badger value log garbage collection on timely basis.
18+ // to run an internal goroutine which run badger value log garbage collection at a semi-regular interval.
19+ // The Cleaner exists for 2 reasons:
20+ // - Run GC frequently enough that each GC is relatively inexpensive
21+ // - Avoid GC being synchronized across all nodes. Since in the happy path, all nodes have very similar
22+ // database load patterns, without intervention they are likely to schedule GC at the same time, which
23+ // can cause temporary consensus halts.
1924type Cleaner struct {
2025 * component.ComponentManager
2126 log zerolog.Logger
@@ -57,6 +62,7 @@ func NewCleaner(log zerolog.Logger, db *badger.DB, metrics module.CleanerMetrics
5762func (c * Cleaner ) gcWorkerRoutine (ctx irrecoverable.SignalerContext , ready component.ReadyFunc ) {
5863 ready ()
5964 ticker := time .NewTicker (c .nextWaitDuration ())
65+ defer ticker .Stop ()
6066 for {
6167 select {
6268 case <- ctx .Done ():
@@ -71,8 +77,8 @@ func (c *Cleaner) gcWorkerRoutine(ctx irrecoverable.SignalerContext, ready compo
7177}
7278
7379// nextWaitDuration calculates next duration for Cleaner to wait before attempting to run GC.
74- // We add 20% jitter into the interval, so that we don't risk nodes syncing
75- // up on their GC calls over time.
80+ // We add 20% jitter into the interval, so that we don't risk nodes syncing their GC calls over time.
81+ // Therefore GC is run every X seconds, where X is uniformly sampled from [interval, interval*1.2]
7682func (c * Cleaner ) nextWaitDuration () time.Duration {
7783 return time .Duration (c .interval .Milliseconds () + rand .Int63n (c .interval .Milliseconds ()/ 5 ))
7884}
0 commit comments