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  }