github.com/klaytn/klaytn@v1.10.2/storage/database/badger_database.go (about) 1 // Copyright 2018 The klaytn Authors 2 // This file is part of the klaytn library. 3 // 4 // The klaytn library is free software: you can redistribute it and/or modify 5 // it under the terms of the GNU Lesser General Public License as published by 6 // the Free Software Foundation, either version 3 of the License, or 7 // (at your option) any later version. 8 // 9 // The klaytn library is distributed in the hope that it will be useful, 10 // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 // GNU Lesser General Public License for more details. 13 // 14 // You should have received a copy of the GNU Lesser General Public License 15 // along with the klaytn library. If not, see <http://www.gnu.org/licenses/>. 16 17 package database 18 19 import ( 20 "fmt" 21 "os" 22 "time" 23 24 "github.com/dgraph-io/badger" 25 "github.com/klaytn/klaytn/log" 26 ) 27 28 const ( 29 gcThreshold = int64(1 << 30) // GB 30 sizeGCTickerTime = 1 * time.Minute 31 ) 32 33 type badgerDB struct { 34 fn string // filename for reporting 35 db *badger.DB 36 37 gcTicker *time.Ticker // runs periodically and runs gc if db size exceeds the threshold. 38 closeCh chan struct{} // stops gc go routine when db closes. 39 40 logger log.Logger // Contextual logger tracking the database path 41 } 42 43 func getBadgerDBOptions(dbDir string) badger.Options { 44 opts := badger.DefaultOptions(dbDir) 45 return opts 46 } 47 48 func NewBadgerDB(dbDir string) (*badgerDB, error) { 49 localLogger := logger.NewWith("dbDir", dbDir) 50 51 if fi, err := os.Stat(dbDir); err == nil { 52 if !fi.IsDir() { 53 return nil, fmt.Errorf("failed to make badgerDB while checking dbDir. Given dbDir is not a directory. dbDir: %v", dbDir) 54 } 55 } else if os.IsNotExist(err) { 56 if err := os.MkdirAll(dbDir, 0o755); err != nil { 57 return nil, fmt.Errorf("failed to make badgerDB while making dbDir. dbDir: %v, err: %v", dbDir, err) 58 } 59 } else { 60 return nil, fmt.Errorf("failed to make badgerDB while checking dbDir. dbDir: %v, err: %v", dbDir, err) 61 } 62 63 opts := getBadgerDBOptions(dbDir) 64 db, err := badger.Open(opts) 65 if err != nil { 66 return nil, fmt.Errorf("failed to make badgerDB while opening the DB. dbDir: %v, err: %v", dbDir, err) 67 } 68 69 badger := &badgerDB{ 70 fn: dbDir, 71 db: db, 72 logger: localLogger, 73 gcTicker: time.NewTicker(sizeGCTickerTime), 74 closeCh: make(chan struct{}), 75 } 76 77 go badger.runValueLogGC() 78 79 return badger, nil 80 } 81 82 // runValueLogGC runs gc for two cases. 83 // It periodically checks the size of value log and runs gc if it exceeds gcThreshold. 84 func (bg *badgerDB) runValueLogGC() { 85 _, lastValueLogSize := bg.db.Size() 86 87 for { 88 select { 89 case <-bg.closeCh: 90 bg.logger.Debug("Stopped value log GC", "dbDir", bg.fn) 91 return 92 case <-bg.gcTicker.C: 93 _, currValueLogSize := bg.db.Size() 94 if currValueLogSize-lastValueLogSize < gcThreshold { 95 continue 96 } 97 98 err := bg.db.RunValueLogGC(0.5) 99 if err != nil { 100 bg.logger.Error("Error while runValueLogGC()", "err", err) 101 continue 102 } 103 104 _, lastValueLogSize = bg.db.Size() 105 } 106 } 107 } 108 109 func (bg *badgerDB) Type() DBType { 110 return BadgerDB 111 } 112 113 // Path returns the path to the database directory. 114 func (bg *badgerDB) Path() string { 115 return bg.fn 116 } 117 118 // Put inserts the given key and value pair to the database. 119 func (bg *badgerDB) Put(key []byte, value []byte) error { 120 txn := bg.db.NewTransaction(true) 121 defer txn.Discard() 122 err := txn.Set(key, value) 123 if err != nil { 124 return err 125 } 126 return txn.Commit() 127 } 128 129 // Has returns true if the corresponding value to the given key exists. 130 func (bg *badgerDB) Has(key []byte) (bool, error) { 131 txn := bg.db.NewTransaction(false) 132 defer txn.Discard() 133 item, err := txn.Get(key) 134 if err != nil { 135 return false, err 136 } 137 err = item.Value(nil) 138 return err == nil, err 139 } 140 141 // Get returns the corresponding value to the given key if exists. 142 func (bg *badgerDB) Get(key []byte) ([]byte, error) { 143 txn := bg.db.NewTransaction(false) 144 defer txn.Discard() 145 item, err := txn.Get(key) 146 if err != nil { 147 if err == badger.ErrKeyNotFound { 148 return nil, dataNotFoundErr 149 } 150 return nil, err 151 } 152 153 var valCopy []byte 154 err = item.Value(func(val []byte) error { 155 valCopy = append([]byte{}, val...) 156 return nil 157 }) 158 return valCopy, err 159 } 160 161 // Delete deletes the key from the queue and database 162 func (bg *badgerDB) Delete(key []byte) error { 163 txn := bg.db.NewTransaction(true) 164 defer txn.Discard() 165 err := txn.Delete(key) 166 if err != nil { 167 return err 168 } 169 return txn.Commit() 170 } 171 172 func (bg *badgerDB) NewIterator(prefix []byte, start []byte) Iterator { 173 // txn := bg.db.NewTransaction(false) 174 // return txn.NewIterator(badger.DefaultIteratorOptions) 175 logger.CritWithStack("badgerDB doesn't support NewIterator") 176 return nil 177 } 178 179 func (bg *badgerDB) Close() { 180 close(bg.closeCh) 181 err := bg.db.Close() 182 if err == nil { 183 bg.logger.Info("Database closed") 184 } else { 185 bg.logger.Error("Failed to close database", "err", err) 186 } 187 } 188 189 func (bg *badgerDB) LDB() *badger.DB { 190 return bg.db 191 } 192 193 func (bg *badgerDB) NewBatch() Batch { 194 txn := bg.db.NewTransaction(true) 195 return &badgerBatch{db: bg.db, txn: txn} 196 } 197 198 func (bg *badgerDB) Meter(prefix string) { 199 logger.Warn("badgerDB does not support metrics!") 200 } 201 202 type badgerBatch struct { 203 db *badger.DB 204 txn *badger.Txn 205 size int 206 } 207 208 // Put inserts the given value into the batch for later committing. 209 func (b *badgerBatch) Put(key, value []byte) error { 210 err := b.txn.Set(key, value) 211 b.size += len(value) 212 return err 213 } 214 215 // Delete inserts the a key removal into the batch for later committing. 216 func (b *badgerBatch) Delete(key []byte) error { 217 if err := b.txn.Delete(key); err != nil { 218 return err 219 } 220 b.size += 1 221 return nil 222 } 223 224 // Write flushes any accumulated data to disk. 225 func (b *badgerBatch) Write() error { 226 return b.txn.Commit() 227 } 228 229 // ValueSize retrieves the amount of data queued up for writing. 230 func (b *badgerBatch) ValueSize() int { 231 return b.size 232 } 233 234 // Replay replays the batch contents. 235 func (b *badgerBatch) Reset() { 236 b.txn = b.db.NewTransaction(true) 237 b.size = 0 238 } 239 240 // Replay replays the batch contents. 241 func (b *badgerBatch) Replay(w KeyValueWriter) error { 242 logger.CritWithStack("Replay is not implemented in badgerBatch!") 243 return nil 244 }