[RateLimiter] Fix sliding_window misbehaving with stale records
This commit is contained in:
parent
ec38bab34a
commit
57033164c6
@ -63,8 +63,12 @@ final class SlidingWindow implements LimiterStateInterface
|
||||
public static function createFromPreviousWindow(self $window, int $intervalInSeconds): self
|
||||
{
|
||||
$new = new self($window->id, $intervalInSeconds);
|
||||
$new->hitCountForLastWindow = $window->hitCount;
|
||||
$new->windowEndAt = $window->windowEndAt + $intervalInSeconds;
|
||||
$windowEndAt = $window->windowEndAt + $intervalInSeconds;
|
||||
|
||||
if (time() < $windowEndAt) {
|
||||
$new->hitCountForLastWindow = $window->hitCount;
|
||||
$new->windowEndAt = $windowEndAt;
|
||||
}
|
||||
|
||||
return $new;
|
||||
}
|
||||
@ -112,7 +116,7 @@ final class SlidingWindow implements LimiterStateInterface
|
||||
public function getHitCount(): int
|
||||
{
|
||||
$startOfWindow = $this->windowEndAt - $this->intervalInSeconds;
|
||||
$percentOfCurrentTimeFrame = (time() - $startOfWindow) / $this->intervalInSeconds;
|
||||
$percentOfCurrentTimeFrame = min((time() - $startOfWindow) / $this->intervalInSeconds, 1);
|
||||
|
||||
return (int) floor($this->hitCountForLastWindow * (1 - $percentOfCurrentTimeFrame) + $this->hitCount);
|
||||
}
|
||||
|
@ -12,9 +12,13 @@
|
||||
namespace Symfony\Component\RateLimiter\Tests\Policy;
|
||||
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Symfony\Bridge\PhpUnit\ClockMock;
|
||||
use Symfony\Component\RateLimiter\Exception\InvalidIntervalException;
|
||||
use Symfony\Component\RateLimiter\Policy\SlidingWindow;
|
||||
|
||||
/**
|
||||
* @group time-sensitive
|
||||
*/
|
||||
class SlidingWindowTest extends TestCase
|
||||
{
|
||||
public function testGetExpirationTime()
|
||||
@ -36,4 +40,36 @@ class SlidingWindowTest extends TestCase
|
||||
$this->expectException(InvalidIntervalException::class);
|
||||
new SlidingWindow('foo', 0);
|
||||
}
|
||||
|
||||
public function testLongInterval()
|
||||
{
|
||||
ClockMock::register(SlidingWindow::class);
|
||||
$window = new SlidingWindow('foo', 60);
|
||||
$this->assertSame(0, $window->getHitCount());
|
||||
$window->add(20);
|
||||
$this->assertSame(20, $window->getHitCount());
|
||||
|
||||
sleep(60);
|
||||
$new = SlidingWindow::createFromPreviousWindow($window, 60);
|
||||
$this->assertSame(20, $new->getHitCount());
|
||||
|
||||
sleep(30);
|
||||
$this->assertSame(10, $new->getHitCount());
|
||||
|
||||
sleep(30);
|
||||
$this->assertSame(0, $new->getHitCount());
|
||||
|
||||
sleep(30);
|
||||
$this->assertSame(0, $new->getHitCount());
|
||||
}
|
||||
|
||||
public function testLongIntervalCreate()
|
||||
{
|
||||
ClockMock::register(SlidingWindow::class);
|
||||
$window = new SlidingWindow('foo', 60);
|
||||
|
||||
sleep(300);
|
||||
$new = SlidingWindow::createFromPreviousWindow($window, 60);
|
||||
$this->assertFalse($new->isExpired());
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user