github.com/lzy4123/fabric@v2.1.1+incompatible/orderer/consensus/kafka/retry.go (about) 1 /* 2 Copyright IBM Corp. All Rights Reserved. 3 4 SPDX-License-Identifier: Apache-2.0 5 */ 6 7 package kafka 8 9 import ( 10 "fmt" 11 "time" 12 13 localconfig "github.com/hyperledger/fabric/orderer/common/localconfig" 14 ) 15 16 type retryProcess struct { 17 shortPollingInterval, shortTimeout time.Duration 18 longPollingInterval, longTimeout time.Duration 19 exit chan struct{} 20 channel channel 21 msg string 22 fn func() error 23 } 24 25 func newRetryProcess(retryOptions localconfig.Retry, exit chan struct{}, channel channel, msg string, fn func() error) *retryProcess { 26 return &retryProcess{ 27 shortPollingInterval: retryOptions.ShortInterval, 28 shortTimeout: retryOptions.ShortTotal, 29 longPollingInterval: retryOptions.LongInterval, 30 longTimeout: retryOptions.LongTotal, 31 exit: exit, 32 channel: channel, 33 msg: msg, 34 fn: fn, 35 } 36 } 37 38 func (rp *retryProcess) retry() error { 39 if err := rp.try(rp.shortPollingInterval, rp.shortTimeout); err != nil { 40 logger.Debugf("[channel: %s] Switching to the long retry interval", rp.channel.topic()) 41 return rp.try(rp.longPollingInterval, rp.longTimeout) 42 } 43 return nil 44 } 45 46 func (rp *retryProcess) try(interval, total time.Duration) (err error) { 47 // Configuration validation will not allow non-positive ticker values 48 // (which would result in panic). The path below is for those test cases 49 // when we cannot avoid the creation of a retriable process but we wish 50 // to terminate it right away. 51 if rp.shortPollingInterval == 0 { 52 return fmt.Errorf("illegal value") 53 } 54 55 // If initial operation is successful, we don't bother start retry process 56 logger.Debugf("[channel: %s] "+rp.msg, rp.channel.topic()) 57 if err = rp.fn(); err == nil { 58 logger.Debugf("[channel: %s] Error is nil, breaking the retry loop", rp.channel.topic()) 59 return 60 } 61 62 logger.Debugf("[channel: %s] Initial attempt failed = %s", rp.channel.topic(), err) 63 64 tickInterval := time.NewTicker(interval) 65 tickTotal := time.NewTicker(total) 66 defer tickTotal.Stop() 67 defer tickInterval.Stop() 68 logger.Debugf("[channel: %s] Retrying every %s for a total of %s", rp.channel.topic(), interval.String(), total.String()) 69 70 for { 71 select { 72 case <-rp.exit: 73 logger.Warningf("[channel: %s] process asked to exit", rp.channel.topic()) 74 return fmt.Errorf("process asked to exit") 75 case <-tickTotal.C: 76 return 77 case <-tickInterval.C: 78 logger.Debugf("[channel: %s] "+rp.msg, rp.channel.topic()) 79 if err = rp.fn(); err == nil { 80 logger.Debugf("[channel: %s] Error is nil, breaking the retry loop", rp.channel.topic()) 81 return 82 } 83 84 logger.Debugf("[channel: %s] Need to retry because process failed = %s", rp.channel.topic(), err) 85 } 86 } 87 }