fabric 2.0, kvledger

The kvledger is used by Peer to manage the ledgers for all channels.

  • Peer Instance
    • LedgerMgr
      • kvledger Provider
        • idStore
        • ledgerstorage.Provider
        • configHistoryMgr->configHistoryDBProvider
        • privacyenabledstate.DBProvider
        • history.DBProvider
        • bookkeeping.Provider

LedgerMgr will also create a global cceventmgmt.Mgr for ccEvent. Each channel will register to it later.

LedgerMgr

It has a map for all opened ledgers and the ledgerProvider.

type LedgerMgr struct {
	lock               sync.Mutex
	openedLedgers      map[string]ledger.PeerLedger
	ledgerProvider     ledger.PeerLedgerProvider
	ebMetadataProvider MetadataProvider
}

kvledger provider

// Provider implements interface ledger.PeerLedgerProvider
type Provider struct {
	idStore             *idStore
	ledgerStoreProvider *ledgerstorage.Provider
	vdbProvider         privacyenabledstate.DBProvider
	historydbProvider   *history.DBProvider
	configHistoryMgr    confighistory.Mgr
	stateListeners      []ledger.StateListener
	bookkeepingProvider bookkeeping.Provider
	initializer         *ledger.Initializer
	collElgNotifier     *collElgNotifier
	stats               *stats
	fileLock            *leveldbhelper.FileLock
	hasher              ledger.Hasher
}

// NewProvider instantiates a new Provider.
// This is not thread-safe and assumed to be synchronized by the caller
func NewProvider(initializer *ledger.Initializer) (pr *Provider, e error) {
	p := &Provider{
		initializer: initializer,
		hasher:      initializer.Hasher,
	}

	defer func() {
		if e != nil {
			p.Close()
			if errFormatMismatch, ok := e.(*dataformat.ErrVersionMismatch); ok {
				if errFormatMismatch.Version == dataformat.Version1x && errFormatMismatch.ExpectedVersion == dataformat.Version20 {
					logger.Errorf("Please execute the 'peer node upgrade-dbs' command to upgrade the database format: %s", errFormatMismatch)
				} else {
					logger.Errorf("Please check the Fabric version matches the ledger data format: %s", errFormatMismatch)
				}
			}
		}
	}()

	fileLockPath := fileLockPath(initializer.Config.RootFSPath)
	fileLock := leveldbhelper.NewFileLock(fileLockPath)
	if err := fileLock.Lock(); err != nil {
		return nil, errors.Wrap(err, "as another peer node command is executing,"+
			" wait for that command to complete its execution or terminate it before retrying")
	}

	p.fileLock = fileLock

	if err := p.initLedgerIDInventory(); err != nil {
		return nil, err
	}

	if err := p.initLedgerStorageProvider(); err != nil {
		return nil, err
	}

	if err := p.initHistoryDBProvider(); err != nil {
		return nil, err
	}

	if err := p.initConfigHistoryManager(); err != nil {
		return nil, err
	}

	p.initCollElgNotifier()

	p.initStateListeners()

	if err := p.initStateDBProvider(); err != nil {
		return nil, err
	}

	p.initLedgerStatistics()

	p.recoverUnderConstructionLedger()

	return p, nil
}

file lock for ledger management

/var/hyperledger/production/ledgersData/fileLock

idStore

Made by initLedgerIDInventory(). It is a DB storing all ledger IDs. DB path is:

/var/hyperledger/production/ledgersData/ledgerProvider

A bit more worth mentioning is that this DB is using leveldbHelper directly, which means no logical DB is supported in idStore.

ledgerstoreage Provider

Made by initLedgerStorageProvider(). It is used by Peer to create ledgers for channels. Similar to ledgerFacory used by Orderer, it will create the underlying blockStoreProvider. Note it will initialize the blockStoreProvider with all 4 kinds of index supported.

And it will also create a pvtdatastorage provider for private data.


// NewProvider returns the handle to the provider
func NewProvider(blockStoreDir string, conf *pvtdatastorage.PrivateDataConfig, metricsProvider metrics.Provider) (*Provider, error) {
	// Initialize the block storage
	indexConfig := &blkstorage.IndexConfig{AttrsToIndex: attrsToIndex}
	blockStoreProvider, err := fsblkstorage.NewProvider(
		fsblkstorage.NewConf(
			blockStoreDir,
			maxBlockFileSize,
		),
		indexConfig,
		metricsProvider,
	)
	if err != nil {
		return nil, err
	}
	pvtStoreProvider, err := pvtdatastorage.NewProvider(conf)
	if err != nil {
		return nil, err
	}
	return &Provider{blockStoreProvider, pvtStoreProvider}, nil
}

  • blockStoreProvider is a fsblockStoreProvider
  • pvtStoreProvider is a leveldbProvider

DB Path:

/var/hyperledger/production/ledgersData/chains/index	        blockStoreProvider
/var/hyperledger/production/ledgersData/pvtdataStore			pvtStoreProvider

history DBProvider

It is a levelDBProvider. Path:

/var/hyperledger/production/ledgersData/historyLeveldb

confighistory.Mgr

It has a DB provider, and ccInfoProvider. The DB Path:

/var/hyperledger/production/ledgersData/configHistory
// Mgr should be registered as a state listener. The state listener builds the history and retriever helps in querying the history
type Mgr interface {
	ledger.StateListener
	GetRetriever(ledgerID string, ledgerInfoRetriever LedgerInfoRetriever) ledger.ConfigHistoryRetriever
	Close()
}

type mgr struct {
	ccInfoProvider ledger.DeployedChaincodeInfoProvider
	dbProvider     *dbProvider
}

collElgNotifier

It listens for the chaincode events and determines whether the peer has become eligible for one or more existing private data collections and notifies the registered listener

type collElgNotifier struct {
	deployedChaincodeInfoProvider ledger.DeployedChaincodeInfoProvider
	membershipInfoProvider        ledger.MembershipInfoProvider
	listeners                     map[string]collElgListener
}

initStateListeners()

It will add collElgNotifier and confighistory to ledger.stateListeners.

CommonStorageDBProvider

It will creates bookkeeper levelDB.Provider and VersionedDBProvider. This CommonStorageDBProvider can be used to create the state DB interface for a ledger/channel.

/var/hyperledger/production/ledgersData/bookkeeper		Bookkeeper
/var/hyperledger/production/ledgersData/stateLeveldb		VersionedDBProvider
// DBProvider provides handle to a PvtVersionedDB
type DBProvider interface {
	// GetDBHandle returns a handle to a PvtVersionedDB
	GetDBHandle(id string) (DB, error)
	// Close closes all the PvtVersionedDB instances and releases any resources held by VersionedDBProvider
	Close()
}
// DB extends VersionedDB interface. This interface provides additional functions for managing private data state
type DB interface {
	statedb.VersionedDB
	IsBulkOptimizable() bool
	LoadCommittedVersionsOfPubAndHashedKeys(pubKeys []*statedb.CompositeKey, hashedKeys []*HashedCompositeKey) error
	GetCachedKeyHashVersion(namespace, collection string, keyHash []byte) (*version.Height, bool)
	ClearCachedVersions()
	GetChaincodeEventListener() cceventmgmt.ChaincodeLifecycleEventListener
	GetPrivateData(namespace, collection, key string) (*statedb.VersionedValue, error)
	GetPrivateDataHash(namespace, collection, key string) (*statedb.VersionedValue, error)
	GetValueHash(namespace, collection string, keyHash []byte) (*statedb.VersionedValue, error)
	GetKeyHashVersion(namespace, collection string, keyHash []byte) (*version.Height, error)
	GetPrivateDataMultipleKeys(namespace, collection string, keys []string) ([]*statedb.VersionedValue, error)
	GetPrivateDataRangeScanIterator(namespace, collection, startKey, endKey string) (statedb.ResultsIterator, error)
	GetStateMetadata(namespace, key string) ([]byte, error)
	GetPrivateDataMetadataByHash(namespace, collection string, keyHash []byte) ([]byte, error)
	ExecuteQueryOnPrivateData(namespace, collection, query string) (statedb.ResultsIterator, error)
	ApplyPrivacyAwareUpdates(updates *UpdateBatch, height *version.Height) error
}
VersionedDBProvider and VersionedDB

These two interfaces:


// VersionedDBProvider provides an instance of an versioned DB
type VersionedDBProvider interface {
	// GetDBHandle returns a handle to a VersionedDB
	GetDBHandle(id string) (VersionedDB, error)
	// Close closes all the VersionedDB instances and releases any resources held by VersionedDBProvider
	Close()
}

// VersionedDB lists methods that a db is supposed to implement
type VersionedDB interface {
	// GetState gets the value for given namespace and key. For a chaincode, the namespace corresponds to the chaincodeId
	GetState(namespace string, key string) (*VersionedValue, error)
	// GetVersion gets the version for given namespace and key. For a chaincode, the namespace corresponds to the chaincodeId
	GetVersion(namespace string, key string) (*version.Height, error)
	// GetStateMultipleKeys gets the values for multiple keys in a single call
	GetStateMultipleKeys(namespace string, keys []string) ([]*VersionedValue, error)
	// GetStateRangeScanIterator returns an iterator that contains all the key-values between given key ranges.
	// startKey is inclusive
	// endKey is exclusive
	// The returned ResultsIterator contains results of type *VersionedKV
	GetStateRangeScanIterator(namespace string, startKey string, endKey string) (ResultsIterator, error)
	// GetStateRangeScanIteratorWithMetadata returns an iterator that contains all the key-values between given key ranges.
	// startKey is inclusive
	// endKey is exclusive
	// metadata is a map of additional query parameters
	// The returned ResultsIterator contains results of type *VersionedKV
	GetStateRangeScanIteratorWithMetadata(namespace string, startKey string, endKey string, metadata map[string]interface{}) (QueryResultsIterator, error)
	// ExecuteQuery executes the given query and returns an iterator that contains results of type *VersionedKV.
	ExecuteQuery(namespace, query string) (ResultsIterator, error)
	// ExecuteQueryWithMetadata executes the given query with associated query options and
	// returns an iterator that contains results of type *VersionedKV.
	// metadata is a map of additional query parameters
	ExecuteQueryWithMetadata(namespace, query string, metadata map[string]interface{}) (QueryResultsIterator, error)
	// ApplyUpdates applies the batch to the underlying db.
	// height is the height of the highest transaction in the Batch that
	// a state db implementation is expected to ues as a save point
	ApplyUpdates(batch *UpdateBatch, height *version.Height) error
	// GetLatestSavePoint returns the height of the highest transaction upto which
	// the state db is consistent
	GetLatestSavePoint() (*version.Height, error)
	// ValidateKeyValue tests whether the key and value is supported by the db implementation.
	// For instance, leveldb supports any bytes for the key while the couchdb supports only valid utf-8 string
	// TODO make the function ValidateKeyValue return a specific error say ErrInvalidKeyValue
	// However, as of now, the both implementations of this function (leveldb and couchdb) are deterministic in returing an error
	// i.e., an error is returned only if the key-value are found to be invalid for the underlying db
	ValidateKeyValue(key string, value []byte) error
	// BytesKeySupported returns true if the implementation (underlying db) supports the any bytes to be used as key.
	// For instance, leveldb supports any bytes for the key while the couchdb supports only valid utf-8 string
	BytesKeySupported() bool
	// Open opens the db
	Open() error
	// Close closes the db
	Close()
}

recoverUnderConstructionLedger

It checks whether the under construction flag is set - this would be the case if a crash had happened during creation of ledger and the ledger creation could have been left in intermediate state. Recovery checks if the ledger was created and the genesis block was committed successfully then it completes the last step of adding the ledger id to the list of created ledgers. Else, it clears the under construction flag.


How to create a ledger

kvLedger

When LedgerMgr trys to open a ledger for a channle, it will call LedgerMgr.OpenLedger(), and eventually invoke ledgerProvider.openInternal():

  • p.ledgerStoreProvider.Open(ledgerid)
    • blkStoreProvider.OpenBlockStore(ledgerid), make block store for ledger
    • pvtdataStoreProvider.OpenStore(ledgerid), make private data store for ledger
  • p.vdbProvider.GetDBHandle(ledgerID), make the versioned database (state database) for ledger
  • historydbProvider.GetDBHandle(ledgerID), make the history database (index for history of values by key) for a chain/ledger
  • Make the kvLedger
    • initTxMgr()
    • initBlockStore()
    • lastPersistedCommitHash()
    • cceventmgmt.GetMgr().Register(ledgerID, ccEventListener)
    • ccLifecycleEventProvider.RegisterListener(l.ledgerID, &ccEventListenerAdaptor{ccEventListener})
    • l.recoverDBs()
    • l.configHistoryRetriever = configHistoryMgr.GetRetriever(ledgerID, l)
type kvLedger struct {
	ledgerID               string
	blockStore             *ledgerstorage.Store
	txtmgmt                txmgr.TxMgr
	historyDB              *history.DB
	configHistoryRetriever ledger.ConfigHistoryRetriever
	blockAPIsRWLock        *sync.RWMutex
	stats                  *ledgerStats
	commitHash             []byte
}

func (p *Provider) openInternal(ledgerID string) (ledger.PeerLedger, error) {
	// Get the block store for a chain/ledger
	blockStore, err := p.ledgerStoreProvider.Open(ledgerID)
	if err != nil {
		return nil, err
	}
	p.collElgNotifier.registerListener(ledgerID, blockStore)

	// Get the versioned database (state database) for a chain/ledger
	vDB, err := p.vdbProvider.GetDBHandle(ledgerID)
	if err != nil {
		return nil, err
	}

	// Get the history database (index for history of values by key) for a chain/ledger
	var historyDB *history.DB
	if p.historydbProvider != nil {
		historyDB, err = p.historydbProvider.GetDBHandle(ledgerID)
		if err != nil {
			return nil, err
		}
	}

	// Create a kvLedger for this chain/ledger, which encapsulates the underlying data stores
	// (id store, blockstore, state database, history database)
	l, err := newKVLedger(
		ledgerID,
		blockStore,
		vDB,
		historyDB,
		p.configHistoryMgr,
		p.stateListeners,
		p.bookkeepingProvider,
		p.initializer.DeployedChaincodeInfoProvider,
		p.initializer.ChaincodeLifecycleEventProvider,
		p.stats.ledgerStats(ledgerID),
		p.initializer.CustomTxProcessors,
		p.hasher,
	)
	if err != nil {
		return nil, err
	}
	return l, nil
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值