github.com/osdi23p228/fabric@v0.0.0-20221218062954-77808885f5db/common/ledger/blkstorage/blockfile_helper.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 "io/ioutil" 11 "os" 12 "path/filepath" 13 "strconv" 14 "strings" 15 16 "github.com/davecgh/go-spew/spew" 17 "github.com/golang/protobuf/proto" 18 "github.com/hyperledger/fabric-protos-go/common" 19 "github.com/pkg/errors" 20 ) 21 22 // constructBlockfilesInfo scans the last blockfile (if any) and construct the blockfilesInfo 23 // if the last file contains no block or only a partially written block (potentially because of a crash while writing block to the file), 24 // this scans the second last file (if any) 25 func constructBlockfilesInfo(rootDir string) (*blockfilesInfo, error) { 26 logger.Debugf("constructing BlockfilesInfo") 27 var lastFileNum int 28 var numBlocksInFile int 29 var endOffsetLastBlock int64 30 var lastBlockNumber uint64 31 32 var lastBlockBytes []byte 33 var lastBlock *common.Block 34 var err error 35 36 if lastFileNum, err = retrieveLastFileSuffix(rootDir); err != nil { 37 return nil, err 38 } 39 logger.Debugf("Last file number found = %d", lastFileNum) 40 41 if lastFileNum == -1 { 42 blkfilesInfo := &blockfilesInfo{ 43 latestFileNumber: 0, 44 latestFileSize: 0, 45 noBlockFiles: true, 46 lastPersistedBlock: 0, 47 } 48 logger.Debugf("No block file found") 49 return blkfilesInfo, nil 50 } 51 52 fileInfo := getFileInfoOrPanic(rootDir, lastFileNum) 53 logger.Debugf("Last Block file info: FileName=[%s], FileSize=[%d]", fileInfo.Name(), fileInfo.Size()) 54 if lastBlockBytes, endOffsetLastBlock, numBlocksInFile, err = scanForLastCompleteBlock(rootDir, lastFileNum, 0); err != nil { 55 logger.Errorf("Error scanning last file [num=%d]: %s", lastFileNum, err) 56 return nil, err 57 } 58 59 if numBlocksInFile == 0 && lastFileNum > 0 { 60 secondLastFileNum := lastFileNum - 1 61 fileInfo := getFileInfoOrPanic(rootDir, secondLastFileNum) 62 logger.Debugf("Second last Block file info: FileName=[%s], FileSize=[%d]", fileInfo.Name(), fileInfo.Size()) 63 if lastBlockBytes, _, _, err = scanForLastCompleteBlock(rootDir, secondLastFileNum, 0); err != nil { 64 logger.Errorf("Error scanning second last file [num=%d]: %s", secondLastFileNum, err) 65 return nil, err 66 } 67 } 68 69 if lastBlockBytes != nil { 70 if lastBlock, err = deserializeBlock(lastBlockBytes); err != nil { 71 logger.Errorf("Error deserializing last block: %s. Block bytes length: %d", err, len(lastBlockBytes)) 72 return nil, err 73 } 74 lastBlockNumber = lastBlock.Header.Number 75 } 76 77 blkfilesInfo := &blockfilesInfo{ 78 lastPersistedBlock: lastBlockNumber, 79 latestFileSize: int(endOffsetLastBlock), 80 latestFileNumber: lastFileNum, 81 noBlockFiles: lastFileNum == 0 && numBlocksInFile == 0, 82 } 83 logger.Debugf("blockfilesInfo constructed from file system = %s", spew.Sdump(blkfilesInfo)) 84 return blkfilesInfo, nil 85 } 86 87 // binarySearchFileNumForBlock locates the file number that contains the given block number. 88 // This function assumes that the caller invokes this function with a block number that has been committed 89 // For any uncommitted block, this function returns the last file present 90 func binarySearchFileNumForBlock(rootDir string, blockNum uint64) (int, error) { 91 blkfilesInfo, err := constructBlockfilesInfo(rootDir) 92 if err != nil { 93 return -1, err 94 } 95 96 beginFile := 0 97 endFile := blkfilesInfo.latestFileNumber 98 99 for endFile != beginFile { 100 searchFile := beginFile + (endFile-beginFile)/2 + 1 101 n, err := retrieveFirstBlockNumFromFile(rootDir, searchFile) 102 if err != nil { 103 return -1, err 104 } 105 switch { 106 case n == blockNum: 107 return searchFile, nil 108 case n > blockNum: 109 endFile = searchFile - 1 110 case n < blockNum: 111 beginFile = searchFile 112 } 113 } 114 return beginFile, nil 115 } 116 117 func retrieveFirstBlockNumFromFile(rootDir string, fileNum int) (uint64, error) { 118 s, err := newBlockfileStream(rootDir, fileNum, 0) 119 if err != nil { 120 return 0, err 121 } 122 defer s.close() 123 bb, err := s.nextBlockBytes() 124 if err != nil { 125 return 0, err 126 } 127 blockInfo, err := extractSerializedBlockInfo(bb) 128 if err != nil { 129 return 0, err 130 } 131 return blockInfo.blockHeader.Number, nil 132 } 133 134 func retrieveLastFileSuffix(rootDir string) (int, error) { 135 logger.Debugf("retrieveLastFileSuffix()") 136 biggestFileNum := -1 137 filesInfo, err := ioutil.ReadDir(rootDir) 138 if err != nil { 139 return -1, errors.Wrapf(err, "error reading dir %s", rootDir) 140 } 141 for _, fileInfo := range filesInfo { 142 name := fileInfo.Name() 143 if fileInfo.IsDir() || !isBlockFileName(name) { 144 logger.Debugf("Skipping File name = %s", name) 145 continue 146 } 147 fileSuffix := strings.TrimPrefix(name, blockfilePrefix) 148 fileNum, err := strconv.Atoi(fileSuffix) 149 if err != nil { 150 return -1, err 151 } 152 if fileNum > biggestFileNum { 153 biggestFileNum = fileNum 154 } 155 } 156 logger.Debugf("retrieveLastFileSuffix() - biggestFileNum = %d", biggestFileNum) 157 return biggestFileNum, err 158 } 159 160 func isBlockFileName(name string) bool { 161 return strings.HasPrefix(name, blockfilePrefix) 162 } 163 164 func getFileInfoOrPanic(rootDir string, fileNum int) os.FileInfo { 165 filePath := deriveBlockfilePath(rootDir, fileNum) 166 fileInfo, err := os.Lstat(filePath) 167 if err != nil { 168 panic(errors.Wrapf(err, "error retrieving file info for file number %d", fileNum)) 169 } 170 return fileInfo 171 } 172 173 func loadBootstrappingSnapshotInfo(rootDir string) (*BootstrappingSnapshotInfo, error) { 174 bsiBytes, err := ioutil.ReadFile(filepath.Join(rootDir, bootstrappingSnapshotInfoFile)) 175 if os.IsNotExist(err) { 176 return nil, nil 177 } 178 if err != nil { 179 return nil, errors.Wrapf(err, "error while reading bootstrappingSnapshotInfo file") 180 } 181 bsi := &BootstrappingSnapshotInfo{} 182 if err := proto.Unmarshal(bsiBytes, bsi); err != nil { 183 return nil, errors.Wrapf(err, "error while unmarshalling bootstrappingSnapshotInfo") 184 } 185 return bsi, nil 186 }