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  }