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