github.com/digdeepmining/go-atheios@v1.5.13-0.20180902133602-d5687a2e6f43/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 "strconv" 22 "strings" 23 "sync" 24 "time" 25 26 "github.com/atheioschain/go-atheios/logger" 27 "github.com/atheioschain/go-atheios/logger/glog" 28 "github.com/atheioschain/go-atheios/metrics" 29 "github.com/syndtr/goleveldb/leveldb" 30 "github.com/syndtr/goleveldb/leveldb/errors" 31 "github.com/syndtr/goleveldb/leveldb/filter" 32 "github.com/syndtr/goleveldb/leveldb/iterator" 33 "github.com/syndtr/goleveldb/leveldb/opt" 34 35 gometrics "github.com/rcrowley/go-metrics" 36 ) 37 38 var OpenFileLimit = 64 39 40 // cacheRatio specifies how the total allotted cache is distributed between the 41 // various system databases. 42 var cacheRatio = map[string]float64{ 43 "chaindata": 1.0, 44 "lightchaindata": 1.0, 45 } 46 47 // handleRatio specifies how the total allotted file descriptors is distributed 48 // between the various system databases. 49 var handleRatio = map[string]float64{ 50 "chaindata": 1.0, 51 "lightchaindata": 1.0, 52 } 53 54 type LDBDatabase struct { 55 fn string // filename for reporting 56 db *leveldb.DB // LevelDB instance 57 58 getTimer gometrics.Timer // Timer for measuring the database get request counts and latencies 59 putTimer gometrics.Timer // Timer for measuring the database put request counts and latencies 60 delTimer gometrics.Timer // Timer for measuring the database delete request counts and latencies 61 missMeter gometrics.Meter // Meter for measuring the missed database get requests 62 readMeter gometrics.Meter // Meter for measuring the database get request data usage 63 writeMeter gometrics.Meter // Meter for measuring the database put request data usage 64 compTimeMeter gometrics.Meter // Meter for measuring the total time spent in database compaction 65 compReadMeter gometrics.Meter // Meter for measuring the data read during compaction 66 compWriteMeter gometrics.Meter // Meter for measuring the data written during compaction 67 68 quitLock sync.Mutex // Mutex protecting the quit channel access 69 quitChan chan chan error // Quit channel to stop the metrics collection before closing the database 70 } 71 72 // NewLDBDatabase returns a LevelDB wrapped object. 73 func NewLDBDatabase(file string, cache int, handles int) (*LDBDatabase, error) { 74 // Calculate the cache and file descriptor allowance for this particular database 75 cache = int(float64(cache) * cacheRatio[filepath.Base(file)]) 76 if cache < 16 { 77 cache = 16 78 } 79 handles = int(float64(handles) * handleRatio[filepath.Base(file)]) 80 if handles < 16 { 81 handles = 16 82 } 83 glog.V(logger.Info).Infof("Allotted %dMB cache and %d file handles to %s", cache, handles, file) 84 85 // Open the db and recover any potential corruptions 86 db, err := leveldb.OpenFile(file, &opt.Options{ 87 OpenFilesCacheCapacity: handles, 88 BlockCacheCapacity: cache / 2 * opt.MiB, 89 WriteBuffer: cache / 4 * opt.MiB, // Two of these are used internally 90 Filter: filter.NewBloomFilter(10), 91 }) 92 if _, corrupted := err.(*errors.ErrCorrupted); corrupted { 93 db, err = leveldb.RecoverFile(file, nil) 94 } 95 // (Re)check for errors and abort if opening of the db failed 96 if err != nil { 97 return nil, err 98 } 99 return &LDBDatabase{ 100 fn: file, 101 db: db, 102 }, nil 103 } 104 105 // Path returns the path to the database directory. 106 func (db *LDBDatabase) Path() string { 107 return db.fn 108 } 109 110 // Put puts the given key / value to the queue 111 func (self *LDBDatabase) Put(key []byte, value []byte) error { 112 // Measure the database put latency, if requested 113 if self.putTimer != nil { 114 defer self.putTimer.UpdateSince(time.Now()) 115 } 116 // Generate the data to write to disk, update the meter and write 117 //value = rle.Compress(value) 118 119 if self.writeMeter != nil { 120 self.writeMeter.Mark(int64(len(value))) 121 } 122 return self.db.Put(key, value, nil) 123 } 124 125 // Get returns the given key if it's present. 126 func (self *LDBDatabase) Get(key []byte) ([]byte, error) { 127 // Measure the database get latency, if requested 128 if self.getTimer != nil { 129 defer self.getTimer.UpdateSince(time.Now()) 130 } 131 // Retrieve the key and increment the miss counter if not found 132 dat, err := self.db.Get(key, nil) 133 if err != nil { 134 if self.missMeter != nil { 135 self.missMeter.Mark(1) 136 } 137 return nil, err 138 } 139 // Otherwise update the actually retrieved amount of data 140 if self.readMeter != nil { 141 self.readMeter.Mark(int64(len(dat))) 142 } 143 return dat, nil 144 //return rle.Decompress(dat) 145 } 146 147 // Delete deletes the key from the queue and database 148 func (self *LDBDatabase) Delete(key []byte) error { 149 // Measure the database delete latency, if requested 150 if self.delTimer != nil { 151 defer self.delTimer.UpdateSince(time.Now()) 152 } 153 // Execute the actual operation 154 return self.db.Delete(key, nil) 155 } 156 157 func (self *LDBDatabase) NewIterator() iterator.Iterator { 158 return self.db.NewIterator(nil, nil) 159 } 160 161 func (self *LDBDatabase) Close() { 162 // Stop the metrics collection to avoid internal database races 163 self.quitLock.Lock() 164 defer self.quitLock.Unlock() 165 166 if self.quitChan != nil { 167 errc := make(chan error) 168 self.quitChan <- errc 169 if err := <-errc; err != nil { 170 glog.V(logger.Error).Infof("metrics failure in '%s': %v\n", self.fn, err) 171 } 172 } 173 err := self.db.Close() 174 if glog.V(logger.Error) { 175 if err == nil { 176 glog.Infoln("closed db:", self.fn) 177 } else { 178 glog.Errorf("error closing db %s: %v", self.fn, err) 179 } 180 } 181 } 182 183 func (self *LDBDatabase) LDB() *leveldb.DB { 184 return self.db 185 } 186 187 // Meter configures the database metrics collectors and 188 func (self *LDBDatabase) Meter(prefix string) { 189 // Short circuit metering if the metrics system is disabled 190 if !metrics.Enabled { 191 return 192 } 193 // Initialize all the metrics collector at the requested prefix 194 self.getTimer = metrics.NewTimer(prefix + "user/gets") 195 self.putTimer = metrics.NewTimer(prefix + "user/puts") 196 self.delTimer = metrics.NewTimer(prefix + "user/dels") 197 self.missMeter = metrics.NewMeter(prefix + "user/misses") 198 self.readMeter = metrics.NewMeter(prefix + "user/reads") 199 self.writeMeter = metrics.NewMeter(prefix + "user/writes") 200 self.compTimeMeter = metrics.NewMeter(prefix + "compact/time") 201 self.compReadMeter = metrics.NewMeter(prefix + "compact/input") 202 self.compWriteMeter = metrics.NewMeter(prefix + "compact/output") 203 204 // Create a quit channel for the periodic collector and run it 205 self.quitLock.Lock() 206 self.quitChan = make(chan chan error) 207 self.quitLock.Unlock() 208 209 go self.meter(3 * time.Second) 210 } 211 212 // meter periodically retrieves internal leveldb counters and reports them to 213 // the metrics subsystem. 214 // 215 // This is how a stats table look like (currently): 216 // Compactions 217 // Level | Tables | Size(MB) | Time(sec) | Read(MB) | Write(MB) 218 // -------+------------+---------------+---------------+---------------+--------------- 219 // 0 | 0 | 0.00000 | 1.27969 | 0.00000 | 12.31098 220 // 1 | 85 | 109.27913 | 28.09293 | 213.92493 | 214.26294 221 // 2 | 523 | 1000.37159 | 7.26059 | 66.86342 | 66.77884 222 // 3 | 570 | 1113.18458 | 0.00000 | 0.00000 | 0.00000 223 func (self *LDBDatabase) meter(refresh time.Duration) { 224 // Create the counters to store current and previous values 225 counters := make([][]float64, 2) 226 for i := 0; i < 2; i++ { 227 counters[i] = make([]float64, 3) 228 } 229 // Iterate ad infinitum and collect the stats 230 for i := 1; ; i++ { 231 // Retrieve the database stats 232 stats, err := self.db.GetProperty("leveldb.stats") 233 if err != nil { 234 glog.V(logger.Error).Infof("failed to read database stats: %v", err) 235 return 236 } 237 // Find the compaction table, skip the header 238 lines := strings.Split(stats, "\n") 239 for len(lines) > 0 && strings.TrimSpace(lines[0]) != "Compactions" { 240 lines = lines[1:] 241 } 242 if len(lines) <= 3 { 243 glog.V(logger.Error).Infof("compaction table not found") 244 return 245 } 246 lines = lines[3:] 247 248 // Iterate over all the table rows, and accumulate the entries 249 for j := 0; j < len(counters[i%2]); j++ { 250 counters[i%2][j] = 0 251 } 252 for _, line := range lines { 253 parts := strings.Split(line, "|") 254 if len(parts) != 6 { 255 break 256 } 257 for idx, counter := range parts[3:] { 258 if value, err := strconv.ParseFloat(strings.TrimSpace(counter), 64); err != nil { 259 glog.V(logger.Error).Infof("compaction entry parsing failed: %v", err) 260 return 261 } else { 262 counters[i%2][idx] += value 263 } 264 } 265 } 266 // Update all the requested meters 267 if self.compTimeMeter != nil { 268 self.compTimeMeter.Mark(int64((counters[i%2][0] - counters[(i-1)%2][0]) * 1000 * 1000 * 1000)) 269 } 270 if self.compReadMeter != nil { 271 self.compReadMeter.Mark(int64((counters[i%2][1] - counters[(i-1)%2][1]) * 1024 * 1024)) 272 } 273 if self.compWriteMeter != nil { 274 self.compWriteMeter.Mark(int64((counters[i%2][2] - counters[(i-1)%2][2]) * 1024 * 1024)) 275 } 276 // Sleep a bit, then repeat the stats collection 277 select { 278 case errc := <-self.quitChan: 279 // Quit requesting, stop hammering the database 280 errc <- nil 281 return 282 283 case <-time.After(refresh): 284 // Timeout, gather a new set of stats 285 } 286 } 287 } 288 289 // TODO: remove this stuff and expose leveldb directly 290 291 func (db *LDBDatabase) NewBatch() Batch { 292 return &ldbBatch{db: db.db, b: new(leveldb.Batch)} 293 } 294 295 type ldbBatch struct { 296 db *leveldb.DB 297 b *leveldb.Batch 298 } 299 300 func (b *ldbBatch) Put(key, value []byte) error { 301 b.b.Put(key, value) 302 return nil 303 } 304 305 func (b *ldbBatch) Write() error { 306 return b.db.Write(b.b, nil) 307 } 308 309 type table struct { 310 db Database 311 prefix string 312 } 313 314 // NewTable returns a Database object that prefixes all keys with a given 315 // string. 316 func NewTable(db Database, prefix string) Database { 317 return &table{ 318 db: db, 319 prefix: prefix, 320 } 321 } 322 323 func (dt *table) Put(key []byte, value []byte) error { 324 return dt.db.Put(append([]byte(dt.prefix), key...), value) 325 } 326 327 func (dt *table) Get(key []byte) ([]byte, error) { 328 return dt.db.Get(append([]byte(dt.prefix), key...)) 329 } 330 331 func (dt *table) Delete(key []byte) error { 332 return dt.db.Delete(append([]byte(dt.prefix), key...)) 333 } 334 335 func (dt *table) Close() { 336 // Do nothing; don't close the underlying DB. 337 } 338 339 type tableBatch struct { 340 batch Batch 341 prefix string 342 } 343 344 // NewTableBatch returns a Batch object which prefixes all keys with a given string. 345 func NewTableBatch(db Database, prefix string) Batch { 346 return &tableBatch{db.NewBatch(), prefix} 347 } 348 349 func (dt *table) NewBatch() Batch { 350 return &tableBatch{dt.db.NewBatch(), dt.prefix} 351 } 352 353 func (tb *tableBatch) Put(key, value []byte) error { 354 return tb.batch.Put(append([]byte(tb.prefix), key...), value) 355 } 356 357 func (tb *tableBatch) Write() error { 358 return tb.batch.Write() 359 }