From 2014e64bf0253fb50897df2af67c222258f31170 Mon Sep 17 00:00:00 2001 From: Evgeny Ruban Date: Tue, 5 Mar 2024 17:39:20 +0400 Subject: [PATCH 1/2] [RateLimiter] Fix results on last token consume. --- .../RateLimiter/Policy/SlidingWindowLimiter.php | 9 ++++++++- .../Tests/Policy/SlidingWindowLimiterTest.php | 15 +++++++++++++++ 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/src/Symfony/Component/RateLimiter/Policy/SlidingWindowLimiter.php b/src/Symfony/Component/RateLimiter/Policy/SlidingWindowLimiter.php index fc9173de49277..85c1f6b9c589d 100644 --- a/src/Symfony/Component/RateLimiter/Policy/SlidingWindowLimiter.php +++ b/src/Symfony/Component/RateLimiter/Policy/SlidingWindowLimiter.php @@ -74,7 +74,14 @@ public function reserve(int $tokens = 1, ?float $maxTime = null): Reservation if ($availableTokens >= $tokens) { $window->add($tokens); - $reservation = new Reservation($now, new RateLimit($this->getAvailableTokens($window->getHitCount()), \DateTimeImmutable::createFromFormat('U', floor($now)), true, $this->limit)); + if ($availableTokens === $tokens) { + $resetDuration = $window->calculateTimeForTokens($this->limit, $tokens); + $retryAfter = $now + $resetDuration; + } else { + $retryAfter = $now; + } + + $reservation = new Reservation($now, new RateLimit($this->getAvailableTokens($window->getHitCount()), \DateTimeImmutable::createFromFormat('U', floor($retryAfter)), true, $this->limit)); } else { $waitDuration = $window->calculateTimeForTokens($this->limit, $tokens); diff --git a/src/Symfony/Component/RateLimiter/Tests/Policy/SlidingWindowLimiterTest.php b/src/Symfony/Component/RateLimiter/Tests/Policy/SlidingWindowLimiterTest.php index 835c6cc767da6..dd3ab844c971d 100644 --- a/src/Symfony/Component/RateLimiter/Tests/Policy/SlidingWindowLimiterTest.php +++ b/src/Symfony/Component/RateLimiter/Tests/Policy/SlidingWindowLimiterTest.php @@ -70,6 +70,21 @@ public function testWaitIntervalOnConsumeOverLimit() $this->assertTrue($limiter->consume()->isAccepted()); } + public function testConsumeLastToken() + { + $limiter = $this->createLimiter(); + $limiter->reset(); + $limiter->consume(9); + + $rateLimit = $limiter->consume(1); + $this->assertSame(0, $rateLimit->getRemainingTokens()); + $this->assertTrue($rateLimit->isAccepted()); + $this->assertEquals( + \DateTimeImmutable::createFromFormat('U', (string) floor(microtime(true) + 12 / 10)), + $rateLimit->getRetryAfter() + ); + } + public function testReserve() { $limiter = $this->createLimiter(); From 99385addc52db4d75a8e4fccbfc96820b193da3d Mon Sep 17 00:00:00 2001 From: Evgeny Ruban Date: Tue, 5 Mar 2024 17:54:53 +0400 Subject: [PATCH 2/2] [RateLimiter] Fix results on last token consume. --- .../Component/RateLimiter/Policy/SlidingWindowLimiter.php | 2 +- .../RateLimiter/Tests/Policy/SlidingWindowLimiterTest.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Symfony/Component/RateLimiter/Policy/SlidingWindowLimiter.php b/src/Symfony/Component/RateLimiter/Policy/SlidingWindowLimiter.php index 85c1f6b9c589d..d3bd37f7b02e0 100644 --- a/src/Symfony/Component/RateLimiter/Policy/SlidingWindowLimiter.php +++ b/src/Symfony/Component/RateLimiter/Policy/SlidingWindowLimiter.php @@ -75,7 +75,7 @@ public function reserve(int $tokens = 1, ?float $maxTime = null): Reservation $window->add($tokens); if ($availableTokens === $tokens) { - $resetDuration = $window->calculateTimeForTokens($this->limit, $tokens); + $resetDuration = $window->calculateTimeForTokens($this->limit, $window->getHitCount()); $retryAfter = $now + $resetDuration; } else { $retryAfter = $now; diff --git a/src/Symfony/Component/RateLimiter/Tests/Policy/SlidingWindowLimiterTest.php b/src/Symfony/Component/RateLimiter/Tests/Policy/SlidingWindowLimiterTest.php index dd3ab844c971d..a5605247a7ef0 100644 --- a/src/Symfony/Component/RateLimiter/Tests/Policy/SlidingWindowLimiterTest.php +++ b/src/Symfony/Component/RateLimiter/Tests/Policy/SlidingWindowLimiterTest.php @@ -80,7 +80,7 @@ public function testConsumeLastToken() $this->assertSame(0, $rateLimit->getRemainingTokens()); $this->assertTrue($rateLimit->isAccepted()); $this->assertEquals( - \DateTimeImmutable::createFromFormat('U', (string) floor(microtime(true) + 12 / 10)), + \DateTimeImmutable::createFromFormat('U', (string) floor(microtime(true) + 12)), $rateLimit->getRetryAfter() ); }