github.com/klaytn/klaytn@v1.12.1/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 "github.com/pkg/errors" 27 ) 28 29 const ( 30 gcThreshold = int64(1 << 30) // GB 31 sizeGCTickerTime = 1 * time.Minute 32 ) 33 34 type badgerDB struct { 35 fn string // filename for reporting 36 db *badger.DB 37 38 gcTicker *time.Ticker // runs periodically and runs gc if db size exceeds the threshold. 39 closeCh chan struct{} // stops gc go routine when db closes. 40 41 logger log.Logger // Contextual logger tracking the database path 42 } 43 44 func getBadgerDBOptions(dbDir string) badger.Options { 45 opts := badger.DefaultOptions(dbDir) 46 return opts 47 } 48 49 func NewBadgerDB(dbDir string) (*badgerDB, error) { 50 localLogger := logger.NewWith("dbDir", dbDir) 51 52 if fi, err := os.Stat(dbDir); err == nil { 53 if !fi.IsDir() { 54 return nil, fmt.Errorf("failed to make badgerDB while checking dbDir. Given dbDir is not a directory. dbDir: %v", dbDir) 55 } 56 } else if os.IsNotExist(err) { 57 if err := os.MkdirAll(dbDir, 0o755); err != nil { 58 return nil, fmt.Errorf("failed to make badgerDB while making dbDir. dbDir: %v, err: %v", dbDir, err) 59 } 60 } else { 61 return nil, fmt.Errorf("failed to make badgerDB while checking dbDir. dbDir: %v, err: %v", dbDir, err) 62 } 63 64 opts := getBadgerDBOptions(dbDir) 65 db, err := badger.Open(opts) 66 if err != nil { 67 return nil, fmt.Errorf("failed to make badgerDB while opening the DB. dbDir: %v, err: %v", dbDir, err) 68 } 69 70 badger := &badgerDB{ 71 fn: dbDir, 72 db: db, 73 logger: localLogger, 74 gcTicker: time.NewTicker(sizeGCTickerTime), 75 closeCh: make(chan struct{}), 76 } 77 78 go badger.runValueLogGC() 79 80 return badger, nil 81 } 82 83 // runValueLogGC runs gc for two cases. 84 // It periodically checks the size of value log and runs gc if it exceeds gcThreshold. 85 func (bg *badgerDB) runValueLogGC() { 86 _, lastValueLogSize := bg.db.Size() 87 88 for { 89 select { 90 case <-bg.closeCh: 91 bg.logger.Debug("Stopped value log GC", "dbDir", bg.fn) 92 return 93 case <-bg.gcTicker.C: 94 _, currValueLogSize := bg.db.Size() 95 if currValueLogSize-lastValueLogSize < gcThreshold { 96 continue 97 } 98 99 err := bg.db.RunValueLogGC(0.5) 100 if err != nil { 101 bg.logger.Error("Error while runValueLogGC()", "err", err) 102 continue 103 } 104 105 _, lastValueLogSize = bg.db.Size() 106 } 107 } 108 } 109 110 func (bg *badgerDB) Type() DBType { 111 return BadgerDB 112 } 113 114 // Path returns the path to the database directory. 115 func (bg *badgerDB) Path() string { 116 return bg.fn 117 } 118 119 // Put inserts the given key and value pair to the database. 120 func (bg *badgerDB) Put(key []byte, value []byte) error { 121 txn := bg.db.NewTransaction(true) 122 defer txn.Discard() 123 err := txn.Set(key, value) 124 if err != nil { 125 return err 126 } 127 return txn.Commit() 128 } 129 130 // Has returns true if the corresponding value to the given key exists. 131 func (bg *badgerDB) Has(key []byte) (bool, error) { 132 txn := bg.db.NewTransaction(false) 133 defer txn.Discard() 134 item, err := txn.Get(key) 135 if err != nil { 136 if err == badger.ErrKeyNotFound { 137 return false, nil 138 } 139 return false, err 140 } 141 err = item.Value(nil) 142 return err == nil, err 143 } 144 145 // Get returns the corresponding value to the given key if exists. 146 func (bg *badgerDB) Get(key []byte) ([]byte, error) { 147 txn := bg.db.NewTransaction(false) 148 defer txn.Discard() 149 item, err := txn.Get(key) 150 if err != nil { 151 if err == badger.ErrKeyNotFound { 152 return nil, dataNotFoundErr 153 } 154 return nil, err 155 } 156 157 var valCopy []byte 158 err = item.Value(func(val []byte) error { 159 valCopy = append([]byte{}, val...) 160 return nil 161 }) 162 return valCopy, err 163 } 164 165 // Delete deletes the key from the queue and database 166 func (bg *badgerDB) Delete(key []byte) error { 167 txn := bg.db.NewTransaction(true) 168 defer txn.Discard() 169 err := txn.Delete(key) 170 if err != nil { 171 return err 172 } 173 return txn.Commit() 174 } 175 176 func (bg *badgerDB) NewIterator(prefix []byte, start []byte) Iterator { 177 // txn := bg.db.NewTransaction(false) 178 // return txn.NewIterator(badger.DefaultIteratorOptions) 179 logger.CritWithStack("badgerDB doesn't support NewIterator") 180 return nil 181 } 182 183 func (bg *badgerDB) Close() { 184 close(bg.closeCh) 185 err := bg.db.Close() 186 if err == nil { 187 bg.logger.Info("Database closed") 188 } else { 189 bg.logger.Error("Failed to close database", "err", err) 190 } 191 } 192 193 func (bg *badgerDB) LDB() *badger.DB { 194 return bg.db 195 } 196 197 func (bg *badgerDB) NewBatch() Batch { 198 txn := bg.db.NewTransaction(true) 199 return &badgerBatch{db: bg.db, txn: txn} 200 } 201 202 func (bg *badgerDB) Meter(prefix string) { 203 logger.Warn("badgerDB does not support metrics!") 204 } 205 206 func (bg *badgerDB) TryCatchUpWithPrimary() error { 207 return nil 208 } 209 210 type badgerBatch struct { 211 db *badger.DB 212 txn *badger.Txn 213 size int 214 } 215 216 // Put inserts the given value into the batch for later committing. 217 func (b *badgerBatch) Put(key, value []byte) error { 218 err := b.txn.Set(key, value) 219 b.size += len(value) 220 return err 221 } 222 223 // Delete inserts the a key removal into the batch for later committing. 224 func (b *badgerBatch) Delete(key []byte) error { 225 if err := b.txn.Delete(key); err != nil { 226 return err 227 } 228 b.size += 1 229 return nil 230 } 231 232 // Write flushes any accumulated data to disk. 233 func (b *badgerBatch) Write() error { 234 return b.txn.Commit() 235 } 236 237 // ValueSize retrieves the amount of data queued up for writing. 238 func (b *badgerBatch) ValueSize() int { 239 return b.size 240 } 241 242 // Replay replays the batch contents. 243 func (b *badgerBatch) Reset() { 244 b.txn = b.db.NewTransaction(true) 245 b.size = 0 246 } 247 248 func (b *badgerBatch) Release() { 249 // nothing to do with badgerBatch 250 } 251 252 // Replay replays the batch contents. 253 func (b *badgerBatch) Replay(w KeyValueWriter) error { 254 logger.CritWithStack("Replay is not implemented in badgerBatch!") 255 return nil 256 } 257 258 func (bg *badgerDB) Stat(property string) (string, error) { 259 return "", errors.New("unknown property") 260 } 261 262 func (bg *badgerDB) Compact(start []byte, limit []byte) error { 263 return nil 264 }