Skip to content

Commit ddc430c

Browse files
committed
Add periodic garbage collector for key-value store
Signed-off-by: Anthony Floeder <anthony.floeder@hpe.com>
1 parent 233d628 commit ddc430c

4 files changed

Lines changed: 100 additions & 16 deletions

File tree

pkg/manager-nnf/manager.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -500,6 +500,7 @@ func (s *StorageService) Initialize(log ec.Logger, ctrl NnfControllerInterface)
500500
// Create the key-value storage database
501501
{
502502
path := "nnf.db"
503+
persistent.SetLogger(log)
503504
s.store, err = persistent.Open(path, false)
504505
if err != nil {
505506
log.Error(err, "Unable to open database", "path", path)

pkg/persistent/storage_api.go

Lines changed: 25 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2022 Hewlett Packard Enterprise Development LP
2+
* Copyright 2022-2025 Hewlett Packard Enterprise Development LP
33
* Other additional copyright holders may be indicated within.
44
*
55
* The entirety of this work is licensed under the Apache License,
@@ -17,15 +17,37 @@
1717
* limitations under the License.
1818
*/
1919

20+
// Package persistent provides persistent storage functionality for key-value operations.
21+
// This package implements durable storage mechanisms with support for various backends.
2022
package persistent
2123

24+
import (
25+
"github.com/NearNodeFlash/nnf-ec/pkg/ec"
26+
"github.com/go-logr/logr"
27+
)
28+
29+
// Package-level logger that can be configured
30+
var packageLogger ec.Logger = logr.Discard()
31+
32+
// SetLogger configures the package-level logger for all persistent storage operations
33+
func SetLogger(log ec.Logger) {
34+
packageLogger = log.WithName("persistent")
35+
}
36+
37+
// GetLogger returns the current package logger
38+
func GetLogger() ec.Logger {
39+
return packageLogger
40+
}
41+
42+
// StorageProvider is the default storage provider instance for the package
2243
var StorageProvider = NewLocalPersistentStorageProvider()
2344

45+
// PersistentStorageProvider provides methods for creating persistent storage interfaces
2446
type PersistentStorageProvider interface {
2547
NewPersistentStorageInterface(path string, readOnly bool) (PersistentStorageApi, error)
2648
}
2749

28-
// Persistent Storage API provides an interface for interacting with persistent storage
50+
// PersistentStorageApi provides an interface for interacting with persistent storage.
2951
type PersistentStorageApi interface {
3052
View(func(txn PersistentStorageTransactionApi) error) error
3153
Update(func(txn PersistentStorageTransactionApi) error) error
@@ -34,7 +56,7 @@ type PersistentStorageApi interface {
3456
Close() error
3557
}
3658

37-
// Persistent Storage Transaction API provides an interface for interacting with persistent storage transactions
59+
// PersistentStorageTransactionApi provides an interface for interacting with persistent storage transactions
3860
type PersistentStorageTransactionApi interface {
3961
NewIterator(prefix string) PersistentStorageIteratorApi
4062
Set(key string, value []byte) error

pkg/persistent/storage_json.go

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2022 Hewlett Packard Enterprise Development LP
2+
* Copyright 2022-2025 Hewlett Packard Enterprise Development LP
33
* Other additional copyright holders may be indicated within.
44
*
55
* The entirety of this work is licensed under the Apache License,
@@ -22,7 +22,6 @@ package persistent
2222
import (
2323
"encoding/json"
2424
"io/ioutil"
25-
2625
)
2726

2827
func NewJsonFilePersistentStorageProvider(filename string) PersistentStorageProvider {
@@ -34,16 +33,22 @@ type jsonFilePersisentStorageProvider struct {
3433
}
3534

3635
func (p *jsonFilePersisentStorageProvider) NewPersistentStorageInterface(name string, readOnly bool) (PersistentStorageApi, error) {
36+
log := GetLogger()
37+
log.Info("Opening JSON file storage", "file", p.filename, "name", name, "readOnly", readOnly)
38+
3739
content, err := ioutil.ReadFile(p.filename)
3840
if err != nil {
41+
log.Error(err, "Failed to read JSON file", "file", p.filename)
3942
return nil, err
4043
}
4144

4245
var payload map[string]map[string]string
4346
if err := json.Unmarshal(content, &payload); err != nil {
47+
log.Error(err, "Failed to unmarshal JSON content", "file", p.filename)
4448
return nil, err
4549
}
4650

51+
log.Info("Successfully opened JSON file storage", "name", name)
4752
return &jsonPersistentStorageInterface{data: payload[name]}, nil
4853
}
4954

@@ -66,7 +71,3 @@ func (*jsonPersistentStorageInterface) Delete(key string) error {
6671
func (*jsonPersistentStorageInterface) Close() error {
6772
panic("unimplemented")
6873
}
69-
70-
71-
72-

pkg/persistent/storage_local.go

Lines changed: 67 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2022 Hewlett Packard Enterprise Development LP
2+
* Copyright 2022-2025 Hewlett Packard Enterprise Development LP
33
* Other additional copyright holders may be indicated within.
44
*
55
* The entirety of this work is licensed under the Apache License,
@@ -20,9 +20,13 @@
2020
package persistent
2121

2222
import (
23+
"time"
24+
2325
"github.com/dgraph-io/badger/v3"
2426
)
2527

28+
const garbageCollectPeriod = 24 * time.Hour
29+
2630
func NewLocalPersistentStorageProvider() PersistentStorageProvider {
2731
return &localPersistentStorageProvider{}
2832
}
@@ -40,27 +44,51 @@ type localPersistentStorage struct {
4044
}
4145

4246
func (s *localPersistentStorage) open(path string, readOnly bool) (err error) {
47+
log := GetLogger().WithValues("path", path, "readOnly", readOnly)
48+
log.Info("BadgerDB: Opening database")
49+
4350
opts := badger.DefaultOptions(path)
4451
opts.SyncWrites = true
45-
//opts.ReadOnly = readOnly // Causes ErrLogTruncate
4652
opts.BypassLockGuard = readOnly
53+
opts.VerifyValueChecksum = true
4754

48-
// Shrink the in-memory and on-disk size to a more manageable 8 MiB and 16 MiB, respectively;
55+
// Shrink the in-memory and on-disk size to a more manageable 8 MiB and 32 MiB, respectively;
4956
// We use very little data and the 64 MiB and 256 MiB defaults will cause OOM issues in kubernetes.
50-
// 8MiB seems to be the lower limit within badger, anything smaller and badger will complain with
57+
// 8MiB seems to be the lower limit within badger, anything smaller and badger complains with
5158
// """
5259
// Valuethreshold 1048576 greater than max batch size of 629145. Either reduce opt.ValueThreshold
5360
// or increase opt.MaxTableSize.
5461
// """
5562
opts.MemTableSize = 8 << 20
56-
opts.BlockCacheSize = 16 << 20
63+
opts.BlockCacheSize = 32 << 20 // Increased to 32 MiB for better cache hit ratio
5764

5865
s.DB, err = badger.Open(opts)
59-
return err
66+
if err != nil {
67+
log.Error(err, "BadgerDB: Failed to open database")
68+
return err
69+
}
70+
71+
log.WithValues("mem_table_size", opts.MemTableSize, "block_cache_size", opts.BlockCacheSize).Info("BadgerDB: Database opened successfully")
72+
73+
// Run garbage collection on existing database during initialization
74+
// Skip GC for read-only databases to avoid potential issues
75+
if !readOnly {
76+
s.RunPeriodicGC(garbageCollectPeriod)
77+
}
78+
79+
return nil
6080
}
6181

6282
func (s *localPersistentStorage) Close() error {
63-
return s.DB.Close()
83+
log := GetLogger()
84+
log.Info("BadgerDB: Closing database")
85+
err := s.DB.Close()
86+
if err != nil {
87+
log.Error(err, "BadgerDB: Failed to close database")
88+
} else {
89+
log.Info("BadgerDB: Database closed successfully")
90+
}
91+
return err
6492
}
6593

6694
func (s *localPersistentStorage) View(fn func(PersistentStorageTransactionApi) error) error {
@@ -84,6 +112,38 @@ func (s *localPersistentStorage) Delete(key string) error {
84112
return txn.Commit()
85113
}
86114

115+
func (s *localPersistentStorage) RunGC() error {
116+
log := GetLogger().WithName("gc")
117+
log.Info("BadgerDB: Starting garbage collection")
118+
119+
err := s.DB.RunValueLogGC(0.5)
120+
if err != nil {
121+
if err == badger.ErrNoRewrite {
122+
log.Info("BadgerDB: GC completed - no rewrite needed")
123+
return nil
124+
}
125+
log.Error(err, "BadgerDB: GC failed")
126+
return err
127+
}
128+
log.Info("BadgerDB: GC completed successfully")
129+
return nil
130+
}
131+
132+
func (s *localPersistentStorage) RunPeriodicGC(interval time.Duration) {
133+
log := GetLogger().WithName("periodic-gc").WithValues("interval", interval)
134+
log.Info("BadgerDB: Starting periodic GC")
135+
136+
ticker := time.NewTicker(interval)
137+
go func() {
138+
defer ticker.Stop()
139+
for range ticker.C {
140+
if err := s.RunGC(); err != nil {
141+
log.Error(err, "BadgerDB: Periodic GC encountered error")
142+
}
143+
}
144+
}()
145+
}
146+
87147
type localPersistentStorageTransaction struct {
88148
*badger.Txn
89149
}

0 commit comments

Comments
 (0)