github.com/osdi23p228/fabric@v0.0.0-20221218062954-77808885f5db/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/osdi23p228/fabric/common/flogging" 17 "github.com/osdi23p228/fabric/orderer/common/cluster" 18 "github.com/osdi23p228/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 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 height := es.height() 117 118 if lastConfigBlock.Header.Number+1 <= height { 119 es.logger.Infof("Our height is higher or equal than the height of the orderer we pulled the last block from, aborting.") 120 return 121 } 122 123 err = es.amIInChannel(lastConfigBlock) 124 if err != cluster.ErrNotInChannel && err != cluster.ErrForbidden { 125 details := fmt.Sprintf(", our certificate was found in config block with sequence %d", lastConfigBlock.Header.Number) 126 if err != nil { 127 details = fmt.Sprintf(": %s", err.Error()) 128 } 129 es.logger.Infof("Cannot confirm our own eviction from the channel%s", details) 130 131 es.triggerCatchUp(&raftpb.Snapshot{Data: protoutil.MarshalOrPanic(lastConfigBlock)}) 132 return 133 } 134 135 es.logger.Warningf("Detected our own eviction from the channel in block [%d]", lastConfigBlock.Header.Number) 136 137 es.logger.Infof("Waiting for chain to halt") 138 es.halt() 139 es.halted = true 140 es.logger.Infof("Chain has been halted, pulling remaining blocks up to (and including) eviction block.") 141 142 nextBlock := height 143 es.logger.Infof("Will now pull blocks %d to %d", nextBlock, lastConfigBlock.Header.Number) 144 for seq := nextBlock; seq <= lastConfigBlock.Header.Number; seq++ { 145 es.logger.Infof("Pulling block [%d]", seq) 146 block := puller.PullBlock(seq) 147 err := es.writeBlock(block) 148 if err != nil { 149 es.logger.Panicf("Failed writing block [%d] to the ledger: %v", block.Header.Number, err) 150 } 151 } 152 153 es.logger.Infof("Pulled all blocks up to eviction block.") 154 }