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  }