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 }