github.com/turingchain2020/turingchain@v1.1.21/blockchain/cache.go (about)

     1  // Copyright Turing Corp. 2018 All Rights Reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package blockchain
     6  
     7  import (
     8  	"fmt"
     9  	"sync"
    10  
    11  	"github.com/turingchain2020/turingchain/types"
    12  )
    13  
    14  const defaultBlockHashCacheSize = 10000 //区块哈希缓存个数暂时采用固定值,约0.4MB内存占用
    15  
    16  //BlockCache 区块缓存
    17  type BlockCache struct {
    18  	hashCache     map[int64]string              //高度->区块哈希
    19  	blockCache    map[string]*types.BlockDetail //区块哈希->区块数据
    20  	blkCacheSize  int64
    21  	hashCacheSize int64
    22  	cacheLock     sync.RWMutex
    23  	//cacheQueue    *list.List
    24  	currentHeight int64 //当前区块高度
    25  	cfg           *types.TuringchainConfig
    26  }
    27  
    28  //newBlockCache new
    29  func newBlockCache(cfg *types.TuringchainConfig, blockHashCacheSize int64) *BlockCache {
    30  	blkCacheSize := cfg.GetModuleConfig().BlockChain.DefCacheSize
    31  	if blkCacheSize > blockHashCacheSize {
    32  		panic(fmt.Sprintf("newBlockCache: block cache size config must less than hash cache size, "+
    33  			"blkCacheSize=%d, blkHashCacheSize=%d", blkCacheSize, blockHashCacheSize))
    34  	}
    35  	return &BlockCache{
    36  		hashCache:     make(map[int64]string, blockHashCacheSize),
    37  		blockCache:    make(map[string]*types.BlockDetail, blkCacheSize),
    38  		blkCacheSize:  blkCacheSize,
    39  		hashCacheSize: blockHashCacheSize,
    40  		cfg:           cfg,
    41  	}
    42  }
    43  
    44  // GetBlockHash get block hash by height
    45  func (bc *BlockCache) GetBlockHash(height int64) []byte {
    46  	bc.cacheLock.RLock()
    47  	defer bc.cacheLock.RUnlock()
    48  	if hash, exist := bc.hashCache[height]; exist {
    49  		return []byte(hash)
    50  	}
    51  	return nil
    52  }
    53  
    54  //GetBlockByHeight 从cache缓存中获取block信息
    55  func (bc *BlockCache) GetBlockByHeight(height int64) *types.BlockDetail {
    56  	bc.cacheLock.RLock()
    57  	defer bc.cacheLock.RUnlock()
    58  
    59  	hash, ok := bc.hashCache[height]
    60  	if !ok {
    61  		return nil
    62  	}
    63  	if blk, ok := bc.blockCache[hash]; ok {
    64  		return blk
    65  	}
    66  	return nil
    67  }
    68  
    69  //GetBlockByHash 不做移动,cache最后的 128个区块
    70  func (bc *BlockCache) GetBlockByHash(hash []byte) (block *types.BlockDetail) {
    71  	bc.cacheLock.RLock()
    72  	defer bc.cacheLock.RUnlock()
    73  	if blk, ok := bc.blockCache[string(hash)]; ok {
    74  		return blk
    75  	}
    76  	return nil
    77  }
    78  
    79  //AddBlock 区块增长,添加block到cache中,方便快速查询
    80  func (bc *BlockCache) AddBlock(detail *types.BlockDetail) {
    81  	bc.cacheLock.Lock()
    82  	defer bc.cacheLock.Unlock()
    83  	if bc.currentHeight > 0 && detail.Block.Height != bc.currentHeight+1 {
    84  		chainlog.Error("...cacheBlock not continue...")
    85  		if types.Debug {
    86  			panic("...cacheBlock not continue...")
    87  		}
    88  	}
    89  
    90  	if len(detail.Receipts) == 0 && len(detail.Block.Txs) != 0 {
    91  		chainlog.Debug("cacheBlock  Receipts == 0", "height", detail.Block.GetHeight())
    92  	}
    93  	bc.currentHeight = detail.Block.Height
    94  	blkHash := string(detail.Block.Hash(bc.cfg))
    95  	// remove oldest key
    96  	delete(bc.hashCache, bc.currentHeight-bc.hashCacheSize)
    97  	delete(bc.blockCache, bc.hashCache[bc.currentHeight-bc.blkCacheSize])
    98  
    99  	// add new key
   100  	bc.hashCache[bc.currentHeight] = blkHash
   101  	bc.blockCache[blkHash] = detail
   102  }
   103  
   104  //DelBlock 区块回滚,删除block
   105  func (bc *BlockCache) DelBlock(height int64) {
   106  	bc.cacheLock.Lock()
   107  	defer bc.cacheLock.Unlock()
   108  	if bc.currentHeight > 0 && height != bc.currentHeight {
   109  		chainlog.Error("...del cacheBlock not continue...")
   110  		if types.Debug {
   111  			panic("...del cacheBlock not continue...")
   112  		}
   113  	}
   114  	bc.currentHeight = height - 1
   115  	// remove block first
   116  	delete(bc.blockCache, bc.hashCache[height])
   117  	delete(bc.hashCache, height)
   118  
   119  }
   120  
   121  type txHeightCacheType interface {
   122  	// Add add block
   123  	Add(block *types.Block)
   124  	// Del del block
   125  	Del(height int64)
   126  	// Contains check key if exist
   127  	Contains(key []byte, txHeight int64) bool
   128  }
   129  
   130  const txHashCacheLen = 16
   131  
   132  // txHashCache 缓存txHeight功能的交易哈希
   133  // 对于区块,只关心允许打包txHeight区间内的所有交易
   134  // 对于单笔交易,只需检查相同txHeight值的交易是否存在重复
   135  // 缓存采用双层map结构,交易按照txHeight值分类,提高map查重效率
   136  // 占用空间, (lowerTxHeightRange+upperTxHeightRange) * 单个区块平均交易数 * 16 (byte)
   137  // 1000 * 10000 * 16/1024/1024 = 150MB
   138  // 不考虑回滚处理,缓存空间进一步优化减少,TODO:实现上区分区块链是否可能回滚
   139  type txHashCache struct {
   140  	currBlockHeight    int64
   141  	lowerTxHeightRange int64                         //区块允许的下限txHeight范围
   142  	upperTxHeightRange int64                         //区块允许的上限txHeight范围
   143  	txHashes           map[int64]map[string]struct{} // [txHeight => [txHash=> struct]]
   144  	lock               *sync.RWMutex
   145  	chain              *BlockChain
   146  }
   147  
   148  //newTxHashCache new tx height cache type
   149  func newTxHashCache(chain *BlockChain, lowerTxHeightRange, upperTxHeightRange int64) *txHashCache {
   150  	return &txHashCache{
   151  		txHashes:           make(map[int64]map[string]struct{}, lowerTxHeightRange+upperTxHeightRange),
   152  		lock:               &sync.RWMutex{},
   153  		lowerTxHeightRange: lowerTxHeightRange,
   154  		upperTxHeightRange: upperTxHeightRange,
   155  		chain:              chain,
   156  	}
   157  }
   158  
   159  // Add :区块增长,缓存txHeight类交易的哈希, 缓存窗口前进一个高度
   160  func (tc *txHashCache) Add(block *types.Block) {
   161  	tc.lock.Lock()
   162  	defer tc.lock.Unlock()
   163  
   164  	tc.currBlockHeight = block.GetHeight()
   165  	tc.addTxList(block.Txs)
   166  	delHeight := tc.currBlockHeight - tc.upperTxHeightRange - tc.lowerTxHeightRange
   167  	if delHeight >= 0 {
   168  		delBlock, err := tc.chain.GetBlock(delHeight)
   169  		if err != nil {
   170  			//获取区块出错,将导致缓存异常
   171  			chainlog.Crit("txHashCache Add", "height", delHeight, "Get del Block err", err)
   172  		} else {
   173  			//对于新区快,所有txHeight<=tc.currBlockHeight - tc.lowerTxHeightRange都已经过期,可以从缓存中删除
   174  			//但批量删除可能导致区块回滚时再重新添加这些交易不好定位,因为这些交易可能分布在多个区块中, 这里只删除一个区块的交易
   175  			tc.delTxList(delBlock.Block.Txs)
   176  		}
   177  	}
   178  }
   179  
   180  // Del 回滚区块时是Add的逆向处理, 缓存窗口后退一个高度
   181  func (tc *txHashCache) Del(height int64) {
   182  
   183  	tc.lock.Lock()
   184  	defer tc.lock.Unlock()
   185  	tc.currBlockHeight = height - 1
   186  	//窗口向左移动一个高度,需要添加窗口中第一个区块内交易哈希
   187  	addHeight := height - tc.upperTxHeightRange - tc.lowerTxHeightRange
   188  	if addHeight >= 0 {
   189  		addBlock, err := tc.chain.GetBlock(addHeight)
   190  		if err != nil {
   191  			//获取区块出错,将导致缓存异常
   192  			chainlog.Crit("txHashCache Del", "height", height, "Get del Block err", err)
   193  			return
   194  		}
   195  		tc.addTxList(addBlock.Block.Txs)
   196  	}
   197  
   198  	delBlock, err := tc.chain.GetBlock(height)
   199  	if err != nil {
   200  		//获取区块出错,将导致缓存异常
   201  		chainlog.Crit("txHashCache Del", "height", height, "Get del Block err", err)
   202  		return
   203  	}
   204  	tc.delTxList(delBlock.Block.Txs)
   205  }
   206  
   207  func (tc *txHashCache) addTxList(txs []*types.Transaction) {
   208  
   209  	//只需要将expire为txHeight类型缓存
   210  	for _, tx := range txs {
   211  		txHeight := types.GetTxHeight(tc.chain.client.GetConfig(), tx.Expire, tc.currBlockHeight)
   212  		if txHeight < 0 {
   213  			continue
   214  		}
   215  		hashMap, ok := tc.txHashes[txHeight]
   216  		if !ok {
   217  			//预分配一定长度空间
   218  			hashMap = make(map[string]struct{}, 2<<10)
   219  			tc.txHashes[txHeight] = hashMap
   220  		}
   221  		hashMap[string(tx.Hash()[:txHashCacheLen])] = struct{}{}
   222  
   223  	}
   224  }
   225  
   226  func (tc *txHashCache) delTxList(txs []*types.Transaction) {
   227  
   228  	for _, tx := range txs {
   229  		txHeight := types.GetTxHeight(tc.chain.client.GetConfig(), tx.Expire, tc.currBlockHeight)
   230  		if hashMap, ok := tc.txHashes[txHeight]; ok {
   231  			delete(hashMap, string(tx.Hash()[:txHashCacheLen]))
   232  			if len(hashMap) == 0 {
   233  				delete(tc.txHashes, txHeight)
   234  			}
   235  		}
   236  	}
   237  }
   238  
   239  //Contains 缓存中是否包含该交易
   240  func (tc *txHashCache) Contains(hash []byte, txHeight int64) bool {
   241  	tc.lock.RLock()
   242  	defer tc.lock.RUnlock()
   243  
   244  	if hashMap, ok := tc.txHashes[txHeight]; ok {
   245  		_, exist := hashMap[string(hash[:txHashCacheLen])]
   246  		return exist
   247  	}
   248  	return false
   249  }
   250  
   251  // txHeight未开启情况,兼容blockchain调用,实现txHeightCacheType接口
   252  type noneCache struct{}
   253  
   254  func (n *noneCache) Add(*types.Block)            {}
   255  func (n *noneCache) Del(int64)                   {}
   256  func (n *noneCache) Contains([]byte, int64) bool { return false }