Skip to content

Commit a81db93

Browse files
committed
e2e functionality for block reconcilation
1 parent ad406a4 commit a81db93

File tree

7 files changed

+223
-11
lines changed

7 files changed

+223
-11
lines changed

common/p2pmessage/p2pmessage.go

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,13 @@ package p2pmessage
22

33
import (
44
"context"
5+
"fmt"
56
"github.com/hyperledger/fabric/common/crypto"
67
"github.com/hyperledger/fabric/common/flogging"
78
"github.com/hyperledger/fabric/common/ledger/blockledger"
89
"github.com/hyperledger/fabric/common/policies"
910
"github.com/hyperledger/fabric/common/util"
11+
gossipprivdata "github.com/hyperledger/fabric/gossip/privdata"
1012
"github.com/hyperledger/fabric/internal/peer/protos"
1113
"time"
1214
)
@@ -113,11 +115,21 @@ func (h *Handler) Handle(ctx context.Context, request *protos.ReconcileRequest)
113115
defer h.Metrics.StreamsClosed.Add(1)
114116

115117
reconcileResponse := &protos.ReconcileResponse{
116-
Success: true,
117-
Message: "I amd sending the response! It's me server",
118+
Success: false,
118119
}
119120

120-
return reconcileResponse, nil
121+
// Calling Reconciler Service
122+
// reconcilerServiceRegistry := gossipprivdata.NewOnDemandReconcilerService()
123+
reconciler := gossipprivdata.GetOnDemandReconcilerService(request.ChannelId)
124+
fmt.Println(reconciler)
125+
126+
if reconciler == nil {
127+
reconcileResponse.Message = "no reconciler found for channel " + request.ChannelId
128+
129+
return reconcileResponse, fmt.Errorf("no reconciler found for channel " + request.ChannelId)
130+
}
131+
response, err := reconciler.Reconcile(request.BlockNumber)
132+
return &response, err
121133
}
122134

123135
// ExtractChannelHeaderCertHash extracts the TLS cert hash from a channel header.

core/ledger/kvledger/kv_ledger.go

Lines changed: 24 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -464,8 +464,8 @@ func (l *kvLedger) filterYetToCommitBlocks(blocksPvtData map[uint64][]*ledger.Tx
464464
return nil
465465
}
466466

467-
//recommitLostBlocks retrieves blocks in specified range and commit the write set to either
468-
//state DB or history DB or both
467+
// recommitLostBlocks retrieves blocks in specified range and commit the write set to either
468+
// state DB or history DB or both
469469
func (l *kvLedger) recommitLostBlocks(firstBlockNum uint64, lastBlockNum uint64, recoverables ...recoverable) error {
470470
logger.Infof("Recommitting lost blocks - firstBlockNum=%d, lastBlockNum=%d, recoverables=%#v", firstBlockNum, lastBlockNum, recoverables)
471471
var err error
@@ -762,6 +762,23 @@ func (l *kvLedger) GetMissingPvtDataInfoForMostRecentBlocks(maxBlock int) (ledge
762762
return l.pvtdataStore.GetMissingPvtDataInfoForMostRecentBlocks(maxBlock)
763763
}
764764

765+
// GetMissingPvtDataInfoForSpecificBlock returns the missing private data information for the
766+
// specified blocks which miss at least a private data of a eligible collection.
767+
func (l *kvLedger) GetMissingPvtDataInfoForSpecificBlock(blockNumber uint64) (ledger.MissingPvtDataInfo, error) {
768+
// the missing pvtData info in the pvtdataStore could belong to a block which is yet
769+
// to be processed and committed to the blockStore and stateDB (such a scenario is possible
770+
// after a peer rollback). In such cases, we cannot return missing pvtData info. Otherwise,
771+
// we would end up in an inconsistent state database.
772+
if l.isPvtstoreAheadOfBlkstore.Load().(bool) {
773+
return nil, nil
774+
}
775+
// it is safe to not acquire a read lock on l.blockAPIsRWLock. Without a lock, the value of
776+
// lastCommittedBlock can change due to a new block commit. As a result, we may not
777+
// be able to fetch the missing data info of truly the most recent blocks. This
778+
// decision was made to ensure that the regular block commit rate is not affected.
779+
return l.pvtdataStore.GetMissingPvtDataInfoForSpecificBlock(blockNumber)
780+
}
781+
765782
func (l *kvLedger) addBlockCommitHash(block *common.Block, updateBatchBytes []byte) {
766783
var valueBytes []byte
767784

@@ -812,9 +829,12 @@ func (l *kvLedger) GetPvtDataByNum(blockNum uint64, filter ledger.PvtNsCollFilte
812829
// DoesPvtDataInfoExist returns true when
813830
// (1) the ledger has pvtdata associated with the given block number (or)
814831
// (2) a few or all pvtdata associated with the given block number is missing but the
815-
// missing info is recorded in the ledger (or)
832+
//
833+
// missing info is recorded in the ledger (or)
834+
//
816835
// (3) the block is committed but it does not contain even a single
817-
// transaction with pvtData.
836+
//
837+
// transaction with pvtData.
818838
func (l *kvLedger) DoesPvtDataInfoExist(blockNum uint64) (bool, error) {
819839
pvtStoreHt, err := l.pvtdataStore.LastCommittedBlockHeight()
820840
if err != nil {

core/ledger/ledger_interface.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -536,6 +536,7 @@ type ConfigHistoryRetriever interface {
536536
// MissingPvtDataTracker allows getting information about the private data that is not missing on the peer
537537
type MissingPvtDataTracker interface {
538538
GetMissingPvtDataInfoForMostRecentBlocks(maxBlocks int) (MissingPvtDataInfo, error)
539+
GetMissingPvtDataInfoForSpecificBlock(blockNumber uint64) (MissingPvtDataInfo, error)
539540
}
540541

541542
// MissingPvtDataInfo is a map of block number to MissingBlockPvtdataInfo

core/ledger/pvtdatastorage/store.go

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ SPDX-License-Identifier: Apache-2.0
77
package pvtdatastorage
88

99
import (
10+
"fmt"
1011
"sync"
1112
"sync/atomic"
1213
"time"
@@ -476,6 +477,41 @@ func (s *Store) GetMissingPvtDataInfoForMostRecentBlocks(maxBlock int) (ledger.M
476477
return s.getMissingData(elgPrioritizedMissingDataGroup, maxBlock)
477478
}
478479

480+
// GetMissingPvtDataInfoForSpecificBlock returns the missing private data information for the
481+
// specified block which miss at least a private data of a eligible collection.
482+
func (s *Store) GetMissingPvtDataInfoForSpecificBlock(blockNumber uint64) (ledger.MissingPvtDataInfo, error) {
483+
// we assume that this function would be called by the gossip only after processing the
484+
// last retrieved missing pvtdata info and committing the same.
485+
if blockNumber < 1 {
486+
return nil, fmt.Errorf("invalid block number [%d]", blockNumber)
487+
}
488+
489+
lastCommittedBlock := atomic.LoadUint64(&s.lastCommittedBlock)
490+
if blockNumber > lastCommittedBlock {
491+
return nil, fmt.Errorf("block [%d] is not committed yet to the ledger", blockNumber)
492+
}
493+
494+
logger.Debug("fetching missing pvtdata entries from the de-prioritized list")
495+
dePrioratizedQue, err := s.getMissingDataFromSpecificBlock(elgDeprioritizedMissingDataGroup, blockNumber)
496+
if err != nil {
497+
logger.Debugf("Error in fetching missing pvtdata entries from the de-prioritized list: %s", err)
498+
return nil, err
499+
}
500+
logger.Debug("fetching missing pvtdata entries from the prioritized list")
501+
prioritizedQue, err := s.getMissingDataFromSpecificBlock(elgPrioritizedMissingDataGroup, blockNumber)
502+
if err != nil {
503+
logger.Debugf("Error in fetching missing pvtdata entries from the prioritized list: %s", err)
504+
return nil, err
505+
}
506+
507+
for k, v := range dePrioratizedQue {
508+
prioritizedQue[k] = v // This will overwrite map1's entry if the key exists
509+
}
510+
511+
return prioritizedQue, nil
512+
513+
}
514+
479515
func (s *Store) getMissingData(group []byte, maxBlock int) (ledger.MissingPvtDataInfo, error) {
480516
missingPvtDataInfo := make(ledger.MissingPvtDataInfo)
481517
numberOfBlockProcessed := 0
@@ -550,6 +586,54 @@ func (s *Store) getMissingData(group []byte, maxBlock int) (ledger.MissingPvtDat
550586
return missingPvtDataInfo, nil
551587
}
552588

589+
func (s *Store) getMissingDataFromSpecificBlock(group []byte, blockNumber uint64) (ledger.MissingPvtDataInfo, error) {
590+
missingPvtDataInfo := make(ledger.MissingPvtDataInfo)
591+
592+
startKey, endKey := createRangeScanKeysForElgMissingData(blockNumber, group)
593+
dbItr, err := s.db.GetIterator(startKey, endKey)
594+
if err != nil {
595+
return nil, err
596+
}
597+
defer dbItr.Release()
598+
599+
for dbItr.Next() {
600+
missingDataKeyBytes := dbItr.Key()
601+
missingDataKey := decodeElgMissingDataKey(missingDataKeyBytes)
602+
603+
// check whether the entry is expired. If so, move to the next item.
604+
// As we may use the old lastCommittedBlock value, there is a possibility that
605+
// this missing data is actually expired but we may get the stale information.
606+
// Though it may leads to extra work of pulling the expired data, it will not
607+
// affect the correctness. Further, as we try to fetch the most recent missing
608+
// data (less possibility of expiring now), such scenario would be rare. In the
609+
// best case, we can load the latest lastCommittedBlock value here atomically to
610+
// make this scenario very rare.
611+
expired, err := isExpired(missingDataKey.nsCollBlk, s.btlPolicy, blockNumber)
612+
if err != nil {
613+
return nil, err
614+
}
615+
if expired {
616+
continue
617+
}
618+
619+
valueBytes := dbItr.Value()
620+
bitmap, err := decodeMissingDataValue(valueBytes)
621+
if err != nil {
622+
return nil, err
623+
}
624+
625+
// for each transaction which misses private data, make an entry in missingBlockPvtDataInfo
626+
for index, isSet := bitmap.NextSet(0); isSet; index, isSet = bitmap.NextSet(index + 1) {
627+
txNum := uint64(index)
628+
missingPvtDataInfo.Add(missingDataKey.blkNum, txNum, missingDataKey.ns, missingDataKey.coll)
629+
}
630+
631+
break
632+
}
633+
634+
return missingPvtDataInfo, nil
635+
}
636+
553637
// FetchBootKVHashes returns the KVHashes from the data that was loaded from a snapshot at the time of
554638
// bootstrapping. This funciton returns an error if the supplied blkNum is greater than the last block
555639
// number in the booting snapshot

gossip/privdata/reconcile.go

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ package privdata
99
import (
1010
"encoding/hex"
1111
"fmt"
12+
"github.com/hyperledger/fabric/internal/peer/protos"
1213
"math"
1314
"sync"
1415
"time"
@@ -53,6 +54,9 @@ type PvtDataReconciler interface {
5354
Start()
5455
// Stop function stops reconciler
5556
Stop()
57+
58+
//Reconcile performs on demand reconcilation a block or transaction
59+
Reconcile(uint65 uint64) (protos.ReconcileResponse, error)
5660
}
5761

5862
type Reconciler struct {
@@ -68,6 +72,25 @@ type Reconciler struct {
6872
committer.Committer
6973
}
7074

75+
var ReconcilerServiceRegistry = make(map[string]PvtDataReconciler)
76+
77+
// SetOnDemandReconcilerService sets a reconciler service by name
78+
func SetOnDemandReconcilerService(name string, reconciler PvtDataReconciler) {
79+
ReconcilerServiceRegistry[name] = reconciler
80+
}
81+
82+
func GetOnDemandReconcilerService(name string) PvtDataReconciler {
83+
84+
if len(ReconcilerServiceRegistry) == 0 {
85+
return nil
86+
}
87+
if ReconcilerServiceRegistry[name] == nil {
88+
return nil
89+
}
90+
91+
return ReconcilerServiceRegistry[name]
92+
}
93+
7194
// NoOpReconciler non functional reconciler to be used
7295
// in case reconciliation has been disabled
7396
type NoOpReconciler struct {
@@ -82,6 +105,11 @@ func (*NoOpReconciler) Stop() {
82105
// do nothing
83106
}
84107

108+
func (*NoOpReconciler) Reconcile(num uint64) (protos.ReconcileResponse, error) {
109+
logger.Debug("Private data reconciliation has been disabled")
110+
return protos.ReconcileResponse{Success: false, Message: "got nil as MissingPvtDataTracker, exiting..."}, nil
111+
}
112+
85113
// NewReconciler creates a new instance of reconciler
86114
func NewReconciler(channel string, metrics *metrics.PrivdataMetrics, c committer.Committer,
87115
fetcher ReconciliationFetcher, config *PrivdataConfig) *Reconciler {
@@ -111,6 +139,10 @@ func (r *Reconciler) Start() {
111139
})
112140
}
113141

142+
func (r *Reconciler) Reconcile(num uint64) (protos.ReconcileResponse, error) {
143+
return r.reconcileSpecific(num)
144+
}
145+
114146
func (r *Reconciler) run() {
115147
for {
116148
select {
@@ -126,6 +158,60 @@ func (r *Reconciler) run() {
126158
}
127159
}
128160

161+
// ReconcileSpecific initiates a reconcilation for specific block and returns the number of items that were reconciled , minBlock, maxBlock (blocks range) and an error
162+
func (r *Reconciler) reconcileSpecific(num uint64) (protos.ReconcileResponse, error) {
163+
missingPvtDataTracker, err := r.GetMissingPvtDataTracker()
164+
if err != nil {
165+
r.logger.Error("reconciliation error when trying to get missingPvtDataTracker:", err)
166+
return protos.ReconcileResponse{Success: false, Message: err.Error()}, err
167+
}
168+
if missingPvtDataTracker == nil {
169+
r.logger.Error("got nil as MissingPvtDataTracker, exiting...")
170+
return protos.ReconcileResponse{Success: false, Message: "got nil as MissingPvtDataTracker, exiting..."}, nil
171+
}
172+
totalReconciled := 0
173+
174+
defer r.reportReconciliationDuration(time.Now())
175+
176+
// for {
177+
missingPvtDataInfo, err := missingPvtDataTracker.GetMissingPvtDataInfoForSpecificBlock(num)
178+
if err != nil {
179+
r.logger.Errorf("reconciliation error when trying to get missing pvt data info for the block [%d] blocks: error %v", num, err.Error())
180+
return protos.ReconcileResponse{Success: false, Message: err.Error()}, err
181+
}
182+
// if missingPvtDataInfo is nil, len will return 0
183+
if len(missingPvtDataInfo) == 0 {
184+
r.logger.Debug("Reconciliation cycle finished successfully. no items to reconcile")
185+
return protos.ReconcileResponse{Success: true, Message: fmt.Sprintf("Reconciliation cycle finished successfully. nothing to reconcile for blocks range [%d]", num)}, nil
186+
}
187+
188+
r.logger.Debug("got from ledger", len(missingPvtDataInfo), "blocks with missing private data, trying to reconcile...")
189+
190+
dig2collectionCfg, _, _ := r.getDig2CollectionConfig(missingPvtDataInfo)
191+
fetchedData, err := r.FetchReconciledItems(dig2collectionCfg)
192+
if err != nil {
193+
r.logger.Error("reconciliation error when trying to fetch missing items from different peers:", err)
194+
return protos.ReconcileResponse{Success: false, Message: err.Error()}, err
195+
}
196+
197+
pvtDataToCommit := r.preparePvtDataToCommit(fetchedData.AvailableElements)
198+
unreconciled := constructUnreconciledMissingData(dig2collectionCfg, fetchedData.AvailableElements)
199+
pvtdataHashMismatch, err := r.CommitPvtDataOfOldBlocks(pvtDataToCommit, unreconciled)
200+
if err != nil {
201+
return protos.ReconcileResponse{Success: false, Message: "failed to commit private data"}, err
202+
}
203+
r.logMismatched(pvtdataHashMismatch)
204+
//if minB < minBlock {
205+
// minBlock = minB
206+
//}
207+
//if maxB > maxBlock {
208+
// maxBlock = maxB
209+
//}
210+
totalReconciled += len(fetchedData.AvailableElements)
211+
// }
212+
return protos.ReconcileResponse{Success: true, Message: fmt.Sprintf("Reconciliation cycle finished successfully. reconciled %d private data keys from blocks range [%d]", totalReconciled, num)}, nil
213+
}
214+
129215
// returns the number of items that were reconciled , minBlock, maxBlock (blocks range) and an error
130216
func (r *Reconciler) reconcile() error {
131217
missingPvtDataTracker, err := r.GetMissingPvtDataTracker()

gossip/service/gossip_service.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -366,6 +366,10 @@ func (g *GossipService) InitializeChannel(channelID string, ordererSource *order
366366
reconciler = &gossipprivdata.NoOpReconciler{}
367367
}
368368

369+
// reconcilerServiceRegistry := gossipprivdata.NewOnDemandReconcilerService()
370+
// reconcilerServiceRegistry.SetOnDemandReconcilerService(channelID, reconciler)
371+
gossipprivdata.SetOnDemandReconcilerService(channelID, reconciler)
372+
369373
pushAckTimeout := g.serviceConfig.PvtDataPushAckTimeout
370374
g.privateHandlers[channelID] = privateHandler{
371375
support: support,

internal/peer/reconciler/block.go

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@ func reconcileBlockCmd(cf *ReconcileCmdFactory) *cobra.Command {
2626

2727
func reconcileBlock(cmd *cobra.Command, args []string, rf *ReconcileCmdFactory) error {
2828

29+
logger.Debugf("Received the arguments for reconcilation %d", args)
30+
2931
if len(args) == 0 {
3032
return fmt.Errorf("reconcile target required, block number")
3133
}
@@ -59,12 +61,15 @@ func reconcileBlock(cmd *cobra.Command, args []string, rf *ReconcileCmdFactory)
5961
}
6062
}
6163

62-
response, _ := rf.DeliverClient.ReconcileSpecifiedBlock(uint64(blockNumber))
64+
response, err := rf.DeliverClient.ReconcileSpecifiedBlock(uint64(blockNumber))
6365

64-
fmt.Println(response)
66+
if err != nil {
67+
fmt.Printf("Failed to reconcile block %d: %v\n", blockNumber, err.Error())
68+
} else {
69+
fmt.Println(response)
70+
}
6571

66-
logger.Debugf("Received the arguments for reconcilation %d", blockNumber)
67-
fmt.Printf("Received the arguments for reconcilation %v on channel %s\n", args, channelID)
72+
// fmt.Printf("Received the arguments for reconcilation %v on channel %s\n", args, channelID)
6873

6974
err = rf.DeliverClient.Close()
7075
if err != nil {

0 commit comments

Comments
 (0)