From a235b830a8de4519d3e631ad5e8be06a709ef020 Mon Sep 17 00:00:00 2001 From: Dave Collins Date: Fri, 8 Jun 2018 20:37:16 -0500 Subject: [PATCH] blockchain: Optimize exported header access. This optimizes the FetchHeader function to make use of the fact that all block nodes are now in memory and therefore it is no longer necessary to consult the database for them. It also renames the function to HeaderByHash so it is consistent with other similar functions such as HeaderByHeight, BlockByHash, and BlockByHeight and updates all callers in the repo accordingly. This speeds up fetching all of the headers in the chain by roughly 3x to 4x for a chain height of 246,000 headers. Longer chains will benefit more. For example, here is some timing information before and after this commit plus the full block index in memory commit for 246,000 headers: 7200 RPM HDD: ------------- Previous fetch time: ~15.76s New fetch time: ~4.17s SSD: ---- Previous fetch time: ~12.63s New fetch time: ~4.17s --- blockchain/blockindex_test.go | 6 +++--- blockchain/chain.go | 25 ++++++++----------------- rpcserver.go | 8 ++++---- 3 files changed, 15 insertions(+), 24 deletions(-) diff --git a/blockchain/blockindex_test.go b/blockchain/blockindex_test.go index efbfef46e6..bbbbc4c196 100644 --- a/blockchain/blockindex_test.go +++ b/blockchain/blockindex_test.go @@ -68,12 +68,12 @@ func TestBlockNodeHeader(t *testing.T) { // Ensure fetching the header from the chain produces the same header used // to create the node. testHeaderHash := testHeader.BlockHash() - gotHeader, err := bc.FetchHeader(&testHeaderHash) + gotHeader, err := bc.HeaderByHash(&testHeaderHash) if err != nil { - t.Fatalf("FetchHeader: unexpected error: %v", err) + t.Fatalf("HeaderByHash: unexpected error: %v", err) } if !reflect.DeepEqual(gotHeader, testHeader) { - t.Fatalf("FetchHeader: mismatched headers: got %+v, want %+v", + t.Fatalf("HeaderByHash: mismatched headers: got %+v, want %+v", gotHeader, testHeader) } } diff --git a/blockchain/chain.go b/blockchain/chain.go index 4eddf0fda5..2bf6aa5828 100644 --- a/blockchain/chain.go +++ b/blockchain/chain.go @@ -1681,27 +1681,18 @@ func (b *BlockChain) MaxBlockSize() (int64, error) { return maxSize, err } -// FetchHeader returns the block header identified by the given hash or an error -// if it doesn't exist. +// HeaderByHash returns the block header identified by the given hash or an +// error if it doesn't exist. Note that this will return headers from both the +// main chain and any side chains. // // This function is safe for concurrent access. -func (b *BlockChain) FetchHeader(hash *chainhash.Hash) (wire.BlockHeader, error) { - // Reconstruct the header from the block index if possible. - if node := b.index.LookupNode(hash); node != nil { - return node.Header(), nil +func (b *BlockChain) HeaderByHash(hash *chainhash.Hash) (wire.BlockHeader, error) { + node := b.index.LookupNode(hash) + if node == nil { + return wire.BlockHeader{}, fmt.Errorf("block %s is not known", hash) } - // Fall back to loading it from the database. - var header *wire.BlockHeader - err := b.db.View(func(dbTx database.Tx) error { - var err error - header, err = dbFetchHeaderByHash(dbTx, hash) - return err - }) - if err != nil { - return wire.BlockHeader{}, err - } - return *header, nil + return node.Header(), nil } // locateInventory returns the node of the block after the first known block in diff --git a/rpcserver.go b/rpcserver.go index 64aee43160..fdccf9a6d3 100644 --- a/rpcserver.go +++ b/rpcserver.go @@ -2069,7 +2069,7 @@ func handleGetBlockHeader(s *rpcServer, cmd interface{}, closeChan <-chan struct if err != nil { return nil, rpcDecodeHexError(c.Hash) } - blockHeader, err := s.chain.FetchHeader(hash) + blockHeader, err := s.chain.HeaderByHash(hash) if err != nil { return nil, &dcrjson.RPCError{ Code: dcrjson.ErrRPCBlockNotFound, @@ -3349,7 +3349,7 @@ func handleGetNetworkHashPS(s *rpcServer, cmd interface{}, closeChan <-chan stru } // Fetch the header from chain. - header, err := s.chain.FetchHeader(hash) + header, err := s.chain.HeaderByHash(hash) if err != nil { context := "Failed to fetch block header" return nil, rpcInternalError(err.Error(), context) @@ -3568,7 +3568,7 @@ func handleGetRawTransaction(s *rpcServer, cmd interface{}, closeChan <-chan str ) if blkHash != nil { // Fetch the header from chain. - header, err := s.chain.FetchHeader(blkHash) + header, err := s.chain.HeaderByHash(blkHash) if err != nil { context := "Failed to fetch block header" return nil, rpcInternalError(err.Error(), context) @@ -4960,7 +4960,7 @@ func handleSearchRawTransactions(s *rpcServer, cmd interface{}, closeChan <-chan var blkHeight int64 if blkHash := rtx.blkHash; blkHash != nil { // Fetch the header from chain. - header, err := s.chain.FetchHeader(blkHash) + header, err := s.chain.HeaderByHash(blkHash) if err != nil { return nil, &dcrjson.RPCError{ Code: dcrjson.ErrRPCBlockNotFound,