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  }