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  }