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