github.com/turingchain2020/turingchain@v1.1.21/blockchain/reduce.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 "sync/atomic" 9 "time" 10 11 dbm "github.com/turingchain2020/turingchain/common/db" 12 "github.com/turingchain2020/turingchain/types" 13 ) 14 15 // ReduceChain 精简chain 16 func (chain *BlockChain) ReduceChain() { 17 height := chain.GetBlockHeight() 18 cfg := chain.client.GetConfig() 19 if cfg.IsEnable("reduceLocaldb") { 20 // 精简localdb 21 chain.InitReduceLocalDB(height) 22 chain.reducewg.Add(1) 23 go chain.ReduceLocalDB() 24 } else { 25 flagHeight, _ := chain.blockStore.loadFlag(types.ReduceLocaldbHeight) //一旦开启reduceLocaldb,后续不能关闭 26 if flagHeight != 0 { 27 panic("toml config disable reduce localdb, but database enable reduce localdb") 28 } 29 } 30 } 31 32 // InitReduceLocalDB 初始化启动时候执行 33 func (chain *BlockChain) InitReduceLocalDB(height int64) { 34 flag, err := chain.blockStore.loadFlag(types.FlagReduceLocaldb) 35 if err != nil { 36 panic(err) 37 } 38 flagHeight, err := chain.blockStore.loadFlag(types.ReduceLocaldbHeight) 39 if err != nil { 40 panic(err) 41 } 42 if flag == 0 { 43 endHeight := height - SafetyReduceHeight //初始化时候执行精简高度要大于最大回滚高度 44 if endHeight > flagHeight { 45 chainlog.Info("start reduceLocaldb", "start height", flagHeight, "end height", endHeight) 46 chain.walkOver(flagHeight, endHeight, false, chain.reduceBodyInit, 47 func(batch dbm.Batch, height int64) { 48 height++ 49 batch.Set(types.ReduceLocaldbHeight, types.Encode(&types.Int64{Data: height})) 50 }) 51 // CompactRange执行将会阻塞仅仅做一次压缩 52 chainlog.Info("reduceLocaldb start compact db") 53 err = chain.blockStore.db.CompactRange(nil, nil) 54 chainlog.Info("reduceLocaldb end compact db", "error", err) 55 } 56 chain.blockStore.saveReduceLocaldbFlag() 57 } 58 } 59 60 // walkOver walk over 61 func (chain *BlockChain) walkOver(start, end int64, sync bool, fn func(batch dbm.Batch, height int64), 62 fnflag func(batch dbm.Batch, height int64)) { 63 // 删除 64 const batchDataSize = 1024 * 1024 * 10 65 newbatch := chain.blockStore.NewBatch(sync) 66 for i := start; i <= end; i++ { 67 fn(newbatch, i) 68 if newbatch.ValueSize() > batchDataSize { 69 // 记录当前高度 70 fnflag(newbatch, i) 71 dbm.MustWrite(newbatch) 72 newbatch.Reset() 73 chainlog.Info("reduceLocaldb", "height", i) 74 } 75 } 76 if newbatch.ValueSize() > 0 { 77 // 记录当前高度 78 fnflag(newbatch, end) 79 dbm.MustWrite(newbatch) 80 newbatch.Reset() 81 chainlog.Info("reduceLocaldb end", "height", end) 82 } 83 } 84 85 // reduceBodyInit 将body中的receipt进行精简;将TxHashPerfix为key的TxResult中的receipt和tx字段进行精简 86 func (chain *BlockChain) reduceBodyInit(batch dbm.Batch, height int64) { 87 blockDetail, err := chain.blockStore.LoadBlock(height, nil) 88 if err == nil { 89 cfg := chain.client.GetConfig() 90 kvs, err := delBlockReceiptTable(chain.blockStore.db, height, blockDetail.Block.Hash(cfg)) 91 if err != nil { 92 chainlog.Debug("reduceBody delBlockReceiptTable", "height", height, "error", err) 93 return 94 } 95 for _, kv := range kvs { 96 if kv.GetValue() == nil { 97 batch.Delete(kv.GetKey()) 98 } 99 } 100 chain.reduceIndexTx(batch, blockDetail.GetBlock()) 101 } 102 } 103 104 // reduceIndexTx 对数据库中的 hash-TX进行精简 105 func (chain *BlockChain) reduceIndexTx(batch dbm.Batch, block *types.Block) { 106 cfg := chain.client.GetConfig() 107 Txs := block.GetTxs() 108 for index, tx := range Txs { 109 hash := tx.Hash() 110 // 之前执行quickIndex时候未对无用hash做删除处理,占用空间,因此这里删除 111 if cfg.IsEnable("quickIndex") { 112 batch.Delete(hash) 113 } 114 115 // 为了提高效率组合生成txresult,不从数据库中读取已有txresult 116 txresult := &types.TxResult{ 117 Height: block.Height, 118 Index: int32(index), 119 Blocktime: block.BlockTime, 120 ActionName: tx.ActionName(), 121 } 122 batch.Set(cfg.CalcTxKey(hash), cfg.CalcTxKeyValue(txresult)) 123 } 124 } 125 126 func (chain *BlockChain) deleteTx(batch dbm.Batch, block *types.Block) { 127 cfg := chain.client.GetConfig() 128 Txs := block.GetTxs() 129 for _, tx := range Txs { 130 batch.Delete(cfg.CalcTxKey(tx.Hash())) 131 } 132 } 133 134 // reduceReceipts 精简receipts 135 func reduceReceipts(src *types.BlockBody) []*types.ReceiptData { 136 dst := src.Clone() 137 for i := 0; i < len(dst.Receipts); i++ { 138 for j := 0; j < len(dst.Receipts[i].Logs); j++ { 139 if dst.Receipts[i].Logs[j] != nil { 140 if dst.Receipts[i].Logs[j].Ty == types.TyLogErr { // 为了匹配界面显示 141 continue 142 } 143 dst.Receipts[i].Logs[j].Log = nil 144 } 145 } 146 } 147 return dst.Receipts 148 } 149 150 // ReduceLocalDB 实时精简localdb 151 func (chain *BlockChain) ReduceLocalDB() { 152 defer chain.reducewg.Done() 153 154 flagHeight, err := chain.blockStore.loadFlag(types.ReduceLocaldbHeight) 155 if err != nil { 156 panic(err) 157 } 158 if flagHeight < 0 { 159 flagHeight = 0 160 } 161 // 10s检测一次是否可以进行reduce localdb 162 checkTicker := time.NewTicker(10 * time.Second) 163 for { 164 select { 165 case <-chain.quit: 166 return 167 case <-checkTicker.C: 168 flagHeight = chain.TryReduceLocalDB(flagHeight, 100) 169 } 170 } 171 } 172 173 //TryReduceLocalDB TryReduce try reduce 174 func (chain *BlockChain) TryReduceLocalDB(flagHeight int64, rangeHeight int64) (newHeight int64) { 175 if rangeHeight <= 0 { 176 rangeHeight = 100 177 } 178 height := chain.GetBlockHeight() 179 safetyHeight := height - ReduceHeight 180 if safetyHeight/rangeHeight > flagHeight/rangeHeight { // 每隔rangeHeight区块进行一次精简 181 sync := true 182 if atomic.LoadInt32(&chain.isbatchsync) == 0 { 183 sync = false 184 } 185 chain.walkOver(flagHeight, safetyHeight, sync, chain.reduceBody, 186 func(batch dbm.Batch, height int64) { 187 // 记录的时候记录下一个,中断开始执行的就是下一个 188 height++ 189 batch.Set(types.ReduceLocaldbHeight, types.Encode(&types.Int64{Data: height})) 190 }) 191 flagHeight = safetyHeight + 1 192 chainlog.Debug("reduceLocaldb ticker", "current height", flagHeight) 193 return flagHeight 194 } 195 return flagHeight 196 } 197 198 // reduceBody 将body中的receipt进行精简; 199 func (chain *BlockChain) reduceBody(batch dbm.Batch, height int64) { 200 hash, err := chain.blockStore.GetBlockHashByHeight(height) 201 if err == nil { 202 kvs, err := delBlockReceiptTable(chain.blockStore.db, height, hash) 203 if err != nil { 204 chainlog.Debug("reduceBody delBlockReceiptTable", "height", height, "error", err) 205 return 206 } 207 for _, kv := range kvs { 208 if kv.GetValue() == nil { 209 batch.Delete(kv.GetKey()) 210 } 211 } 212 } 213 }