github.com/ethereumproject/go-ethereum@v5.5.2+incompatible/ethdb/database.go (about) 1 // Copyright 2014 The go-ethereum Authors 2 // This file is part of the go-ethereum library. 3 // 4 // The go-ethereum 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 go-ethereum 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 go-ethereum library. If not, see <http://www.gnu.org/licenses/>. 16 17 package ethdb 18 19 import ( 20 "path/filepath" 21 22 "strconv" 23 24 "github.com/ethereumproject/go-ethereum/logger" 25 "github.com/ethereumproject/go-ethereum/logger/glog" 26 "github.com/syndtr/goleveldb/leveldb" 27 "github.com/syndtr/goleveldb/leveldb/errors" 28 "github.com/syndtr/goleveldb/leveldb/filter" 29 "github.com/syndtr/goleveldb/leveldb/iterator" 30 "github.com/syndtr/goleveldb/leveldb/opt" 31 ldbutil "github.com/syndtr/goleveldb/leveldb/util" 32 "sync" 33 ) 34 35 var OpenFileLimit = 64 36 37 // cacheRatio specifies how the total allotted cache is distributed between the 38 // various system databases. 39 var cacheRatio = map[string]float64{ 40 "dapp": 0.0, 41 "chaindata": 1.0, 42 } 43 44 // handleRatio specifies how the total alloted file descriptors is distributed 45 // between the various system databases. 46 var handleRatio = map[string]float64{ 47 "dapp": 0.0, 48 "chaindata": 1.0, 49 } 50 51 func SetCacheRatio(db string, ratio float64) { 52 cacheRatio[db] = ratio 53 } 54 55 func SetHandleRatio(db string, ratio float64) { 56 handleRatio[db] = ratio 57 } 58 59 type LDBDatabase struct { 60 file string 61 db *leveldb.DB 62 63 quitLock sync.Mutex // Mutex protecting the quit channel access 64 quitChan chan chan error // Quit channel to stop the metrics collection before closing the database 65 } 66 67 // NewLDBDatabase returns a LevelDB wrapped object. 68 func NewLDBDatabase(file string, cache int, handles int) (*LDBDatabase, error) { 69 // Calculate the cache and file descriptor allowance for this particular database 70 cache = int(float64(cache) * cacheRatio[filepath.Base(file)]) 71 if cache < 16 { 72 cache = 16 73 } 74 handles = int(float64(handles) * handleRatio[filepath.Base(file)]) 75 if handles < 16 { 76 handles = 16 77 } 78 glog.V(logger.Info).Infof("Allotted %dMB cache and %d file handles to %s", cache, handles, file) 79 glog.D(logger.Warn).Infof("Allotted %s cache and %s file handles to %s", logger.ColorGreen(strconv.Itoa(cache)+"MB"), logger.ColorGreen(strconv.Itoa(handles)), logger.ColorGreen(file)) 80 81 // Open the db and recover any potential corruptions 82 db, err := leveldb.OpenFile(file, &opt.Options{ 83 OpenFilesCacheCapacity: handles, 84 BlockCacheCapacity: cache / 2 * opt.MiB, 85 WriteBuffer: cache / 4 * opt.MiB, // Two of these are used internally 86 Filter: filter.NewBloomFilter(10), 87 }) 88 if _, corrupted := err.(*errors.ErrCorrupted); corrupted { 89 db, err = leveldb.RecoverFile(file, nil) 90 } 91 // (Re)check for errors and abort if opening of the db failed 92 if err != nil { 93 return nil, err 94 } 95 return &LDBDatabase{ 96 file: file, 97 db: db, 98 }, nil 99 } 100 101 // Path returns the path to the database directory. 102 func (db *LDBDatabase) Path() string { 103 return db.file 104 } 105 106 // Put puts the given key / value to the queue 107 func (self *LDBDatabase) Put(key []byte, value []byte) error { 108 return self.db.Put(key, value, nil) 109 } 110 111 // Get returns the given key if it's present. 112 func (self *LDBDatabase) Get(key []byte) ([]byte, error) { 113 // Retrieve the key and increment the miss counter if not found 114 dat, err := self.db.Get(key, nil) 115 if err != nil { 116 return nil, err 117 } 118 return dat, nil 119 } 120 121 func (db *LDBDatabase) Has(key []byte) (bool, error) { 122 return db.db.Has(key, nil) 123 } 124 125 // Delete deletes the key from the queue and database 126 func (self *LDBDatabase) Delete(key []byte) error { 127 // Execute the actual operation 128 return self.db.Delete(key, nil) 129 } 130 131 func (self *LDBDatabase) NewIterator() iterator.Iterator { 132 return self.db.NewIterator(nil, nil) 133 } 134 135 func (self *LDBDatabase) NewIteratorRange(slice *ldbutil.Range) iterator.Iterator { 136 return self.db.NewIterator(slice, nil) 137 } 138 139 func NewBytesPrefix(prefix []byte) *ldbutil.Range { 140 return ldbutil.BytesPrefix(prefix) 141 } 142 143 func (self *LDBDatabase) Close() { 144 if err := self.db.Close(); err != nil { 145 glog.Errorf("eth: DB %s: %s", self.file, err) 146 } 147 } 148 149 func (self *LDBDatabase) LDB() *leveldb.DB { 150 return self.db 151 } 152 153 // TODO: remove this stuff and expose leveldb directly 154 155 func (db *LDBDatabase) NewBatch() Batch { 156 return &ldbBatch{db: db.db, b: new(leveldb.Batch)} 157 } 158 159 type ldbBatch struct { 160 db *leveldb.DB 161 b *leveldb.Batch 162 size int 163 } 164 165 func (b *ldbBatch) Put(key, value []byte) error { 166 b.b.Put(key, value) 167 b.size += len(value) 168 return nil 169 } 170 171 func (b *ldbBatch) Write() error { 172 return b.db.Write(b.b, nil) 173 } 174 175 func (b *ldbBatch) ValueSize() int { 176 return b.size 177 } 178 179 type table struct { 180 db Database 181 prefix string 182 } 183 184 // NewTable returns a Database object that prefixes all keys with a given 185 // string. 186 func NewTable(db Database, prefix string) Database { 187 return &table{ 188 db: db, 189 prefix: prefix, 190 } 191 } 192 193 func (dt *table) Put(key []byte, value []byte) error { 194 return dt.db.Put(append([]byte(dt.prefix), key...), value) 195 } 196 197 func (dt *table) Has(key []byte) (bool, error) { 198 return dt.db.Has(append([]byte(dt.prefix), key...)) 199 } 200 201 func (dt *table) Get(key []byte) ([]byte, error) { 202 return dt.db.Get(append([]byte(dt.prefix), key...)) 203 } 204 205 func (dt *table) Delete(key []byte) error { 206 return dt.db.Delete(append([]byte(dt.prefix), key...)) 207 } 208 209 func (dt *table) Close() { 210 // Do nothing; don't close the underlying DB. 211 } 212 213 type tableBatch struct { 214 batch Batch 215 prefix string 216 } 217 218 // NewTableBatch returns a Batch object which prefixes all keys with a given string. 219 func NewTableBatch(db Database, prefix string) Batch { 220 return &tableBatch{db.NewBatch(), prefix} 221 } 222 223 func (dt *table) NewBatch() Batch { 224 return &tableBatch{dt.db.NewBatch(), dt.prefix} 225 } 226 227 func (tb *tableBatch) Put(key, value []byte) error { 228 return tb.batch.Put(append([]byte(tb.prefix), key...), value) 229 } 230 231 func (tb *tableBatch) Write() error { 232 return tb.batch.Write() 233 } 234 235 func (tb *tableBatch) ValueSize() int { 236 return tb.batch.ValueSize() 237 }