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 }