github.com/ronaksoft/rony@v0.16.26-0.20230807065236-1743dbfe6959/store/store.go (about) 1 package store 2 3 import ( 4 "path/filepath" 5 "time" 6 7 "github.com/dgraph-io/badger/v3" 8 "github.com/ronaksoft/rony" 9 "github.com/ronaksoft/rony/di" 10 "github.com/ronaksoft/rony/internal/metrics" 11 "github.com/ronaksoft/rony/tools" 12 ) 13 14 /* 15 Creation Time: 2021 - May - 16 16 Created by: (ehsan) 17 Maintainers: 18 1. Ehsan N. Moosa (E2) 19 Auditor: Ehsan N. Moosa (E2) 20 Copyright Ronak Software Group 2020 21 */ 22 23 var ( 24 vlogTicker *time.Ticker // runs every 1m, check size of vlog and run GC conditionally. 25 mandatoryVlogTicker *time.Ticker // runs every 10m, we always run vlog GC. 26 ) 27 28 type Store struct { 29 db *badger.DB 30 } 31 32 func New(cfg Config) (*Store, error) { 33 st := &Store{} 34 db, err := newDB(cfg) 35 if err != nil { 36 return nil, err 37 } 38 st.db = db 39 40 vlogTicker = time.NewTicker(time.Minute) 41 mandatoryVlogTicker = time.NewTicker(time.Minute * 10) 42 go runVlogGC(db, 1<<30) 43 44 return st, nil 45 } 46 47 func ProvideDI(cfg Config) { 48 di.MustProvide(func() (*Store, error) { 49 return New(cfg) 50 }) 51 } 52 53 func newDB(config Config) (*badger.DB, error) { 54 opt := badger.DefaultOptions(filepath.Join(config.DirPath, "badger")) 55 if config.InMemory { 56 opt.InMemory = config.InMemory 57 opt.ValueDir = "" 58 opt.Dir = "" 59 } 60 61 opt.Logger = nil 62 63 return badger.Open(opt) 64 } 65 66 func runVlogGC(db *badger.DB, threshold int64) { 67 // Get initial size on start. 68 _, lastVlogSize := db.Size() 69 70 runGC := func() { 71 var err error 72 for err == nil { 73 // If a GC is successful, immediately run it again. 74 err = db.RunValueLogGC(0.7) 75 } 76 _, lastVlogSize = db.Size() 77 } 78 79 for { 80 select { 81 case <-vlogTicker.C: 82 _, currentVlogSize := db.Size() 83 if currentVlogSize < lastVlogSize+threshold { 84 continue 85 } 86 runGC() 87 case <-mandatoryVlogTicker.C: 88 runGC() 89 } 90 } 91 } 92 93 func (s *Store) View(fn func(txn *rony.StoreTxn) error) error { 94 retry := defaultConflictRetries 95 Retry: 96 err := s.db.View(fn) 97 if err == badger.ErrConflict { 98 if retry--; retry > 0 { 99 metrics.IncCounter(metrics.CntStoreConflicts) 100 time.Sleep(time.Duration(tools.RandomInt64(int64(defaultMaxInterval)))) 101 102 goto Retry 103 } 104 } 105 106 return err 107 } 108 109 func (s *Store) Update(fn func(txn *rony.StoreTxn) error) error { 110 retry := defaultConflictRetries 111 Retry: 112 err := s.db.Update(fn) 113 if err == badger.ErrConflict { 114 if retry--; retry > 0 { 115 metrics.IncCounter(metrics.CntStoreConflicts) 116 time.Sleep(time.Duration(tools.RandomInt64(int64(defaultMaxInterval)))) 117 118 goto Retry 119 } 120 } 121 122 return err 123 } 124 125 func (s *Store) Shutdown() { 126 if vlogTicker != nil { 127 vlogTicker.Stop() 128 } 129 if mandatoryVlogTicker != nil { 130 mandatoryVlogTicker.Stop() 131 } 132 if s.db != nil { 133 _ = s.db.Close() 134 } 135 } 136 137 func (s *Store) LocalDB() *LocalDB { 138 return s.db 139 }