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 }