github.com/defanghe/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 }