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