github.com/hechain20/hechain@v0.0.0-20220316014945-b544036ba106/orderer/consensus/etcdraft/eviction.go (about)

     1  /*
     2  Copyright hechain. All Rights Reserved.
     3  
     4  SPDX-License-Identifier: Apache-2.0
     5  */
     6  
     7  package etcdraft
     8  
     9  import (
    10  	"fmt"
    11  	"sync"
    12  	"sync/atomic"
    13  	"time"
    14  
    15  	"github.com/hechain20/hechain/common/flogging"
    16  	"github.com/hechain20/hechain/orderer/common/cluster"
    17  	"github.com/hechain20/hechain/protoutil"
    18  	"github.com/hyperledger/fabric-protos-go/common"
    19  	"go.etcd.io/etcd/raft/raftpb"
    20  )
    21  
    22  // PeriodicCheck checks periodically a condition, and reports
    23  // the cumulative consecutive period the condition was fulfilled.
    24  type PeriodicCheck struct {
    25  	Logger              *flogging.FabricLogger
    26  	CheckInterval       time.Duration
    27  	Condition           func() bool
    28  	Report              func(cumulativePeriod time.Duration)
    29  	ReportCleared       func()
    30  	conditionHoldsSince time.Time
    31  	once                sync.Once // Used to prevent double initialization
    32  	stopped             uint32
    33  }
    34  
    35  // Run runs the PeriodicCheck
    36  func (pc *PeriodicCheck) Run() {
    37  	pc.once.Do(pc.check)
    38  }
    39  
    40  // Stop stops the periodic checks
    41  func (pc *PeriodicCheck) Stop() {
    42  	pc.Logger.Info("Periodic check is stopping.")
    43  	atomic.AddUint32(&pc.stopped, 1)
    44  }
    45  
    46  func (pc *PeriodicCheck) shouldRun() bool {
    47  	return atomic.LoadUint32(&pc.stopped) == 0
    48  }
    49  
    50  func (pc *PeriodicCheck) check() {
    51  	if pc.Condition() {
    52  		pc.conditionFulfilled()
    53  	} else {
    54  		pc.conditionNotFulfilled()
    55  	}
    56  
    57  	if !pc.shouldRun() {
    58  		return
    59  	}
    60  	time.AfterFunc(pc.CheckInterval, pc.check)
    61  }
    62  
    63  func (pc *PeriodicCheck) conditionNotFulfilled() {
    64  	if pc.ReportCleared != nil && !pc.conditionHoldsSince.IsZero() {
    65  		pc.ReportCleared()
    66  	}
    67  	pc.conditionHoldsSince = time.Time{}
    68  }
    69  
    70  func (pc *PeriodicCheck) conditionFulfilled() {
    71  	if pc.conditionHoldsSince.IsZero() {
    72  		pc.conditionHoldsSince = time.Now()
    73  	}
    74  
    75  	pc.Report(time.Since(pc.conditionHoldsSince))
    76  }
    77  
    78  type evictionSuspector struct {
    79  	evictionSuspicionThreshold time.Duration
    80  	logger                     *flogging.FabricLogger
    81  	createPuller               CreateBlockPuller
    82  	height                     func() uint64
    83  	amIInChannel               cluster.SelfMembershipPredicate
    84  	halt                       func()
    85  	writeBlock                 func(block *common.Block) error
    86  	triggerCatchUp             func(sn *raftpb.Snapshot)
    87  	halted                     bool
    88  	timesTriggered             int
    89  }
    90  
    91  func (es *evictionSuspector) clearSuspicion() {
    92  	es.timesTriggered = 0
    93  }
    94  
    95  func (es *evictionSuspector) confirmSuspicion(cumulativeSuspicion time.Duration) {
    96  	// The goal here is to only execute the body of the function once every es.evictionSuspicionThreshold
    97  	if es.evictionSuspicionThreshold*time.Duration(es.timesTriggered+1) > cumulativeSuspicion || es.halted {
    98  		return
    99  	}
   100  	es.timesTriggered++
   101  
   102  	es.logger.Infof("Suspecting our own eviction from the channel for %v", cumulativeSuspicion)
   103  	puller, err := es.createPuller()
   104  	if err != nil {
   105  		es.logger.Panicf("Failed creating a block puller: %v", err)
   106  	}
   107  
   108  	lastConfigBlock, err := cluster.PullLastConfigBlock(puller)
   109  	if err != nil {
   110  		es.logger.Errorf("Failed pulling the last config block: %v", err)
   111  		return
   112  	}
   113  
   114  	es.logger.Infof("Last config block was found to be block [%d]", lastConfigBlock.Header.Number)
   115  
   116  	err = es.amIInChannel(lastConfigBlock)
   117  	if err != cluster.ErrNotInChannel && err != cluster.ErrForbidden {
   118  		details := fmt.Sprintf(", our certificate was found in config block with sequence %d", lastConfigBlock.Header.Number)
   119  		if err != nil {
   120  			details = fmt.Sprintf(": %s", err.Error())
   121  		}
   122  		es.logger.Infof("Cannot confirm our own eviction from the channel%s", details)
   123  
   124  		es.triggerCatchUp(&raftpb.Snapshot{Data: protoutil.MarshalOrPanic(lastConfigBlock)})
   125  		return
   126  	}
   127  	es.logger.Warningf("Detected our own eviction from the channel in block [%d]", lastConfigBlock.Header.Number)
   128  
   129  	es.logger.Infof("Waiting for chain to halt")
   130  	es.halt()
   131  	es.halted = true
   132  
   133  	height := es.height()
   134  	if lastConfigBlock.Header.Number+1 <= height {
   135  		es.logger.Infof("Our height is higher or equal than the height of the orderer we pulled the last block from, aborting.")
   136  		return
   137  	}
   138  
   139  	es.logger.Infof("Chain has been halted, pulling remaining blocks up to (and including) eviction block.")
   140  
   141  	nextBlock := height
   142  	es.logger.Infof("Will now pull blocks %d to %d", nextBlock, lastConfigBlock.Header.Number)
   143  	for seq := nextBlock; seq <= lastConfigBlock.Header.Number; seq++ {
   144  		es.logger.Infof("Pulling block [%d]", seq)
   145  		block := puller.PullBlock(seq)
   146  		err := es.writeBlock(block)
   147  		if err != nil {
   148  			es.logger.Panicf("Failed writing block [%d] to the ledger: %v", block.Header.Number, err)
   149  		}
   150  	}
   151  
   152  	es.logger.Infof("Pulled all blocks up to eviction block.")
   153  }