waiter.go 1.9 KB

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