github.com/decred/dcrlnd@v0.7.6/blockcache/blockcache.go (about)

     1  package blockcache
     2  
     3  import (
     4  	"context"
     5  
     6  	"github.com/decred/dcrd/chaincfg/chainhash"
     7  	"github.com/decred/dcrd/dcrutil/v4"
     8  	"github.com/decred/dcrd/wire"
     9  	"github.com/decred/dcrlnd/lntypes"
    10  	"github.com/decred/dcrlnd/multimutex"
    11  	cache "github.com/decred/dcrlnd/neutrinocache"
    12  	"github.com/decred/dcrlnd/neutrinocache/lru"
    13  )
    14  
    15  // BlockCache is an lru cache for blocks.
    16  type BlockCache struct {
    17  	Cache     *lru.Cache
    18  	HashMutex *multimutex.HashMutex
    19  }
    20  
    21  // NewBlockCache creates a new BlockCache with the given maximum capacity.
    22  func NewBlockCache(capacity uint64) *BlockCache {
    23  	return &BlockCache{
    24  		Cache:     lru.NewCache(capacity),
    25  		HashMutex: multimutex.NewHashMutex(),
    26  	}
    27  }
    28  
    29  // GetBlock first checks to see if the BlockCache already contains the block
    30  // with the given hash. If it does then the block is fetched from the cache and
    31  // returned. Otherwise the getBlockImpl function is used in order to fetch the
    32  // new block and then it is stored in the block cache and returned.
    33  func (bc *BlockCache) GetBlock(ctx context.Context, hash *chainhash.Hash,
    34  	getBlockImpl func(ctx context.Context, hash *chainhash.Hash) (*wire.MsgBlock,
    35  		error)) (*wire.MsgBlock, error) {
    36  
    37  	// A nil BlockCache performs no caching.
    38  	if bc == nil {
    39  		return getBlockImpl(ctx, hash)
    40  	}
    41  
    42  	bc.HashMutex.Lock(lntypes.Hash(*hash))
    43  	defer bc.HashMutex.Unlock(lntypes.Hash(*hash))
    44  
    45  	// Create an inv vector for getting the block.
    46  	inv := wire.NewInvVect(wire.InvTypeBlock, hash)
    47  
    48  	// Check if the block corresponding to the given hash is already
    49  	// stored in the blockCache and return it if it is.
    50  	cacheBlock, err := bc.Cache.Get(*inv)
    51  	if err != nil && err != cache.ErrElementNotFound {
    52  		return nil, err
    53  	}
    54  	if cacheBlock != nil {
    55  		return cacheBlock.(*cache.CacheableBlock).MsgBlock(), nil
    56  	}
    57  
    58  	// Fetch the block from the chain backends.
    59  	block, err := getBlockImpl(ctx, hash)
    60  	if err != nil {
    61  		return nil, err
    62  	}
    63  
    64  	// Add the new block to blockCache. If the Cache is at its maximum
    65  	// capacity then the LFU item will be evicted in favour of this new
    66  	// block.
    67  	_, err = bc.Cache.Put(
    68  		*inv, &cache.CacheableBlock{
    69  			Block: dcrutil.NewBlock(block),
    70  		},
    71  	)
    72  	if err != nil {
    73  		return nil, err
    74  	}
    75  
    76  	return block, nil
    77  }