區塊鏈教程Fabric1.0源代碼分析Ledger historydb歷史數據庫

1、historydb概述

historydb,用於存儲所有塊讀寫集中寫集的內容。
代碼分佈在core/ledger/kvledger/history/historydb目錄下,目錄結構如下:

  • historydb.go,定義核心接口HistoryDBProvider和HistoryDB。
  • histmgr_helper.go,historydb工具函數。
  • historyleveldb目錄,historydb基於leveldb的實現。
        * historyleveldb.go,HistoryDBProvider和HistoryDB接口實現,即historyleveldb.HistoryDBProvider和historyleveldb.historyDB結構體及方法。

    * historyleveldb_query_executer.go,定義LevelHistoryDBQueryExecutor和historyScanner結構體及方法。

2、核心接口定義

HistoryDBProvider接口定義:

type HistoryDBProvider interface {
    GetDBHandle(id string) (HistoryDB, error) //獲取HistoryDB
    Close() //關閉所有HistoryDB
}
//代碼在core/ledger/kvledger/history/historydb/historydb.go

HistoryDB接口定義:

type HistoryDB interface {
    //構造 LevelHistoryDBQueryExecutor
    NewHistoryQueryExecutor(blockStore blkstorage.BlockStore) (ledger.HistoryQueryExecutor, error)
    //提交Block入historyDB
    Commit(block *common.Block) error
    //獲取savePointKey,即version.Height
    GetLastSavepoint() (*version.Height, error)
    //是否應該恢復,比較lastAvailableBlock和Savepoint
    ShouldRecover(lastAvailableBlock uint64) (bool, uint64, error)
    //提交丟失的塊
    CommitLostBlock(block *common.Block) error
}
//代碼在core/ledger/kvledger/history/historydb/historydb.go

補充ledger.HistoryQueryExecutor接口定義:執行歷史記錄查詢。

type HistoryQueryExecutor interface {
    GetHistoryForKey(namespace string, key string) (commonledger.ResultsIterator, error) //按key查歷史記錄
}
//代碼在core/ledger/ledger_interface.go

3、historydb工具函數

//構造複合HistoryKey,ns 0x00 key 0x00 blocknum trannum
func ConstructCompositeHistoryKey(ns string, key string, blocknum uint64, trannum uint64) []byte
//構造部分複合HistoryKey,ns 0x00 key 0x00 0xff
func ConstructPartialCompositeHistoryKey(ns string, key string, endkey bool) []byte 
//按分隔符separator,分割bytesToSplit
func SplitCompositeHistoryKey(bytesToSplit []byte, separator []byte) ([]byte, []byte) 
//代碼在core/ledger/kvledger/history/historydb/histmgr_helper.go

4、HistoryDB接口實現

HistoryDB接口實現,即historyleveldb.historyDB結構體及方法。historyDB結構體定義如下:

type historyDB struct {
    db     *leveldbhelper.DBHandle //leveldb
    dbName string //dbName
}
//代碼在core/ledger/kvledger/history/historydb/historyleveldb/historyleveldb.go

涉及方法如下:

//構造historyDB
func newHistoryDB(db *leveldbhelper.DBHandle, dbName string) *historyDB
//do nothing
func (historyDB *historyDB) Open() error
//do nothing
func (historyDB *historyDB) Close()
//提交Block入historyDB,將讀寫集中寫集入庫,並更新savePointKey
func (historyDB *historyDB) Commit(block *common.Block) error
//構造 LevelHistoryDBQueryExecutor
func (historyDB *historyDB) NewHistoryQueryExecutor(blockStore blkstorage.BlockStore) (ledger.HistoryQueryExecutor, error)
獲取savePointKey,即version.Height
func (historyDB *historyDB) GetLastSavepoint() (*version.Height, error)
//是否應該恢復,比較lastAvailableBlock和Savepoint
func (historyDB *historyDB) ShouldRecover(lastAvailableBlock uint64) (bool, uint64, error)
//提交丟失的塊,即調用historyDB.Commit(block)
func (historyDB *historyDB) CommitLostBlock(block *common.Block) error
//代碼在core/ledger/kvledger/history/historydb/historyleveldb/historyleveldb.go

func (historyDB historyDB) Commit(block common.Block) error代碼如下:

blockNo := block.Header.Number //區塊編號
var tranNo uint64 //交易編號,初始化值爲0
dbBatch := leveldbhelper.NewUpdateBatch() //leveldb批量更新

//交易驗證代碼,type TxValidationFlags []uint8
//交易篩選器
txsFilter := util.TxValidationFlags(block.Metadata.Metadata[common.BlockMetadataIndex_TRANSACTIONS_FILTER])
if len(txsFilter) == 0 {
    txsFilter = util.NewTxValidationFlags(len(block.Data.Data))
    block.Metadata.Metadata[common.BlockMetadataIndex_TRANSACTIONS_FILTER] = txsFilter
}
for _, envBytes := range block.Data.Data {
    if txsFilter.IsInvalid(int(tranNo)) { //檢查指定的交易是否有效
        tranNo++
        continue
    }
    //[]byte反序列化爲Envelope
    env, err := putils.GetEnvelopeFromBlock(envBytes)
    payload, err := putils.GetPayload(env) //e.Payload反序列化爲Payload
    //[]byte反序列化爲ChannelHeader
    chdr, err := putils.UnmarshalChannelHeader(payload.Header.ChannelHeader)

    if common.HeaderType(chdr.Type) == common.HeaderType_ENDORSER_TRANSACTION { //背書交易,type HeaderType int32
        respPayload, err := putils.GetActionFromEnvelope(envBytes) //獲取ChaincodeAction
        txRWSet := &rwsetutil.TxRwSet{}
        err = txRWSet.FromProtoBytes(respPayload.Results) //[]byte反序列化後構造NsRwSet,加入txRWSet.NsRwSets
        for _, nsRWSet := range txRWSet.NsRwSets {
            ns := nsRWSet.NameSpace
            for _, kvWrite := range nsRWSet.KvRwSet.Writes {
                writeKey := kvWrite.Key
                //txRWSet中寫集入庫
                compositeHistoryKey := historydb.ConstructCompositeHistoryKey(ns, writeKey, blockNo, tranNo)
                dbBatch.Put(compositeHistoryKey, emptyValue)
            }
        }
    } else {
        logger.Debugf("Skipping transaction [%d] since it is not an endorsement transaction\n", tranNo)
    }
    tranNo++
}

height := version.NewHeight(blockNo, tranNo)
dbBatch.Put(savePointKey, height.ToBytes())
err := historyDB.db.WriteBatch(dbBatch, true)
//代碼在core/ledger/kvledger/history/historydb/historyleveldb/historyleveldb.go

Tx(Transaction 交易)相關更詳細內容,參考:Fabric 1.0源代碼筆記 之 Tx(Transaction 交易)

5、HistoryDBProvider接口實現

HistoryDBProvider接口實現,即historyleveldb.HistoryDBProvider結構體和方法。

type HistoryDBProvider struct {
    dbProvider *leveldbhelper.Provider
}

//構造HistoryDBProvider
func NewHistoryDBProvider() *HistoryDBProvider
//獲取HistoryDB
func (provider *HistoryDBProvider) GetDBHandle(dbName string) (historydb.HistoryDB, error)
//關閉所有HistoryDB句柄,調取provider.dbProvider.Close()
func (provider *HistoryDBProvider) Close()
//代碼在core/ledger/kvledger/history/historydb/historyleveldb/historyleveldb.go

6、LevelHistoryDBQueryExecutor和historyScanner結構體及方法

LevelHistoryDBQueryExecutor結構體及方法:實現ledger.HistoryQueryExecutor接口。

type LevelHistoryDBQueryExecutor struct {
    historyDB  *historyDB
    blockStore blkstorage.BlockStore //用於傳遞給historyScanner
}
//按key查historyDB,調用q.historyDB.db.GetIterator(compositeStartKey, compositeEndKey)
func (q *LevelHistoryDBQueryExecutor) GetHistoryForKey(namespace string, key string) (commonledger.ResultsIterator, error) 
//代碼在core/ledger/kvledger/history/historydb/historyleveldb/historyleveldb_query_executer.go

historyScanner結構體及方法:實現ledger.ResultsIterator接口。

type historyScanner struct {
    compositePartialKey []byte //ns 0x00 key 0x00
    namespace           string
    key                 string
    dbItr               iterator.Iterator //leveldb迭代器
    blockStore          blkstorage.BlockStore
}

//構造historyScanner
func newHistoryScanner(compositePartialKey []byte, namespace string, key string, dbItr iterator.Iterator, blockStore blkstorage.BlockStore) *historyScanner
//按迭代器中key取blockNum和tranNum,再按blockNum和tranNum從blockStore中取Envelope,然後從Envelope的txRWSet.NsRwSets中按key查找並構造queryresult.KeyModification
func (scanner *historyScanner) Next() (commonledger.QueryResult, error)
func (scanner *historyScanner) Close() //scanner.dbItr.Release()
從Envelope的txRWSet.NsRwSets中按key查找並構造queryresult.KeyModification
func getKeyModificationFromTran(tranEnvelope *common.Envelope, namespace string, key string) (commonledger.QueryResult, error)
//代碼在core/ledger/kvledger/history/historydb/historyleveldb/historyleveldb_query_executer.go

補充queryresult.KeyModification:

type KeyModification struct {
    TxId      string //交易ID,ChannelHeader.TxId
    Value     []byte //讀寫集中Value,KVWrite.Value
    Timestamp *google_protobuf.Timestamp //ChannelHeader.Timestamp
    IsDelete  bool //KVWrite.IsDelete
}
//代碼在protos/ledger/queryresult/kv_query_result.pb.go

感謝關注兄弟連區塊鏈教程分享!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章