Skip to content

Commit 9a4b843

Browse files
authored
fix: prevent badgerdb hang when closing without prior operations (#5074)
1 parent 57dd8c1 commit 9a4b843

File tree

2 files changed

+27
-9
lines changed

2 files changed

+27
-9
lines changed

services/dedup/badger/badger.go

+19-9
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package badger
22

33
import (
4+
"context"
45
"fmt"
56
"strconv"
67
"sync"
@@ -23,11 +24,12 @@ type BadgerDB struct {
2324
logger loggerForBadger
2425
badgerDB *badger.DB
2526
window config.ValueLoader[time.Duration]
26-
close chan struct{}
27-
gcDone chan struct{}
2827
path string
2928
opts badger.Options
3029
once sync.Once
30+
wg sync.WaitGroup
31+
bgCtx context.Context
32+
cancel context.CancelFunc
3133
}
3234

3335
// DefaultPath returns the default path for the deduplication service's badger DB
@@ -57,14 +59,16 @@ func NewBadgerDB(conf *config.Config, stats stats.Stats, path string) *Dedup {
5759
WithSyncWrites(conf.GetBool("BadgerDB.syncWrites", false)).
5860
WithDetectConflicts(conf.GetBool("BadgerDB.detectConflicts", false))
5961

62+
bgCtx, cancel := context.WithCancel(context.Background())
6063
db := &BadgerDB{
6164
stats: stats,
6265
logger: loggerForBadger{log},
6366
path: path,
64-
gcDone: make(chan struct{}),
65-
close: make(chan struct{}),
6667
window: dedupWindow,
6768
opts: badgerOpts,
69+
wg: sync.WaitGroup{},
70+
bgCtx: bgCtx,
71+
cancel: cancel,
6872
}
6973
return &Dedup{
7074
badgerDB: db,
@@ -114,9 +118,11 @@ func (d *BadgerDB) Set(kvs []types.KeyValue) error {
114118
}
115119

116120
func (d *BadgerDB) Close() {
117-
close(d.close)
118-
<-d.gcDone
119-
_ = d.badgerDB.Close()
121+
d.cancel()
122+
d.wg.Wait()
123+
if d.badgerDB != nil {
124+
_ = d.badgerDB.Close()
125+
}
120126
}
121127

122128
func (d *BadgerDB) init() error {
@@ -127,9 +133,10 @@ func (d *BadgerDB) init() error {
127133
if err != nil {
128134
return
129135
}
136+
d.wg.Add(1)
130137
rruntime.Go(func() {
138+
defer d.wg.Done()
131139
d.gcLoop()
132-
close(d.gcDone)
133140
})
134141
})
135142
return err
@@ -138,12 +145,15 @@ func (d *BadgerDB) init() error {
138145
func (d *BadgerDB) gcLoop() {
139146
for {
140147
select {
141-
case <-d.close:
148+
case <-d.bgCtx.Done():
142149
_ = d.badgerDB.RunValueLogGC(0.5)
143150
return
144151
case <-time.After(5 * time.Minute):
145152
}
146153
again:
154+
if d.bgCtx.Err() != nil {
155+
return
156+
}
147157
// One call would only result in removal of at max one log file.
148158
// As an optimization, you could also immediately re-run it whenever it returns nil error
149159
// (this is why `goto again` is used).

services/dedup/badger/badger_test.go

+8
Original file line numberDiff line numberDiff line change
@@ -46,3 +46,11 @@ func Test_Badger(t *testing.T) {
4646
require.False(t, found)
4747
})
4848
}
49+
50+
func TestBadgerClose(t *testing.T) {
51+
badger := NewBadgerDB(config.New(), stats.NOP, t.TempDir())
52+
require.NotNil(t, badger)
53+
54+
t.Log("close badger without any other operation")
55+
badger.Close()
56+
}

0 commit comments

Comments
 (0)