github.com/true-sqn/fabric@v2.1.1+incompatible/orderer/consensus/etcdraft/eviction.go (about)

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