github.com/kaituanwang/hyperledger@v2.0.1+incompatible/core/ledger/kvledger/history/db.go (about) 1 /* 2 Copyright IBM Corp. All Rights Reserved. 3 SPDX-License-Identifier: Apache-2.0 4 */ 5 6 package history 7 8 import ( 9 "github.com/hyperledger/fabric-protos-go/common" 10 "github.com/hyperledger/fabric/common/flogging" 11 "github.com/hyperledger/fabric/common/ledger/blkstorage" 12 "github.com/hyperledger/fabric/common/ledger/dataformat" 13 "github.com/hyperledger/fabric/common/ledger/util/leveldbhelper" 14 "github.com/hyperledger/fabric/core/ledger" 15 "github.com/hyperledger/fabric/core/ledger/kvledger/txmgmt/rwsetutil" 16 "github.com/hyperledger/fabric/core/ledger/kvledger/txmgmt/version" 17 "github.com/hyperledger/fabric/core/ledger/util" 18 protoutil "github.com/hyperledger/fabric/protoutil" 19 ) 20 21 var logger = flogging.MustGetLogger("history") 22 23 // DBProvider provides handle to HistoryDB for a given channel 24 type DBProvider struct { 25 leveldbProvider *leveldbhelper.Provider 26 } 27 28 // NewDBProvider instantiates DBProvider 29 func NewDBProvider(path string) (*DBProvider, error) { 30 logger.Debugf("constructing HistoryDBProvider dbPath=%s", path) 31 levelDBProvider, err := leveldbhelper.NewProvider( 32 &leveldbhelper.Conf{ 33 DBPath: path, 34 ExpectedFormatVersion: dataformat.Version20, 35 }, 36 ) 37 if err != nil { 38 return nil, err 39 } 40 return &DBProvider{ 41 leveldbProvider: levelDBProvider, 42 }, nil 43 } 44 45 // GetDBHandle gets the handle to a named database 46 func (p *DBProvider) GetDBHandle(name string) (*DB, error) { 47 return &DB{ 48 levelDB: p.leveldbProvider.GetDBHandle(name), 49 name: name, 50 }, 51 nil 52 } 53 54 // Close closes the underlying db 55 func (p *DBProvider) Close() { 56 p.leveldbProvider.Close() 57 } 58 59 // DB maintains and provides access to history data for a particular channel 60 type DB struct { 61 levelDB *leveldbhelper.DBHandle 62 name string 63 } 64 65 // Commit implements method in HistoryDB interface 66 func (d *DB) Commit(block *common.Block) error { 67 68 blockNo := block.Header.Number 69 //Set the starting tranNo to 0 70 var tranNo uint64 71 72 dbBatch := leveldbhelper.NewUpdateBatch() 73 74 logger.Debugf("Channel [%s]: Updating history database for blockNo [%v] with [%d] transactions", 75 d.name, blockNo, len(block.Data.Data)) 76 77 // Get the invalidation byte array for the block 78 txsFilter := util.TxValidationFlags(block.Metadata.Metadata[common.BlockMetadataIndex_TRANSACTIONS_FILTER]) 79 80 // write each tran's write set to history db 81 for _, envBytes := range block.Data.Data { 82 83 // If the tran is marked as invalid, skip it 84 if txsFilter.IsInvalid(int(tranNo)) { 85 logger.Debugf("Channel [%s]: Skipping history write for invalid transaction number %d", 86 d.name, tranNo) 87 tranNo++ 88 continue 89 } 90 91 env, err := protoutil.GetEnvelopeFromBlock(envBytes) 92 if err != nil { 93 return err 94 } 95 96 payload, err := protoutil.UnmarshalPayload(env.Payload) 97 if err != nil { 98 return err 99 } 100 101 chdr, err := protoutil.UnmarshalChannelHeader(payload.Header.ChannelHeader) 102 if err != nil { 103 return err 104 } 105 106 if common.HeaderType(chdr.Type) == common.HeaderType_ENDORSER_TRANSACTION { 107 // extract RWSet from transaction 108 respPayload, err := protoutil.GetActionFromEnvelope(envBytes) 109 if err != nil { 110 return err 111 } 112 txRWSet := &rwsetutil.TxRwSet{} 113 if err = txRWSet.FromProtoBytes(respPayload.Results); err != nil { 114 return err 115 } 116 // add a history record for each write 117 for _, nsRWSet := range txRWSet.NsRwSets { 118 ns := nsRWSet.NameSpace 119 120 for _, kvWrite := range nsRWSet.KvRwSet.Writes { 121 dataKey := constructDataKey(ns, kvWrite.Key, blockNo, tranNo) 122 // No value is required, write an empty byte array (emptyValue) since Put() of nil is not allowed 123 dbBatch.Put(dataKey, emptyValue) 124 } 125 } 126 127 } else { 128 logger.Debugf("Skipping transaction [%d] since it is not an endorsement transaction\n", tranNo) 129 } 130 tranNo++ 131 } 132 133 // add savepoint for recovery purpose 134 height := version.NewHeight(blockNo, tranNo) 135 dbBatch.Put(savePointKey, height.ToBytes()) 136 137 // write the block's history records and savepoint to LevelDB 138 // Setting snyc to true as a precaution, false may be an ok optimization after further testing. 139 if err := d.levelDB.WriteBatch(dbBatch, true); err != nil { 140 return err 141 } 142 143 logger.Debugf("Channel [%s]: Updates committed to history database for blockNo [%v]", d.name, blockNo) 144 return nil 145 } 146 147 // NewQueryExecutor implements method in HistoryDB interface 148 func (d *DB) NewQueryExecutor(blockStore blkstorage.BlockStore) (ledger.HistoryQueryExecutor, error) { 149 return &QueryExecutor{d.levelDB, blockStore}, nil 150 } 151 152 // GetLastSavepoint implements returns the height till which the history is present in the db 153 func (d *DB) GetLastSavepoint() (*version.Height, error) { 154 versionBytes, err := d.levelDB.Get(savePointKey) 155 if err != nil || versionBytes == nil { 156 return nil, err 157 } 158 height, _, err := version.NewHeightFromBytes(versionBytes) 159 if err != nil { 160 return nil, err 161 } 162 return height, nil 163 } 164 165 // ShouldRecover implements method in interface kvledger.Recoverer 166 func (d *DB) ShouldRecover(lastAvailableBlock uint64) (bool, uint64, error) { 167 savepoint, err := d.GetLastSavepoint() 168 if err != nil { 169 return false, 0, err 170 } 171 if savepoint == nil { 172 return true, 0, nil 173 } 174 return savepoint.BlockNum != lastAvailableBlock, savepoint.BlockNum + 1, nil 175 } 176 177 // Name returns the name of the database that manages historical states. 178 func (d *DB) Name() string { 179 return "history" 180 } 181 182 // CommitLostBlock implements method in interface kvledger.Recoverer 183 func (d *DB) CommitLostBlock(blockAndPvtdata *ledger.BlockAndPvtData) error { 184 block := blockAndPvtdata.Block 185 186 // log every 1000th block at Info level so that history rebuild progress can be tracked in production envs. 187 if block.Header.Number%1000 == 0 { 188 logger.Infof("Recommitting block [%d] to history database", block.Header.Number) 189 } else { 190 logger.Debugf("Recommitting block [%d] to history database", block.Header.Number) 191 } 192 193 if err := d.Commit(block); err != nil { 194 return err 195 } 196 return nil 197 }