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