waiter.go raw

   1  package waiter
   2  
   3  import (
   4  	"fmt"
   5  	"math"
   6  	"time"
   7  
   8  	"github.com/aws/smithy-go/rand"
   9  )
  10  
  11  // ComputeDelay computes delay between waiter attempts. The function takes in a current attempt count,
  12  // minimum delay, maximum delay, and remaining wait time for waiter as input. The inputs minDelay and maxDelay
  13  // must always be greater than 0, along with minDelay lesser than or equal to maxDelay.
  14  //
  15  // Returns the computed delay and if next attempt count is possible within the given input time constraints.
  16  // Note that the zeroth attempt results in no delay.
  17  func ComputeDelay(attempt int64, minDelay, maxDelay, remainingTime time.Duration) (delay time.Duration, err error) {
  18  	// zeroth attempt, no delay
  19  	if attempt <= 0 {
  20  		return 0, nil
  21  	}
  22  
  23  	// remainingTime is zero or less, no delay
  24  	if remainingTime <= 0 {
  25  		return 0, nil
  26  	}
  27  
  28  	// validate min delay is greater than 0
  29  	if minDelay == 0 {
  30  		return 0, fmt.Errorf("minDelay must be greater than zero when computing Delay")
  31  	}
  32  
  33  	// validate max delay is greater than 0
  34  	if maxDelay == 0 {
  35  		return 0, fmt.Errorf("maxDelay must be greater than zero when computing Delay")
  36  	}
  37  
  38  	// Get attempt ceiling to prevent integer overflow.
  39  	attemptCeiling := (math.Log(float64(maxDelay/minDelay)) / math.Log(2)) + 1
  40  
  41  	if attempt > int64(attemptCeiling) {
  42  		delay = maxDelay
  43  	} else {
  44  		// Compute exponential delay based on attempt.
  45  		ri := 1 << uint64(attempt-1)
  46  		// compute delay
  47  		delay = minDelay * time.Duration(ri)
  48  	}
  49  
  50  	if delay != minDelay {
  51  		// randomize to get jitter between min delay and delay value
  52  		d, err := rand.CryptoRandInt63n(int64(delay - minDelay))
  53  		if err != nil {
  54  			return 0, fmt.Errorf("error computing retry jitter, %w", err)
  55  		}
  56  
  57  		delay = time.Duration(d) + minDelay
  58  	}
  59  
  60  	// check if this is the last attempt possible and compute delay accordingly
  61  	if remainingTime-delay <= minDelay {
  62  		delay = remainingTime - minDelay
  63  	}
  64  
  65  	return delay, nil
  66  }
  67