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  }