github.com/tenywen/fabric@v1.0.0-beta.0.20170620030522-a5b1ed380643/orderer/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/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) 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*time.Second {
    52  		return fmt.Errorf("illegal value")
    53  	}
    54  
    55  	var err = fmt.Errorf("process has not been executed yet")
    56  
    57  	tickInterval := time.NewTicker(interval)
    58  	tickTotal := time.NewTicker(total)
    59  	defer tickTotal.Stop()
    60  	defer tickInterval.Stop()
    61  	logger.Debugf("[channel: %s] Retrying every %s for a total of %s", rp.channel.topic(), interval.String(), total.String())
    62  
    63  	for {
    64  		select {
    65  		case <-rp.exit:
    66  			exitErr := fmt.Errorf("[channel: %s] process asked to exit", rp.channel.topic())
    67  			logger.Warning(exitErr.Error()) // Log it at the warning level
    68  			return exitErr
    69  		case <-tickTotal.C:
    70  			return err
    71  		case <-tickInterval.C:
    72  			logger.Debugf("[channel: %s] "+rp.msg, rp.channel.topic())
    73  			if err = rp.fn(); err == nil {
    74  				logger.Debugf("[channel: %s] Error is nil, breaking the retry loop", rp.channel.topic())
    75  				return err
    76  			}
    77  		}
    78  	}
    79  }