github.com/turingchain2020/turingchain@v1.1.21/blockchain/chunkshard.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 "errors" 9 "fmt" 10 "sync/atomic" 11 "time" 12 13 "github.com/turingchain2020/turingchain/common" 14 dbm "github.com/turingchain2020/turingchain/common/db" 15 "github.com/turingchain2020/turingchain/types" 16 ) 17 18 var ( 19 //ErrNoBlockToChunk ... 20 ErrNoBlockToChunk = errors.New("ErrNoBlockToChunk") 21 //ErrNoChunkInfoToDownLoad ... 22 ErrNoChunkInfoToDownLoad = errors.New("ErrNoChunkInfoToDownLoad") 23 ) 24 25 const ( 26 // OnceMaxChunkNum 每次检测最大生成chunk数 27 OnceMaxChunkNum int32 = 30 28 // DelRollbackChunkNum 删除小于当前chunk为DelRollbackChunkNum 29 DelRollbackChunkNum int32 = 10 30 // MaxReqChunkRecord 每次请求最大MaxReqChunkRecord个chunk的record 31 MaxReqChunkRecord int32 = 100 32 ) 33 34 func (chain *BlockChain) chunkProcessRoutine() { 35 defer chain.tickerwg.Done() 36 37 // 1.60s检测一次是否可以删除本地的body数据 38 // 2.10s检测一次是否可以触发归档操作 39 40 checkDelTicker := time.NewTicker(time.Minute) 41 checkGenChunkTicker := time.NewTicker(10 * time.Second) 42 for { 43 select { 44 case <-chain.quit: 45 return 46 case <-checkDelTicker.C: 47 go chain.CheckDeleteBlockBody() 48 case <-checkGenChunkTicker.C: 49 //主动查询当前未归档,然后进行触发 50 go chain.CheckGenChunkNum() 51 } 52 } 53 } 54 55 // CheckGenChunkNum 检测是否需要生成chunkNum 56 func (chain *BlockChain) CheckGenChunkNum() { 57 if !atomic.CompareAndSwapInt32(&chain.processingGenChunk, 0, 1) { 58 // 保证同一时刻只存在一个该协程 59 return 60 } 61 defer atomic.StoreInt32(&chain.processingGenChunk, 0) 62 safetyChunkNum, _, _ := chain.CalcSafetyChunkInfo(chain.GetBlockHeight()) 63 for i := int32(0); i < OnceMaxChunkNum; i++ { 64 chunkNum := chain.getMaxSerialChunkNum() + 1 65 if chunkNum > safetyChunkNum { 66 break 67 } 68 if err := chain.chunkShardHandle(chunkNum); err != nil { 69 break 70 } 71 if err := chain.updateMaxSerialChunkNum(chunkNum); err != nil { 72 break 73 } 74 } 75 } 76 77 // CheckDeleteBlockBody 检测是否需要删除已经归档BlockBody 78 func (chain *BlockChain) CheckDeleteBlockBody() { 79 if !atomic.CompareAndSwapInt32(&chain.processingDeleteChunk, 0, 1) { 80 // 保证同一时刻只存在一个该协程 81 return 82 } 83 atomic.StoreInt32(&chain.processingDeleteChunk, 1) 84 defer atomic.StoreInt32(&chain.processingDeleteChunk, 0) 85 const onceDelChunkNum = 100 // 每次walkOverDeleteChunk的最大删除chunk个数 86 var count int64 87 var kvs []*types.KeyValue 88 toDelete := chain.blockStore.GetMaxDeletedChunkNum() + 1 89 chainlog.Info("CheckDeleteBlockBody start", "start", toDelete) 90 91 for toDelete+int64(DelRollbackChunkNum) < atomic.LoadInt64(&chain.maxSerialChunkNum) && count < onceDelChunkNum { 92 chainlog.Info("CheckDeleteBlockBody toDelete", "toDelete", toDelete) 93 kv := chain.DeleteBlockBody(toDelete) 94 kvs = append(kvs, kv...) 95 toDelete++ 96 count++ 97 } 98 atomic.AddInt64(&chain.deleteChunkCount, count) 99 batch := chain.GetDB().NewBatch(true) 100 batch.Reset() 101 for _, kv := range kvs { 102 batch.Delete(kv.GetKey()) 103 } 104 if count != 0 { 105 dbm.MustWrite(batch) 106 if err := chain.blockStore.SetMaxDeletedChunkNum(toDelete - 1); err != nil { 107 chainlog.Error("CheckDeleteBlockBody", "SetMaxDeletedChunkNum error", err) 108 } 109 } 110 111 //删除超过100个chunk则进行数据库压缩 112 if atomic.LoadInt64(&chain.deleteChunkCount) >= 100 { 113 now := time.Now() 114 start := []byte("CHAIN-body-body-") 115 limit := make([]byte, len(start)) 116 copy(limit, start) 117 limit[len(limit)-1]++ 118 if err := chain.blockStore.db.CompactRange(start, limit); err != nil { 119 chainlog.Error("walkOverDeleteChunk", "CompactRange error", err) 120 return 121 } 122 chainlog.Info("walkOverDeleteChunk", "CompactRange time cost", time.Since(now)) 123 atomic.StoreInt64(&chain.deleteChunkCount, 0) 124 } 125 126 } 127 128 // DeleteBlockBody del chunk body 129 func (chain *BlockChain) DeleteBlockBody(chunkNum int64) []*types.KeyValue { 130 value, err := chain.blockStore.GetKey(calcChunkNumToHash(chunkNum)) 131 if err != nil { 132 return nil 133 } 134 chunk := &types.ChunkInfo{} 135 err = types.Decode(value, chunk) 136 if err != nil { 137 return nil 138 } 139 var kvs []*types.KeyValue 140 for i := chunk.Start; i <= chunk.End; i++ { 141 kv, err := chain.deleteBlockBody(i) 142 if err != nil { 143 continue 144 } 145 kvs = append(kvs, kv...) 146 } 147 return kvs 148 } 149 150 func (chain *BlockChain) deleteBlockBody(height int64) (kvs []*types.KeyValue, err error) { 151 hash, err := chain.blockStore.GetBlockHashByHeight(height) 152 if err != nil { 153 chainlog.Error("deleteBlockBody GetBlockHashByHeight", "height", height, "error", err) 154 return nil, err 155 } 156 kvs, err = delBlockBodyTable(chain.blockStore.db, height, hash) 157 if err != nil { 158 chainlog.Error("deleteBlockBody delBlockBodyTable", "height", height, "error", err) 159 return nil, err 160 } 161 return kvs, err 162 } 163 164 func (chain *BlockChain) chunkShardHandle(chunkNum int64) error { 165 // 1、计算当前chunk信息; 166 // 2、生成归档记录; 167 // 3、生成辅助删除信息; 168 // 4、保存归档记录信息; 169 // 5、更新chunk最大连续序列号 170 start := chunkNum * chain.cfg.ChunkblockNum 171 end := start + chain.cfg.ChunkblockNum - 1 172 chunkHash, bodys, err := chain.genChunkBlocks(start, end) 173 if err != nil { 174 chainlog.Error("chunkShardHandle", "chunkNum", chunkNum, "start", start, "end", end, "err", err) 175 return err 176 } 177 chunk := &types.ChunkInfo{ 178 ChunkNum: chunkNum, 179 ChunkHash: chunkHash, 180 Start: start, 181 End: end, 182 } 183 kvs := genChunkRecord(chunk, bodys) 184 chain.saveChunkRecord(kvs) 185 if err := chain.notifyStoreChunkToP2P(chunk); err != nil { 186 return err 187 } 188 chainlog.Info("chunkShardHandle", "chunkNum", chunk.ChunkNum, "start", start, "end", end, "chunkHash", common.ToHex(chunkHash)) 189 return nil 190 } 191 func (chain *BlockChain) getMaxSerialChunkNum() int64 { 192 return atomic.LoadInt64(&chain.maxSerialChunkNum) 193 } 194 195 func (chain *BlockChain) updateMaxSerialChunkNum(chunkNum int64) error { 196 err := chain.blockStore.SetMaxSerialChunkNum(chunkNum) 197 if err != nil { 198 return err 199 } 200 atomic.StoreInt64(&chain.maxSerialChunkNum, chunkNum) 201 return nil 202 } 203 204 func (chain *BlockChain) notifyStoreChunkToP2P(data *types.ChunkInfo) error { 205 if chain.client == nil { 206 chainlog.Error("notifyStoreChunkToP2P: chain client not bind message queue.") 207 return fmt.Errorf("no message queue") 208 } 209 210 req := &types.ChunkInfoMsg{ 211 ChunkHash: data.ChunkHash, 212 Start: data.Start, 213 End: data.End, 214 } 215 216 chainlog.Debug("notifyStoreChunkToP2P", "chunknum", data.ChunkNum, "block start height", 217 data.Start, "block end height", data.End, "chunk hash", common.ToHex(data.ChunkHash)) 218 219 msg := chain.client.NewMessage("p2p", types.EventNotifyStoreChunk, req) 220 err := chain.client.Send(msg, true) 221 if err != nil { 222 return err 223 } 224 resp, err := chain.client.Wait(msg) 225 if err != nil { 226 return err 227 } 228 229 if data, ok := resp.Data.(*types.Reply); ok && data.IsOk { 230 return nil 231 } 232 return fmt.Errorf("p2p process error") 233 } 234 235 func (chain *BlockChain) genChunkBlocks(start, end int64) ([]byte, *types.BlockBodys, error) { 236 var hashs types.ReplyHashes 237 var bodys types.BlockBodys 238 for i := start; i <= end; i++ { 239 detail, err := chain.blockStore.LoadBlock(i, nil) 240 if err != nil { 241 return nil, nil, err 242 } 243 body := chain.blockStore.BlockdetailToBlockBody(detail) 244 bodys.Items = append(bodys.Items, body) 245 hashs.Hashes = append(hashs.Hashes, body.Hash) 246 } 247 return hashs.Hash(), &bodys, nil 248 } 249 250 func (chain *BlockChain) saveChunkRecord(kvs []*types.KeyValue) { 251 chain.blockStore.mustSaveKvset(&types.LocalDBSet{KV: kvs}) 252 } 253 254 // GetChunkBlockBody 从localdb本地获取chunkbody 255 func (chain *BlockChain) GetChunkBlockBody(req *types.ChunkInfoMsg) (*types.BlockBodys, error) { 256 if req == nil || req.Start > req.End { 257 return nil, types.ErrInvalidParam 258 } 259 _, bodys, err := chain.genChunkBlocks(req.Start, req.End) 260 return bodys, err 261 } 262 263 // AddChunkRecord ... 264 func (chain *BlockChain) AddChunkRecord(req *types.ChunkRecords) { 265 dbset := &types.LocalDBSet{} 266 for _, info := range req.Infos { 267 dbset.KV = append(dbset.KV, &types.KeyValue{Key: calcRecvChunkNumToHash(info.ChunkNum), Value: types.Encode(info)}) 268 } 269 if len(dbset.KV) > 0 { 270 chain.blockStore.mustSaveKvset(dbset) 271 } 272 } 273 274 // GetChunkRecord ... 275 func (chain *BlockChain) GetChunkRecord(req *types.ReqChunkRecords) (*types.ChunkRecords, error) { 276 if req.Start > req.End { 277 return nil, types.ErrInvalidParam 278 } 279 rep := &types.ChunkRecords{} 280 for i := req.Start; i <= req.End; i++ { 281 key := append([]byte{}, calcChunkNumToHash(i)...) 282 value, err := chain.blockStore.GetKey(key) 283 if err != nil { 284 return nil, types.ErrNotFound 285 } 286 chunk := &types.ChunkInfo{} 287 err = types.Decode(value, chunk) 288 if err != nil { 289 return nil, err 290 } 291 rep.Infos = append(rep.Infos, chunk) 292 } 293 if len(rep.Infos) == 0 { 294 return nil, types.ErrNotFound 295 } 296 return rep, nil 297 } 298 299 // GetCurRecvChunkNum ... 300 func (chain *BlockChain) GetCurRecvChunkNum() int64 { 301 return chain.blockStore.getCurChunkNum(RecvChunkNumToHash) 302 } 303 304 // GetCurChunkNum ... 305 func (chain *BlockChain) GetCurChunkNum() int64 { 306 return chain.blockStore.getCurChunkNum(ChunkNumToHash) 307 } 308 309 // CalcSafetyChunkInfo 计算安全的chunkNum用于生成chunk时候或者删除时候 310 func (chain *BlockChain) CalcSafetyChunkInfo(height int64) (chunkNum, start, end int64) { 311 height = chain.calcSafetyChunkHeight(height) 312 if height < 0 { 313 return -1, 0, 0 314 } 315 return calcChunkInfo(chain.cfg, height) 316 } 317 318 func (chain *BlockChain) calcSafetyChunkHeight(height int64) int64 { 319 return height - MaxRollBlockNum - chain.cfg.ChunkblockNum 320 } 321 322 // CalcChunkInfo 主要用于计算验证 323 func (chain *BlockChain) CalcChunkInfo(height int64) (chunkNum, start, end int64) { 324 return calcChunkInfo(chain.cfg, height) 325 } 326 327 func calcChunkInfo(cfg *types.BlockChain, height int64) (chunkNum, start, end int64) { 328 chunkNum = height / cfg.ChunkblockNum 329 start = chunkNum * cfg.ChunkblockNum 330 end = start + cfg.ChunkblockNum - 1 331 return chunkNum, start, end 332 } 333 334 // genChunkRecord 生成归档索引 1:blockhash--->chunkhash 2:blockHeight--->chunkhash 335 func genChunkRecord(chunk *types.ChunkInfo, bodys *types.BlockBodys) []*types.KeyValue { 336 var kvs []*types.KeyValue 337 kvs = append(kvs, &types.KeyValue{Key: calcChunkNumToHash(chunk.ChunkNum), Value: types.Encode(chunk)}) 338 kvs = append(kvs, &types.KeyValue{Key: calcChunkHashToNum(chunk.ChunkHash), Value: types.Encode(chunk)}) 339 return kvs 340 }