github.com/ethereum/go-ethereum@v1.16.1/node/database.go (about)

     1  // Copyright 2024 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 node
    18  
    19  import (
    20  	"fmt"
    21  
    22  	"github.com/ethereum/go-ethereum/core/rawdb"
    23  	"github.com/ethereum/go-ethereum/ethdb"
    24  	"github.com/ethereum/go-ethereum/ethdb/leveldb"
    25  	"github.com/ethereum/go-ethereum/ethdb/pebble"
    26  	"github.com/ethereum/go-ethereum/log"
    27  )
    28  
    29  // DatabaseOptions contains the options to apply when opening a database.
    30  type DatabaseOptions struct {
    31  	// Directory for storing chain history ("freezer").
    32  	AncientsDirectory string
    33  
    34  	// The optional Era folder, which can be either a subfolder under
    35  	// ancient/chain or a directory specified via an absolute path.
    36  	EraDirectory string
    37  
    38  	MetricsNamespace string // the namespace for database relevant metrics
    39  	Cache            int    // the capacity(in megabytes) of the data caching
    40  	Handles          int    // number of files to be open simultaneously
    41  	ReadOnly         bool   // if true, no writes can be performed
    42  }
    43  
    44  type internalOpenOptions struct {
    45  	directory string
    46  	dbEngine  string // "leveldb" | "pebble"
    47  	DatabaseOptions
    48  }
    49  
    50  // openDatabase opens both a disk-based key-value database such as leveldb or pebble, but also
    51  // integrates it with a freezer database -- if the AncientDir option has been
    52  // set on the provided OpenOptions.
    53  // The passed o.AncientDir indicates the path of root ancient directory where
    54  // the chain freezer can be opened.
    55  func openDatabase(o internalOpenOptions) (ethdb.Database, error) {
    56  	kvdb, err := openKeyValueDatabase(o)
    57  	if err != nil {
    58  		return nil, err
    59  	}
    60  	opts := rawdb.OpenOptions{
    61  		Ancient:          o.AncientsDirectory,
    62  		Era:              o.EraDirectory,
    63  		MetricsNamespace: o.MetricsNamespace,
    64  		ReadOnly:         o.ReadOnly,
    65  	}
    66  	frdb, err := rawdb.Open(kvdb, opts)
    67  	if err != nil {
    68  		kvdb.Close()
    69  		return nil, err
    70  	}
    71  	return frdb, nil
    72  }
    73  
    74  // openKeyValueDatabase opens a disk-based key-value database, e.g. leveldb or pebble.
    75  //
    76  //						  type == null          type != null
    77  //					   +----------------------------------------
    78  //	db is non-existent |  pebble default  |  specified type
    79  //	db is existent     |  from db         |  specified type (if compatible)
    80  func openKeyValueDatabase(o internalOpenOptions) (ethdb.KeyValueStore, error) {
    81  	// Reject any unsupported database type
    82  	if len(o.dbEngine) != 0 && o.dbEngine != rawdb.DBLeveldb && o.dbEngine != rawdb.DBPebble {
    83  		return nil, fmt.Errorf("unknown db.engine %v", o.dbEngine)
    84  	}
    85  	// Retrieve any pre-existing database's type and use that or the requested one
    86  	// as long as there's no conflict between the two types
    87  	existingDb := rawdb.PreexistingDatabase(o.directory)
    88  	if len(existingDb) != 0 && len(o.dbEngine) != 0 && o.dbEngine != existingDb {
    89  		return nil, fmt.Errorf("db.engine choice was %v but found pre-existing %v database in specified data directory", o.dbEngine, existingDb)
    90  	}
    91  	if o.dbEngine == rawdb.DBPebble || existingDb == rawdb.DBPebble {
    92  		log.Info("Using pebble as the backing database")
    93  		return newPebbleDBDatabase(o.directory, o.Cache, o.Handles, o.MetricsNamespace, o.ReadOnly)
    94  	}
    95  	if o.dbEngine == rawdb.DBLeveldb || existingDb == rawdb.DBLeveldb {
    96  		log.Info("Using leveldb as the backing database")
    97  		return newLevelDBDatabase(o.directory, o.Cache, o.Handles, o.MetricsNamespace, o.ReadOnly)
    98  	}
    99  	// No pre-existing database, no user-requested one either. Default to Pebble.
   100  	log.Info("Defaulting to pebble as the backing database")
   101  	return newPebbleDBDatabase(o.directory, o.Cache, o.Handles, o.MetricsNamespace, o.ReadOnly)
   102  }
   103  
   104  // newLevelDBDatabase creates a persistent key-value database without a freezer
   105  // moving immutable chain segments into cold storage.
   106  func newLevelDBDatabase(file string, cache int, handles int, namespace string, readonly bool) (ethdb.KeyValueStore, error) {
   107  	db, err := leveldb.New(file, cache, handles, namespace, readonly)
   108  	if err != nil {
   109  		return nil, err
   110  	}
   111  	log.Info("Using LevelDB as the backing database")
   112  	return db, nil
   113  }
   114  
   115  // newPebbleDBDatabase creates a persistent key-value database without a freezer
   116  // moving immutable chain segments into cold storage.
   117  func newPebbleDBDatabase(file string, cache int, handles int, namespace string, readonly bool) (ethdb.KeyValueStore, error) {
   118  	db, err := pebble.New(file, cache, handles, namespace, readonly)
   119  	if err != nil {
   120  		return nil, err
   121  	}
   122  	return db, nil
   123  }