Backoff.php 1.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384
  1. <?php
  2. declare(strict_types=1);
  3. /**
  4. * This file is part of Hyperf.
  5. *
  6. * @link https://www.hyperf.io
  7. * @document https://hyperf.wiki
  8. * @contact group@hyperf.io
  9. * @license https://github.com/hyperf/hyperf/blob/master/LICENSE
  10. */
  11. namespace Hyperf\Utils;
  12. class Backoff
  13. {
  14. /**
  15. * Max backoff.
  16. */
  17. private const CAP = 60 * 1000; // 1 minute
  18. /**
  19. * @var int
  20. */
  21. private $firstMs;
  22. /**
  23. * Backoff interval.
  24. * @var int
  25. */
  26. private $currentMs;
  27. /**
  28. * @param int the first backoff in milliseconds
  29. */
  30. public function __construct(int $firstMs = 0)
  31. {
  32. if ($firstMs < 0) {
  33. throw new \InvalidArgumentException(
  34. 'first backoff interval must be greater or equal than 0'
  35. );
  36. }
  37. if ($firstMs > Backoff::CAP) {
  38. throw new \InvalidArgumentException(
  39. sprintf(
  40. 'first backoff interval must be less or equal than %d milliseconds',
  41. self::CAP
  42. )
  43. );
  44. }
  45. $this->firstMs = $firstMs;
  46. $this->currentMs = $firstMs;
  47. }
  48. /**
  49. * Sleep until the next execution.
  50. */
  51. public function sleep(): void
  52. {
  53. if ($this->currentMs === 0) {
  54. return;
  55. }
  56. usleep($this->currentMs * 1000);
  57. // update backoff using Decorrelated Jitter
  58. // see: https://aws.amazon.com/blogs/architecture/exponential-backoff-and-jitter/
  59. $this->currentMs = rand($this->firstMs, $this->currentMs * 3);
  60. if ($this->currentMs > self::CAP) {
  61. $this->currentMs = self::CAP;
  62. }
  63. }
  64. /**
  65. * Get the next backoff for logging, etc.
  66. * @return int next backoff
  67. */
  68. public function nextBackoff(): int
  69. {
  70. return $this->currentMs;
  71. }
  72. }