core, eth: skip transaction indexing during fast sync if required

This commit is contained in:
rjl493456442 2019-07-26 13:36:26 +08:00 committed by Péter Szilágyi
parent 24192a95c0
commit 89ad90d335
No known key found for this signature in database
GPG key ID: E9AE538CEDF8293D
5 changed files with 62 additions and 40 deletions

View file

@ -457,7 +457,7 @@ func (bc *BlockChain) SetHead(head uint64) error {
// FastSyncCommitHead sets the current head block to the one defined by the hash // FastSyncCommitHead sets the current head block to the one defined by the hash
// irrelevant what the chain contents were prior. // irrelevant what the chain contents were prior.
func (bc *BlockChain) FastSyncCommitHead(hash common.Hash) error { func (bc *BlockChain) FastSyncCommitHead(hash common.Hash, from uint64, ancient uint64) error {
// Make sure that both the block as well at its state trie exists // Make sure that both the block as well at its state trie exists
block := bc.GetBlockByHash(hash) block := bc.GetBlockByHash(hash)
if block == nil { if block == nil {
@ -472,6 +472,22 @@ func (bc *BlockChain) FastSyncCommitHead(hash common.Hash) error {
headBlockGauge.Update(int64(block.NumberU64())) headBlockGauge.Update(int64(block.NumberU64()))
bc.chainmu.Unlock() bc.chainmu.Unlock()
// Write tx indices tail if it doesn't exist in database.
// The tx index tail can only be one of the following two options:
// * the start point of fast sync:
// in this case all blocks imported during fast sync have been indexed
// * ancient - limit:
// in this case the block before ancient-limit won't be indexed
if tail := rawdb.ReadTxIndexTail(bc.db); tail == nil {
if bc.txLookupLimit != 0 && ancient >= bc.txLookupLimit && ancient-bc.txLookupLimit > from {
rawdb.WriteTxIndexTail(bc.db, ancient-bc.txLookupLimit)
} else {
if from == 1 {
from = 0
}
rawdb.WriteTxIndexTail(bc.db, from)
}
}
log.Info("Committed new head block", "number", block.Number(), "hash", hash) log.Info("Committed new head block", "number", block.Number(), "hash", hash)
return nil return nil
} }
@ -1091,11 +1107,14 @@ func (bc *BlockChain) InsertReceiptChain(blockChain types.Blocks, receiptChain [
// Flush data into ancient database. // Flush data into ancient database.
size += rawdb.WriteAncientBlock(bc.db, block, receiptChain[i], bc.GetTd(block.Hash(), block.NumberU64())) size += rawdb.WriteAncientBlock(bc.db, block, receiptChain[i], bc.GetTd(block.Hash(), block.NumberU64()))
// We don't write tx lookup indices here because Geth can offer a CLI flag `txlookuplimt` // Write tx indices if any condition is satisfied:
// with which user can choose to drop historical indices data and only keep latest indices. // * If user requires to reserve all tx indices(txlookuplimit=0)
// After the fast sync, we will reconstruct all missing indices even user requires to keep // * If all ancient tx indices are required to be reserved(txlookuplimit is even higher than ancientlimit)
// all historical indices. // * If block number is large enough to be regarded as a recent block
// It means blocks below the ancientLimit-txlookupLimit won't be indexed.
if bc.txLookupLimit == 0 || ancientLimit <= bc.txLookupLimit || block.NumberU64() >= ancientLimit-bc.txLookupLimit {
rawdb.WriteTxLookupEntries(batch, block)
}
stats.processed++ stats.processed++
} }
// Flush all tx-lookup index data. // Flush all tx-lookup index data.
@ -1169,11 +1188,8 @@ func (bc *BlockChain) InsertReceiptChain(blockChain types.Blocks, receiptChain [
// Write all the data out into the database // Write all the data out into the database
rawdb.WriteBody(batch, block.Hash(), block.NumberU64(), block.Body()) rawdb.WriteBody(batch, block.Hash(), block.NumberU64(), block.Body())
rawdb.WriteReceipts(batch, block.Hash(), block.NumberU64(), receiptChain[i]) rawdb.WriteReceipts(batch, block.Hash(), block.NumberU64(), receiptChain[i])
// We always write tx indices for live block since we assume the indices are needed.
// We don't write tx lookup indices here because Geth can offer a CLI flag `txlookuplimt` rawdb.WriteTxLookupEntries(batch, block)
// with which user can choose to drop historical indices data and only keep latest indices.
// After the fast sync, we will reconstruct all missing indices even user requires to keep
// all historical indices.
stats.processed++ stats.processed++
if batch.ValueSize() >= ethdb.IdealBatchSize { if batch.ValueSize() >= ethdb.IdealBatchSize {
@ -2056,10 +2072,12 @@ func (bc *BlockChain) update() {
// The user can adjust the txlookuplimit value for each launch, Geth will // The user can adjust the txlookuplimit value for each launch, Geth will
// automatically construct the missing indices and delete the extra indices. // automatically construct the missing indices and delete the extra indices.
func (bc *BlockChain) maintainTxIndex() { func (bc *BlockChain) maintainTxIndex() {
// initialiseIndices inits txlookup indices into the database. // initialiseIndices inits tx indices into the database if `TxIndexTail`
// If there already exists some indices, this function will find // is missing in database.
// the oldest block which has been indexed and start indexing from // Note for archive sync or fast sync, this code path will only be triggered
// this point. // after importing the first batch of blocks(e.g. 1024). But these block
// actually are already indexed. So a binary search will be performed to
// skip reindexing.
initialiseIndices := func(head uint64, done chan struct{}) { initialiseIndices := func(head uint64, done chan struct{}) {
defer func() { done <- struct{}{} }() defer func() { done <- struct{}{} }()
@ -2067,27 +2085,26 @@ func (bc *BlockChain) maintainTxIndex() {
if bc.txLookupLimit != 0 && head > bc.txLookupLimit { if bc.txLookupLimit != 0 && head > bc.txLookupLimit {
from = head - bc.txLookupLimit from = head - bc.txLookupLimit
} }
// Find oldest indexed block via binary search when we don't if tail := rawdb.FindTxIndexTail(bc.db, from, to); tail != nil {
// have this flag in database. // Special case here is genesis block doesn't contain any transaction
start := time.Now() // that will be regarded as unindexed.
oldest := rawdb.FindTxIndexTail(bc.db, from, to) if *tail == from || (from == 0 && *tail == 1) {
log.Debug("Find oldest indexed block", "oldest", oldest, "elapsed", common.PrettyDuration(time.Since(start))) rawdb.WriteTxIndexTail(bc.db, from)
// Re-construct missing tx indices.
if oldest == nil {
rawdb.IndexTxLookup(bc.db, from, to) // No block has been indexed.
} else {
rawdb.IndexTxLookup(bc.db, from, *oldest)
// Drop all useless tx indices below the HEAD-limit. // Drop all useless tx indices below the HEAD-limit.
if from > 0 { if from > 0 {
oldest := rawdb.FindTxIndexTail(bc.db, 0, from) rawdb.RemoveTxsLookup(bc.db, 0, from)
if oldest != nil { }
rawdb.RemoveTxsLookup(bc.db, *oldest, from) return
} }
} }
// Re-construct missing tx indices.
rawdb.IndexTxLookup(bc.db, from, to)
// Drop all useless tx indices below the HEAD-limit.
if from > 0 {
rawdb.RemoveTxsLookup(bc.db, 0, from)
} }
log.Debug("Initialised transaction indices", "elapsed", common.PrettyDuration(time.Since(start)))
} }
// indexBlocks reindex or unindex transaction indices depends // indexBlocks reindex or unindex transaction indices depends
// on user's requirement. // on user's requirement.
@ -2131,10 +2148,10 @@ func (bc *BlockChain) maintainTxIndex() {
case head := <-headCh: case head := <-headCh:
if done == nil { if done == nil {
done = make(chan struct{}) done = make(chan struct{})
if number := rawdb.ReadTxIndexTail(bc.db); number == nil { if tail := rawdb.ReadTxIndexTail(bc.db); tail == nil {
go initialiseIndices(head.Block.NumberU64(), done) go initialiseIndices(head.Block.NumberU64(), done)
} else { } else {
go indexBlocks(*number, head.Block.NumberU64(), done) go indexBlocks(*tail, head.Block.NumberU64(), done)
} }
} }
case <-done: case <-done:

View file

@ -199,15 +199,16 @@ func IndexTxLookup(db ethdb.Database, from uint64, to uint64) {
// writeIndices injects txlookup indices into the database. // writeIndices injects txlookup indices into the database.
writeIndices := func(batch ethdb.Batch, block *types.Block) { writeIndices := func(batch ethdb.Batch, block *types.Block) {
WriteTxLookupEntries(batch, block) WriteTxLookupEntries(batch, block)
if block.NumberU64()%1000000 == 0 { if block.NumberU64()%100000 == 0 {
WriteTxIndexTail(batch, block.NumberU64()) WriteTxIndexTail(batch, block.NumberU64())
} }
} }
start := time.Now()
if err := iterateCanonicalChain(db, from, to, "txlookup", hashTxs, writeIndices, true, true); err != nil { if err := iterateCanonicalChain(db, from, to, "txlookup", hashTxs, writeIndices, true, true); err != nil {
log.Crit("Failed to iterate canonical chain", "err", err) log.Crit("Failed to iterate canonical chain", "err", err)
} }
WriteTxIndexTail(db, from) WriteTxIndexTail(db, from)
log.Info("Constructed transaction indices", "from", from, "to", to, "count", to-from) log.Info("Constructed transaction indices", "from", from, "to", to, "count", to-from, "elapsed", common.PrettyDuration(time.Since(start)))
} }
// RemoveTxsLookup removes txlookup indices of the specified range blocks. // RemoveTxsLookup removes txlookup indices of the specified range blocks.

View file

@ -200,7 +200,7 @@ type BlockChain interface {
CurrentFastBlock() *types.Block CurrentFastBlock() *types.Block
// FastSyncCommitHead directly commits the head block to a certain entity. // FastSyncCommitHead directly commits the head block to a certain entity.
FastSyncCommitHead(common.Hash) error FastSyncCommitHead(common.Hash, uint64, uint64) error
// InsertChain inserts a batch of blocks into the local chain. // InsertChain inserts a batch of blocks into the local chain.
InsertChain(types.Blocks) (int, error) InsertChain(types.Blocks) (int, error)
@ -1721,7 +1721,11 @@ func (d *Downloader) commitPivotBlock(result *fetchResult) error {
if _, err := d.blockchain.InsertReceiptChain([]*types.Block{block}, []types.Receipts{result.Receipts}, d.ancientLimit); err != nil { if _, err := d.blockchain.InsertReceiptChain([]*types.Block{block}, []types.Receipts{result.Receipts}, d.ancientLimit); err != nil {
return err return err
} }
if err := d.blockchain.FastSyncCommitHead(block.Hash()); err != nil { // Use origin block number + 1 as the number of the first inserted block.
d.syncStatsLock.RLock()
from := d.syncStatsChainOrigin + 1
d.syncStatsLock.RUnlock()
if err := d.blockchain.FastSyncCommitHead(block.Hash(), from, d.ancientLimit); err != nil {
return err return err
} }
atomic.StoreInt32(&d.committed, 1) atomic.StoreInt32(&d.committed, 1)

View file

@ -218,7 +218,7 @@ func (dl *downloadTester) CurrentFastBlock() *types.Block {
} }
// FastSyncCommitHead manually sets the head block to a given hash. // FastSyncCommitHead manually sets the head block to a given hash.
func (dl *downloadTester) FastSyncCommitHead(hash common.Hash) error { func (dl *downloadTester) FastSyncCommitHead(hash common.Hash, from uint64, ancient uint64) error {
// For now only check that the state trie is correct // For now only check that the state trie is correct
if block := dl.GetBlockByHash(hash); block != nil { if block := dl.GetBlockByHash(hash); block != nil {
_, err := trie.NewSecure(block.Root(), trie.NewDatabase(dl.stateDb)) _, err := trie.NewSecure(block.Root(), trie.NewDatabase(dl.stateDb))

View file

@ -50,7 +50,7 @@ const (
// The number is referenced from the size of tx pool. // The number is referenced from the size of tx pool.
txChanSize = 4096 txChanSize = 4096
// minimim number of peers to broadcast new blocks to // minBroadcastPeers is the minimal number of peers to broadcast new blocks to.
minBroadcastPeers = 4 minBroadcastPeers = 4
) )