github.com/ccm-chain/ccmchain@v1.0.0/core/rawdb/database.go (about)

     1  // Copyright 2018 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 rawdb
    18  
    19  import (
    20  	"bytes"
    21  	"errors"
    22  	"fmt"
    23  	"os"
    24  	"sync/atomic"
    25  	"time"
    26  
    27  	"github.com/ccm-chain/ccmchain/common"
    28  	"github.com/ccm-chain/ccmchain/database"
    29  	"github.com/ccm-chain/ccmchain/database/leveldb"
    30  	"github.com/ccm-chain/ccmchain/database/memorydb"
    31  	"github.com/ccm-chain/ccmchain/log"
    32  	"github.com/olekukonko/tablewriter"
    33  )
    34  
    35  // freezerdb is a database wrapper that enabled freezer data retrievals.
    36  type freezerdb struct {
    37  	database.KeyValueStore
    38  	database.AncientStore
    39  }
    40  
    41  // Close implements io.Closer, closing both the fast key-value store as well as
    42  // the slow ancient tables.
    43  func (frdb *freezerdb) Close() error {
    44  	var errs []error
    45  	if err := frdb.AncientStore.Close(); err != nil {
    46  		errs = append(errs, err)
    47  	}
    48  	if err := frdb.KeyValueStore.Close(); err != nil {
    49  		errs = append(errs, err)
    50  	}
    51  	if len(errs) != 0 {
    52  		return fmt.Errorf("%v", errs)
    53  	}
    54  	return nil
    55  }
    56  
    57  // Freeze is a helper method used for external testing to trigger and block until
    58  // a freeze cycle completes, without having to sleep for a minute to trigger the
    59  // automatic background run.
    60  func (frdb *freezerdb) Freeze(threshold uint64) {
    61  	// Set the freezer threshold to a temporary value
    62  	defer func(old uint64) {
    63  		atomic.StoreUint64(&frdb.AncientStore.(*freezer).threshold, old)
    64  	}(atomic.LoadUint64(&frdb.AncientStore.(*freezer).threshold))
    65  	atomic.StoreUint64(&frdb.AncientStore.(*freezer).threshold, threshold)
    66  
    67  	// Trigger a freeze cycle and block until it's done
    68  	trigger := make(chan struct{}, 1)
    69  	frdb.AncientStore.(*freezer).trigger <- trigger
    70  	<-trigger
    71  }
    72  
    73  // nofreezedb is a database wrapper that disables freezer data retrievals.
    74  type nofreezedb struct {
    75  	database.KeyValueStore
    76  }
    77  
    78  // HasAncient returns an error as we don't have a backing chain freezer.
    79  func (db *nofreezedb) HasAncient(kind string, number uint64) (bool, error) {
    80  	return false, errNotSupported
    81  }
    82  
    83  // Ancient returns an error as we don't have a backing chain freezer.
    84  func (db *nofreezedb) Ancient(kind string, number uint64) ([]byte, error) {
    85  	return nil, errNotSupported
    86  }
    87  
    88  // Ancients returns an error as we don't have a backing chain freezer.
    89  func (db *nofreezedb) Ancients() (uint64, error) {
    90  	return 0, errNotSupported
    91  }
    92  
    93  // AncientSize returns an error as we don't have a backing chain freezer.
    94  func (db *nofreezedb) AncientSize(kind string) (uint64, error) {
    95  	return 0, errNotSupported
    96  }
    97  
    98  // AppendAncient returns an error as we don't have a backing chain freezer.
    99  func (db *nofreezedb) AppendAncient(number uint64, hash, header, body, receipts, td []byte) error {
   100  	return errNotSupported
   101  }
   102  
   103  // TruncateAncients returns an error as we don't have a backing chain freezer.
   104  func (db *nofreezedb) TruncateAncients(items uint64) error {
   105  	return errNotSupported
   106  }
   107  
   108  // Sync returns an error as we don't have a backing chain freezer.
   109  func (db *nofreezedb) Sync() error {
   110  	return errNotSupported
   111  }
   112  
   113  // NewDatabase creates a high level database on top of a given key-value data
   114  // store without a freezer moving immutable chain segments into cold storage.
   115  func NewDatabase(db database.KeyValueStore) database.Database {
   116  	return &nofreezedb{
   117  		KeyValueStore: db,
   118  	}
   119  }
   120  
   121  // NewDatabaseWithFreezer creates a high level database on top of a given key-
   122  // value data store with a freezer moving immutable chain segments into cold
   123  // storage.
   124  func NewDatabaseWithFreezer(db database.KeyValueStore, freezer string, namespace string) (database.Database, error) {
   125  	// Create the idle freezer instance
   126  	frdb, err := newFreezer(freezer, namespace)
   127  	if err != nil {
   128  		return nil, err
   129  	}
   130  	// Since the freezer can be stored separately from the user's key-value database,
   131  	// there's a fairly high probability that the user requests invalid combinations
   132  	// of the freezer and database. Ensure that we don't shoot ourselves in the foot
   133  	// by serving up conflicting data, leading to both datastores getting corrupted.
   134  	//
   135  	//   - If both the freezer and key-value store is empty (no genesis), we just
   136  	//     initialized a new empty freezer, so everything's fine.
   137  	//   - If the key-value store is empty, but the freezer is not, we need to make
   138  	//     sure the user's genesis matches the freezer. That will be checked in the
   139  	//     blockchain, since we don't have the genesis block here (nor should we at
   140  	//     this point care, the key-value/freezer combo is valid).
   141  	//   - If neither the key-value store nor the freezer is empty, cross validate
   142  	//     the genesis hashes to make sure they are compatible. If they are, also
   143  	//     ensure that there's no gap between the freezer and sunsequently leveldb.
   144  	//   - If the key-value store is not empty, but the freezer is we might just be
   145  	//     upgrading to the freezer release, or we might have had a small chain and
   146  	//     not frozen anything yet. Ensure that no blocks are missing yet from the
   147  	//     key-value store, since that would mean we already had an old freezer.
   148  
   149  	// If the genesis hash is empty, we have a new key-value store, so nothing to
   150  	// validate in this method. If, however, the genesis hash is not nil, compare
   151  	// it to the freezer content.
   152  	if kvgenesis, _ := db.Get(headerHashKey(0)); len(kvgenesis) > 0 {
   153  		if frozen, _ := frdb.Ancients(); frozen > 0 {
   154  			// If the freezer already contains something, ensure that the genesis blocks
   155  			// match, otherwise we might mix up freezers across chains and destroy both
   156  			// the freezer and the key-value store.
   157  			frgenesis, err := frdb.Ancient(freezerHashTable, 0)
   158  			if err != nil {
   159  				return nil, fmt.Errorf("failed to retrieve genesis from ancient %v", err)
   160  			} else if !bytes.Equal(kvgenesis, frgenesis) {
   161  				return nil, fmt.Errorf("genesis mismatch: %#x (leveldb) != %#x (ancients)", kvgenesis, frgenesis)
   162  			}
   163  			// Key-value store and freezer belong to the same network. Ensure that they
   164  			// are contiguous, otherwise we might end up with a non-functional freezer.
   165  			if kvhash, _ := db.Get(headerHashKey(frozen)); len(kvhash) == 0 {
   166  				// Subsequent header after the freezer limit is missing from the database.
   167  				// Reject startup is the database has a more recent head.
   168  				if *ReadHeaderNumber(db, ReadHeadHeaderHash(db)) > frozen-1 {
   169  					return nil, fmt.Errorf("gap (#%d) in the chain between ancients and leveldb", frozen)
   170  				}
   171  				// Database contains only older data than the freezer, this happens if the
   172  				// state was wiped and reinited from an existing freezer.
   173  			}
   174  			// Otherwise, key-value store continues where the freezer left off, all is fine.
   175  			// We might have duplicate blocks (crash after freezer write but before key-value
   176  			// store deletion, but that's fine).
   177  		} else {
   178  			// If the freezer is empty, ensure nothing was moved yet from the key-value
   179  			// store, otherwise we'll end up missing data. We check block #1 to decide
   180  			// if we froze anything previously or not, but do take care of databases with
   181  			// only the genesis block.
   182  			if ReadHeadHeaderHash(db) != common.BytesToHash(kvgenesis) {
   183  				// Key-value store contains more data than the genesis block, make sure we
   184  				// didn't freeze anything yet.
   185  				if kvblob, _ := db.Get(headerHashKey(1)); len(kvblob) == 0 {
   186  					return nil, errors.New("ancient chain segments already extracted, please set --datadir.ancient to the correct path")
   187  				}
   188  				// Block #1 is still in the database, we're allowed to init a new feezer
   189  			}
   190  			// Otherwise, the head header is still the genesis, we're allowed to init a new
   191  			// feezer.
   192  		}
   193  	}
   194  	// Freezer is consistent with the key-value database, permit combining the two
   195  	go frdb.freeze(db)
   196  
   197  	return &freezerdb{
   198  		KeyValueStore: db,
   199  		AncientStore:  frdb,
   200  	}, nil
   201  }
   202  
   203  // NewMemoryDatabase creates an ephemeral in-memory key-value database without a
   204  // freezer moving immutable chain segments into cold storage.
   205  func NewMemoryDatabase() database.Database {
   206  	return NewDatabase(memorydb.New())
   207  }
   208  
   209  // NewMemoryDatabaseWithCap creates an ephemeral in-memory key-value database
   210  // with an initial starting capacity, but without a freezer moving immutable
   211  // chain segments into cold storage.
   212  func NewMemoryDatabaseWithCap(size int) database.Database {
   213  	return NewDatabase(memorydb.NewWithCap(size))
   214  }
   215  
   216  // NewLevelDBDatabase creates a persistent key-value database without a freezer
   217  // moving immutable chain segments into cold storage.
   218  func NewLevelDBDatabase(file string, cache int, handles int, namespace string) (database.Database, error) {
   219  	db, err := leveldb.New(file, cache, handles, namespace)
   220  	if err != nil {
   221  		return nil, err
   222  	}
   223  	return NewDatabase(db), nil
   224  }
   225  
   226  // NewLevelDBDatabaseWithFreezer creates a persistent key-value database with a
   227  // freezer moving immutable chain segments into cold storage.
   228  func NewLevelDBDatabaseWithFreezer(file string, cache int, handles int, freezer string, namespace string) (database.Database, error) {
   229  	kvdb, err := leveldb.New(file, cache, handles, namespace)
   230  	if err != nil {
   231  		return nil, err
   232  	}
   233  	frdb, err := NewDatabaseWithFreezer(kvdb, freezer, namespace)
   234  	if err != nil {
   235  		kvdb.Close()
   236  		return nil, err
   237  	}
   238  	return frdb, nil
   239  }
   240  
   241  // InspectDatabase traverses the entire database and checks the size
   242  // of all different categories of data.
   243  func InspectDatabase(db database.Database) error {
   244  	it := db.NewIterator(nil, nil)
   245  	defer it.Release()
   246  
   247  	var (
   248  		count  int64
   249  		start  = time.Now()
   250  		logged = time.Now()
   251  
   252  		// Key-value store statistics
   253  		total           common.StorageSize
   254  		headerSize      common.StorageSize
   255  		bodySize        common.StorageSize
   256  		receiptSize     common.StorageSize
   257  		tdSize          common.StorageSize
   258  		numHashPairing  common.StorageSize
   259  		hashNumPairing  common.StorageSize
   260  		trieSize        common.StorageSize
   261  		codeSize        common.StorageSize
   262  		txlookupSize    common.StorageSize
   263  		accountSnapSize common.StorageSize
   264  		storageSnapSize common.StorageSize
   265  		preimageSize    common.StorageSize
   266  		bloomBitsSize   common.StorageSize
   267  		cliqueSnapsSize common.StorageSize
   268  
   269  		// Ancient store statistics
   270  		ancientHeaders  common.StorageSize
   271  		ancientBodies   common.StorageSize
   272  		ancientReceipts common.StorageSize
   273  		ancientHashes   common.StorageSize
   274  		ancientTds      common.StorageSize
   275  
   276  		// Les statistic
   277  		chtTrieNodes   common.StorageSize
   278  		bloomTrieNodes common.StorageSize
   279  
   280  		// Meta- and unaccounted data
   281  		metadata    common.StorageSize
   282  		unaccounted common.StorageSize
   283  	)
   284  	// Inspect key-value database first.
   285  	for it.Next() {
   286  		var (
   287  			key  = it.Key()
   288  			size = common.StorageSize(len(key) + len(it.Value()))
   289  		)
   290  		total += size
   291  		switch {
   292  		case bytes.HasPrefix(key, headerPrefix) && bytes.HasSuffix(key, headerTDSuffix):
   293  			tdSize += size
   294  		case bytes.HasPrefix(key, headerPrefix) && bytes.HasSuffix(key, headerHashSuffix):
   295  			numHashPairing += size
   296  		case bytes.HasPrefix(key, headerPrefix) && len(key) == (len(headerPrefix)+8+common.HashLength):
   297  			headerSize += size
   298  		case bytes.HasPrefix(key, headerNumberPrefix) && len(key) == (len(headerNumberPrefix)+common.HashLength):
   299  			hashNumPairing += size
   300  		case bytes.HasPrefix(key, blockBodyPrefix) && len(key) == (len(blockBodyPrefix)+8+common.HashLength):
   301  			bodySize += size
   302  		case bytes.HasPrefix(key, blockReceiptsPrefix) && len(key) == (len(blockReceiptsPrefix)+8+common.HashLength):
   303  			receiptSize += size
   304  		case bytes.HasPrefix(key, txLookupPrefix) && len(key) == (len(txLookupPrefix)+common.HashLength):
   305  			txlookupSize += size
   306  		case bytes.HasPrefix(key, SnapshotAccountPrefix) && len(key) == (len(SnapshotAccountPrefix)+common.HashLength):
   307  			accountSnapSize += size
   308  		case bytes.HasPrefix(key, SnapshotStoragePrefix) && len(key) == (len(SnapshotStoragePrefix)+2*common.HashLength):
   309  			storageSnapSize += size
   310  		case bytes.HasPrefix(key, preimagePrefix) && len(key) == (len(preimagePrefix)+common.HashLength):
   311  			preimageSize += size
   312  		case bytes.HasPrefix(key, bloomBitsPrefix) && len(key) == (len(bloomBitsPrefix)+10+common.HashLength):
   313  			bloomBitsSize += size
   314  		case bytes.HasPrefix(key, []byte("clique-")) && len(key) == 7+common.HashLength:
   315  			cliqueSnapsSize += size
   316  		case bytes.HasPrefix(key, []byte("cht-")) && len(key) == 4+common.HashLength:
   317  			chtTrieNodes += size
   318  		case bytes.HasPrefix(key, []byte("blt-")) && len(key) == 4+common.HashLength:
   319  			bloomTrieNodes += size
   320  		case bytes.HasPrefix(key, codePrefix) && len(key) == len(codePrefix)+common.HashLength:
   321  			codeSize += size
   322  		case len(key) == common.HashLength:
   323  			trieSize += size
   324  		default:
   325  			var accounted bool
   326  			for _, meta := range [][]byte{databaseVerisionKey, headHeaderKey, headBlockKey, headFastBlockKey, fastTrieProgressKey} {
   327  				if bytes.Equal(key, meta) {
   328  					metadata += size
   329  					accounted = true
   330  					break
   331  				}
   332  			}
   333  			if !accounted {
   334  				unaccounted += size
   335  			}
   336  		}
   337  		count += 1
   338  		if count%1000 == 0 && time.Since(logged) > 8*time.Second {
   339  			log.Info("Inspecting database", "count", count, "elapsed", common.PrettyDuration(time.Since(start)))
   340  			logged = time.Now()
   341  		}
   342  	}
   343  	// Inspect append-only file store then.
   344  	ancients := []*common.StorageSize{&ancientHeaders, &ancientBodies, &ancientReceipts, &ancientHashes, &ancientTds}
   345  	for i, category := range []string{freezerHeaderTable, freezerBodiesTable, freezerReceiptTable, freezerHashTable, freezerDifficultyTable} {
   346  		if size, err := db.AncientSize(category); err == nil {
   347  			*ancients[i] += common.StorageSize(size)
   348  			total += common.StorageSize(size)
   349  		}
   350  	}
   351  	// Display the database statistic.
   352  	stats := [][]string{
   353  		{"Key-Value store", "Headers", headerSize.String()},
   354  		{"Key-Value store", "Bodies", bodySize.String()},
   355  		{"Key-Value store", "Receipts", receiptSize.String()},
   356  		{"Key-Value store", "Difficulties", tdSize.String()},
   357  		{"Key-Value store", "Block number->hash", numHashPairing.String()},
   358  		{"Key-Value store", "Block hash->number", hashNumPairing.String()},
   359  		{"Key-Value store", "Transaction index", txlookupSize.String()},
   360  		{"Key-Value store", "Bloombit index", bloomBitsSize.String()},
   361  		{"Key-Value store", "Contract codes", codeSize.String()},
   362  		{"Key-Value store", "Trie nodes", trieSize.String()},
   363  		{"Key-Value store", "Trie preimages", preimageSize.String()},
   364  		{"Key-Value store", "Account snapshot", accountSnapSize.String()},
   365  		{"Key-Value store", "Storage snapshot", storageSnapSize.String()},
   366  		{"Key-Value store", "Clique snapshots", cliqueSnapsSize.String()},
   367  		{"Key-Value store", "Singleton metadata", metadata.String()},
   368  		{"Ancient store", "Headers", ancientHeaders.String()},
   369  		{"Ancient store", "Bodies", ancientBodies.String()},
   370  		{"Ancient store", "Receipts", ancientReceipts.String()},
   371  		{"Ancient store", "Difficulties", ancientTds.String()},
   372  		{"Ancient store", "Block number->hash", ancientHashes.String()},
   373  		{"Light client", "CHT trie nodes", chtTrieNodes.String()},
   374  		{"Light client", "Bloom trie nodes", bloomTrieNodes.String()},
   375  	}
   376  	table := tablewriter.NewWriter(os.Stdout)
   377  	table.SetHeader([]string{"Database", "Category", "Size"})
   378  	table.SetFooter([]string{"", "Total", total.String()})
   379  	table.AppendBulk(stats)
   380  	table.Render()
   381  
   382  	if unaccounted > 0 {
   383  		log.Error("Database contains unaccounted data", "size", unaccounted)
   384  	}
   385  	return nil
   386  }