github.com/aigarnetwork/aigar@v0.0.0-20191115204914-d59a6eb70f8e/core/rawdb/freezer_reinit.go (about)

     1  //  Copyright 2018 The go-ethereum Authors
     2  //  Copyright 2019 The go-aigar Authors
     3  //  This file is part of the go-aigar library.
     4  //
     5  //  The go-aigar library is free software: you can redistribute it and/or modify
     6  //  it under the terms of the GNU Lesser General Public License as published by
     7  //  the Free Software Foundation, either version 3 of the License, or
     8  //  (at your option) any later version.
     9  //
    10  //  The go-aigar library is distributed in the hope that it will be useful,
    11  //  but WITHOUT ANY WARRANTY; without even the implied warranty of
    12  //  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    13  //  GNU Lesser General Public License for more details.
    14  //
    15  //  You should have received a copy of the GNU Lesser General Public License
    16  //  along with the go-aigar library. If not, see <http://www.gnu.org/licenses/>.
    17  
    18  package rawdb
    19  
    20  import (
    21  	"errors"
    22  	"runtime"
    23  	"sync/atomic"
    24  	"time"
    25  
    26  	"github.com/AigarNetwork/aigar/common"
    27  	"github.com/AigarNetwork/aigar/common/prque"
    28  	"github.com/AigarNetwork/aigar/core/types"
    29  	"github.com/AigarNetwork/aigar/ethdb"
    30  	"github.com/AigarNetwork/aigar/log"
    31  )
    32  
    33  // InitDatabaseFromFreezer reinitializes an empty database from a previous batch
    34  // of frozen ancient blocks. The method iterates over all the frozen blocks and
    35  // injects into the database the block hash->number mappings and the transaction
    36  // lookup entries.
    37  func InitDatabaseFromFreezer(db ethdb.Database) error {
    38  	// If we can't access the freezer or it's empty, abort
    39  	frozen, err := db.Ancients()
    40  	if err != nil || frozen == 0 {
    41  		return err
    42  	}
    43  	// Blocks previously frozen, iterate over- and hash them concurrently
    44  	var (
    45  		number  = ^uint64(0) // -1
    46  		results = make(chan *types.Block, 4*runtime.NumCPU())
    47  	)
    48  	abort := make(chan struct{})
    49  	defer close(abort)
    50  
    51  	for i := 0; i < runtime.NumCPU(); i++ {
    52  		go func() {
    53  			for {
    54  				// Fetch the next task number, terminating if everything's done
    55  				n := atomic.AddUint64(&number, 1)
    56  				if n >= frozen {
    57  					return
    58  				}
    59  				// Retrieve the block from the freezer (no need for the hash, we pull by
    60  				// number from the freezer). If successful, pre-cache the block hash and
    61  				// the individual transaction hashes for storing into the database.
    62  				block := ReadBlock(db, common.Hash{}, n)
    63  				if block != nil {
    64  					block.Hash()
    65  					for _, tx := range block.Transactions() {
    66  						tx.Hash()
    67  					}
    68  				}
    69  				// Feed the block to the aggregator, or abort on interrupt
    70  				select {
    71  				case results <- block:
    72  				case <-abort:
    73  					return
    74  				}
    75  			}
    76  		}()
    77  	}
    78  	// Reassemble the blocks into a contiguous stream and push them out to disk
    79  	var (
    80  		queue = prque.New(nil)
    81  		next  = int64(0)
    82  
    83  		batch  = db.NewBatch()
    84  		start  = time.Now()
    85  		logged time.Time
    86  	)
    87  	for i := uint64(0); i < frozen; i++ {
    88  		// Retrieve the next result and bail if it's nil
    89  		block := <-results
    90  		if block == nil {
    91  			return errors.New("broken ancient database")
    92  		}
    93  		// Push the block into the import queue and process contiguous ranges
    94  		queue.Push(block, -int64(block.NumberU64()))
    95  		for !queue.Empty() {
    96  			// If the next available item is gapped, return
    97  			if _, priority := queue.Peek(); -priority != next {
    98  				break
    99  			}
   100  			// Next block available, pop it off and index it
   101  			block = queue.PopItem().(*types.Block)
   102  			next++
   103  
   104  			// Inject hash<->number mapping and txlookup indexes
   105  			WriteHeaderNumber(batch, block.Hash(), block.NumberU64())
   106  			WriteTxLookupEntries(batch, block)
   107  
   108  			// If enough data was accumulated in memory or we're at the last block, dump to disk
   109  			if batch.ValueSize() > ethdb.IdealBatchSize || uint64(next) == frozen {
   110  				if err := batch.Write(); err != nil {
   111  					return err
   112  				}
   113  				batch.Reset()
   114  			}
   115  			// If we've spent too much time already, notify the user of what we're doing
   116  			if time.Since(logged) > 8*time.Second {
   117  				log.Info("Initializing chain from ancient data", "number", block.Number(), "hash", block.Hash(), "total", frozen-1, "elapsed", common.PrettyDuration(time.Since(start)))
   118  				logged = time.Now()
   119  			}
   120  		}
   121  	}
   122  	hash := ReadCanonicalHash(db, frozen-1)
   123  	WriteHeadHeaderHash(db, hash)
   124  	WriteHeadFastBlockHash(db, hash)
   125  
   126  	log.Info("Initialized chain from ancient data", "number", frozen-1, "hash", hash, "elapsed", common.PrettyDuration(time.Since(start)))
   127  	return nil
   128  }