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