github.com/hechain20/hechain@v0.0.0-20220316014945-b544036ba106/common/ledger/blkstorage/rollback.go (about) 1 /* 2 Copyright hechain. All Rights Reserved. 3 4 SPDX-License-Identifier: Apache-2.0 5 */ 6 7 package blkstorage 8 9 import ( 10 "os" 11 12 "github.com/hechain20/hechain/common/ledger/util/leveldbhelper" 13 "github.com/hechain20/hechain/internal/fileutil" 14 "github.com/hechain20/hechain/protoutil" 15 "github.com/pkg/errors" 16 ) 17 18 type rollbackMgr struct { 19 ledgerID string 20 ledgerDir string 21 indexDir string 22 dbProvider *leveldbhelper.Provider 23 indexStore *blockIndex 24 targetBlockNum uint64 25 reusableBatch *leveldbhelper.UpdateBatch 26 } 27 28 // Rollback reverts changes made to the block store beyond a given block number. 29 func Rollback(blockStorageDir, ledgerID string, targetBlockNum uint64, indexConfig *IndexConfig) error { 30 r, err := newRollbackMgr(blockStorageDir, ledgerID, indexConfig, targetBlockNum) 31 if err != nil { 32 return err 33 } 34 defer r.dbProvider.Close() 35 36 if err := recordHeightIfGreaterThanPreviousRecording(r.ledgerDir); err != nil { 37 return err 38 } 39 40 logger.Infof("Rolling back block index to block number [%d]", targetBlockNum) 41 if err := r.rollbackBlockIndex(); err != nil { 42 return err 43 } 44 45 logger.Infof("Rolling back block files to block number [%d]", targetBlockNum) 46 if err := r.rollbackBlockFiles(); err != nil { 47 return err 48 } 49 50 return nil 51 } 52 53 func newRollbackMgr(blockStorageDir, ledgerID string, indexConfig *IndexConfig, targetBlockNum uint64) (*rollbackMgr, error) { 54 r := &rollbackMgr{} 55 56 r.ledgerID = ledgerID 57 conf := &Conf{blockStorageDir: blockStorageDir} 58 r.ledgerDir = conf.getLedgerBlockDir(ledgerID) 59 r.targetBlockNum = targetBlockNum 60 61 r.indexDir = conf.getIndexDir() 62 var err error 63 r.dbProvider, err = leveldbhelper.NewProvider( 64 &leveldbhelper.Conf{ 65 DBPath: r.indexDir, 66 ExpectedFormat: dataFormatVersion(indexConfig), 67 }, 68 ) 69 if err != nil { 70 return nil, err 71 } 72 indexDB := r.dbProvider.GetDBHandle(ledgerID) 73 r.indexStore, err = newBlockIndex(indexConfig, indexDB) 74 r.reusableBatch = r.indexStore.db.NewUpdateBatch() 75 return r, err 76 } 77 78 func (r *rollbackMgr) rollbackBlockIndex() error { 79 lastBlockNumber, err := r.indexStore.getLastBlockIndexed() 80 if err == errIndexSavePointKeyNotPresent { 81 return nil 82 } 83 if err != nil { 84 return err 85 } 86 87 // we remove index associated with only 10 blocks at a time 88 // to avoid overuse of memory occupied by the leveldb batch. 89 // If we assume a block size of 2000 transactions and 4 indices 90 // per transaction and 2 index per block, the total number of 91 // index keys to be added in the batch would be 80020. Even if a 92 // key takes 100 bytes (overestimation), the memory utilization 93 // of a batch of 10 blocks would be 7 MB only. 94 batchLimit := uint64(10) 95 96 // start each iteration of the loop with full range for deletion 97 // and shrink the range to batchLimit if the range is greater than batchLimit 98 start, end := r.targetBlockNum+1, lastBlockNumber 99 for end >= start { 100 if end-start >= batchLimit { 101 start = end - batchLimit + 1 // 1 is added as range is inclusive 102 } 103 logger.Infof("Deleting index associated with block number [%d] to [%d]", start, end) 104 if err := r.deleteIndexEntriesRange(start, end); err != nil { 105 return err 106 } 107 start, end = r.targetBlockNum+1, start-1 108 } 109 return nil 110 } 111 112 func (r *rollbackMgr) deleteIndexEntriesRange(startBlkNum, endBlkNum uint64) error { 113 // TODO: when more than half of the blocks' indices are to be deleted, it 114 // might be efficient to drop the whole index database rather than deleting 115 // entries. However, if there is more than more than 1 channel, dropping of 116 // index would impact the time taken to recover the peer. We need to analyze 117 // a bit before making a decision on rollback vs drop of index. FAB-15672 118 r.reusableBatch.Reset() 119 lp, err := r.indexStore.getBlockLocByBlockNum(startBlkNum) 120 if err != nil { 121 return err 122 } 123 124 stream, err := newBlockStream(r.ledgerDir, lp.fileSuffixNum, int64(lp.offset), -1) 125 if err != nil { 126 return err 127 } 128 defer stream.close() 129 130 numberOfBlocksToRetrieve := endBlkNum - startBlkNum + 1 131 for numberOfBlocksToRetrieve > 0 { 132 blockBytes, _, err := stream.nextBlockBytesAndPlacementInfo() 133 if err != nil { 134 return err 135 } 136 blockInfo, err := extractSerializedBlockInfo(blockBytes) 137 if err != nil { 138 return err 139 } 140 addIndexEntriesToBeDeleted(r.reusableBatch, blockInfo, r.indexStore) 141 numberOfBlocksToRetrieve-- 142 } 143 144 r.reusableBatch.Put(indexSavePointKey, encodeBlockNum(startBlkNum-1)) 145 return r.indexStore.db.WriteBatch(r.reusableBatch, true) 146 } 147 148 func addIndexEntriesToBeDeleted(batch *leveldbhelper.UpdateBatch, blockInfo *serializedBlockInfo, indexStore *blockIndex) error { 149 if indexStore.isAttributeIndexed(IndexableAttrBlockHash) { 150 batch.Delete(constructBlockHashKey(protoutil.BlockHeaderHash(blockInfo.blockHeader))) 151 } 152 153 if indexStore.isAttributeIndexed(IndexableAttrBlockNum) { 154 batch.Delete(constructBlockNumKey(blockInfo.blockHeader.Number)) 155 } 156 157 if indexStore.isAttributeIndexed(IndexableAttrBlockNumTranNum) { 158 for txIndex := range blockInfo.txOffsets { 159 batch.Delete(constructBlockNumTranNumKey(blockInfo.blockHeader.Number, uint64(txIndex))) 160 } 161 } 162 163 if indexStore.isAttributeIndexed(IndexableAttrTxID) { 164 for i, txOffset := range blockInfo.txOffsets { 165 batch.Delete(constructTxIDKey(txOffset.txID, blockInfo.blockHeader.Number, uint64(i))) 166 } 167 } 168 return nil 169 } 170 171 func (r *rollbackMgr) rollbackBlockFiles() error { 172 logger.Infof("Deleting blockfilesInfo") 173 if err := r.indexStore.db.Delete(blkMgrInfoKey, true); err != nil { 174 return err 175 } 176 // must not use index for block location search since the index can be behind the target block 177 targetFileNum, err := binarySearchFileNumForBlock(r.ledgerDir, r.targetBlockNum) 178 if err != nil { 179 return err 180 } 181 lastFileNum, err := retrieveLastFileSuffix(r.ledgerDir) 182 if err != nil { 183 return err 184 } 185 186 logger.Infof("Removing all block files with suffixNum in the range [%d] to [%d]", 187 targetFileNum+1, lastFileNum) 188 189 for n := lastFileNum; n >= targetFileNum+1; n-- { 190 filepath := deriveBlockfilePath(r.ledgerDir, n) 191 if err := os.Remove(filepath); err != nil { 192 return errors.Wrapf(err, "error removing the block file [%s]", filepath) 193 } 194 } 195 196 logger.Infof("Truncating block file [%d] to the end boundary of block number [%d]", targetFileNum, r.targetBlockNum) 197 endOffset, err := calculateEndOffSet(r.ledgerDir, targetFileNum, r.targetBlockNum) 198 if err != nil { 199 return err 200 } 201 202 filePath := deriveBlockfilePath(r.ledgerDir, targetFileNum) 203 if err := os.Truncate(filePath, endOffset); err != nil { 204 return errors.Wrapf(err, "error trucating the block file [%s]", filePath) 205 } 206 207 return nil 208 } 209 210 func calculateEndOffSet(ledgerDir string, targetBlkFileNum int, blockNum uint64) (int64, error) { 211 stream, err := newBlockfileStream(ledgerDir, targetBlkFileNum, 0) 212 if err != nil { 213 return 0, err 214 } 215 defer stream.close() 216 for { 217 blockBytes, err := stream.nextBlockBytes() 218 if err != nil { 219 return 0, err 220 } 221 blockInfo, err := extractSerializedBlockInfo(blockBytes) 222 if err != nil { 223 return 0, err 224 } 225 if blockInfo.blockHeader.Number == blockNum { 226 break 227 } 228 } 229 return stream.currentOffset, nil 230 } 231 232 // ValidateRollbackParams performs necessary validation on the input given for 233 // the rollback operation. 234 func ValidateRollbackParams(blockStorageDir, ledgerID string, targetBlockNum uint64) error { 235 logger.Infof("Validating the rollback parameters: ledgerID [%s], block number [%d]", 236 ledgerID, targetBlockNum) 237 conf := &Conf{blockStorageDir: blockStorageDir} 238 ledgerDir := conf.getLedgerBlockDir(ledgerID) 239 if err := validateLedgerID(ledgerDir, ledgerID); err != nil { 240 return err 241 } 242 if err := validateTargetBlkNum(ledgerDir, targetBlockNum); err != nil { 243 return err 244 } 245 return nil 246 } 247 248 func validateLedgerID(ledgerDir, ledgerID string) error { 249 logger.Debugf("Validating the existence of ledgerID [%s]", ledgerID) 250 exists, err := fileutil.DirExists(ledgerDir) 251 if err != nil { 252 return err 253 } 254 if !exists { 255 return errors.Errorf("ledgerID [%s] does not exist", ledgerID) 256 } 257 return nil 258 } 259 260 func validateTargetBlkNum(ledgerDir string, targetBlockNum uint64) error { 261 logger.Debugf("Validating the given block number [%d] against the ledger block height", targetBlockNum) 262 blkfilesInfo, err := constructBlockfilesInfo(ledgerDir) 263 if err != nil { 264 return err 265 } 266 if blkfilesInfo.lastPersistedBlock <= targetBlockNum { 267 return errors.Errorf("target block number [%d] should be less than the biggest block number [%d]", 268 targetBlockNum, blkfilesInfo.lastPersistedBlock) 269 } 270 return nil 271 }