Backoff.php 1.8 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980
  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\Support;
  12. use InvalidArgumentException;
  13. class Backoff
  14. {
  15. /**
  16. * Max backoff.
  17. */
  18. private const CAP = 60 * 1000; // 1 minute
  19. /**
  20. * Backoff interval.
  21. */
  22. private int $currentMs;
  23. /**
  24. * @param int the first backoff in milliseconds
  25. */
  26. public function __construct(private int $firstMs = 0)
  27. {
  28. if ($firstMs < 0) {
  29. throw new InvalidArgumentException(
  30. 'first backoff interval must be greater or equal than 0'
  31. );
  32. }
  33. if ($firstMs > Backoff::CAP) {
  34. throw new InvalidArgumentException(
  35. sprintf(
  36. 'first backoff interval must be less or equal than %d milliseconds',
  37. self::CAP
  38. )
  39. );
  40. }
  41. $this->currentMs = $firstMs;
  42. }
  43. /**
  44. * Sleep until the next execution.
  45. */
  46. public function sleep(): void
  47. {
  48. if ($this->currentMs === 0) {
  49. return;
  50. }
  51. usleep($this->currentMs * 1000);
  52. // update backoff using Decorrelated Jitter
  53. // see: https://aws.amazon.com/blogs/architecture/exponential-backoff-and-jitter/
  54. $this->currentMs = rand($this->firstMs, $this->currentMs * 3);
  55. if ($this->currentMs > self::CAP) {
  56. $this->currentMs = self::CAP;
  57. }
  58. }
  59. /**
  60. * Get the next backoff for logging, etc.
  61. * @return int next backoff
  62. */
  63. public function nextBackoff(): int
  64. {
  65. return $this->currentMs;
  66. }
  67. }