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
- kvledger Provider
- LedgerMgr
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
}