Skip to content

Commit

Permalink
[RateLimiter] rename Limit to Quota
Browse files Browse the repository at this point in the history
  • Loading branch information
kbond committed Oct 16, 2020
1 parent 27524ff commit 15bf572
Show file tree
Hide file tree
Showing 16 changed files with 102 additions and 97 deletions.
Expand Up @@ -12,9 +12,9 @@
namespace Symfony\Component\HttpFoundation\RateLimiter;

use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\RateLimiter\Limit;
use Symfony\Component\RateLimiter\LimiterInterface;
use Symfony\Component\RateLimiter\NoLimiter;
use Symfony\Component\RateLimiter\Quota;

/**
* An implementation of RequestRateLimiterInterface that
Expand All @@ -26,23 +26,23 @@
*/
abstract class AbstractRequestRateLimiter implements RequestRateLimiterInterface
{
public function consume(Request $request): Limit
public function consume(Request $request): Quota
{
$limiters = $this->getLimiters($request);
if (0 === \count($limiters)) {
$limiters = [new NoLimiter()];
}

$minimalLimit = null;
$minimalQuota = null;
foreach ($limiters as $limiter) {
$limit = $limiter->consume(1);
$quota = $limiter->consume(1);

if (null === $minimalLimit || $limit->getRemainingTokens() < $minimalLimit->getRemainingTokens()) {
$minimalLimit = $limit;
if (null === $minimalQuota || $quota->getRemainingTokens() < $minimalQuota->getRemainingTokens()) {
$minimalQuota = $quota;
}
}

return $minimalLimit;
return $minimalQuota;
}

public function reset(): void
Expand Down
Expand Up @@ -12,7 +12,7 @@
namespace Symfony\Component\HttpFoundation\RateLimiter;

use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\RateLimiter\Limit;
use Symfony\Component\RateLimiter\Quota;

/**
* A special type of limiter that deals with requests.
Expand All @@ -26,7 +26,7 @@
*/
interface RequestRateLimiterInterface
{
public function consume(Request $request): Limit;
public function consume(Request $request): Quota;

public function reset(): void;
}
12 changes: 6 additions & 6 deletions src/Symfony/Component/RateLimiter/CompoundLimiter.php
Expand Up @@ -38,18 +38,18 @@ public function reserve(int $tokens = 1, ?float $maxTime = null): Reservation
throw new ReserveNotSupportedException(__CLASS__);
}

public function consume(int $tokens = 1): Limit
public function consume(int $tokens = 1): Quota
{
$minimalLimit = null;
$minimalQuota = null;
foreach ($this->limiters as $limiter) {
$limit = $limiter->consume($tokens);
$quota = $limiter->consume($tokens);

if (null === $minimalLimit || $limit->getRemainingTokens() < $minimalLimit->getRemainingTokens()) {
$minimalLimit = $limit;
if (null === $minimalQuota || $quota->getRemainingTokens() < $minimalQuota->getRemainingTokens()) {
$minimalQuota = $quota;
}
}

return $minimalLimit;
return $minimalQuota;
}

public function reset(): void
Expand Down
Expand Up @@ -11,7 +11,7 @@

namespace Symfony\Component\RateLimiter\Exception;

use Symfony\Component\RateLimiter\Limit;
use Symfony\Component\RateLimiter\Quota;

/**
* @author Wouter de Jong <wouter@wouterj.nl>
Expand All @@ -20,17 +20,17 @@
*/
class MaxWaitDurationExceededException extends \RuntimeException
{
private $limit;
private $quota;

public function __construct(string $message, Limit $limit, int $code = 0, ?\Throwable $previous = null)
public function __construct(string $message, Quota $quota, int $code = 0, ?\Throwable $previous = null)
{
parent::__construct($message, $code, $previous);

$this->limit = $limit;
$this->quota = $quota;
}

public function getLimit(): Limit
public function getQuota(): Quota
{
return $this->limit;
return $this->quota;
}
}
Expand Up @@ -11,7 +11,7 @@

namespace Symfony\Component\RateLimiter\Exception;

use Symfony\Component\RateLimiter\Limit;
use Symfony\Component\RateLimiter\Quota;

/**
* @author Kevin Bond <kevinbond@gmail.com>
Expand All @@ -20,27 +20,32 @@
*/
class RateLimitExceededException extends \RuntimeException
{
private $limit;
private $quota;

public function __construct(Limit $limit, $code = 0, \Throwable $previous = null)
public function __construct(Quota $quota, $code = 0, \Throwable $previous = null)
{
parent::__construct('Rate Limit Exceeded', $code, $previous);

$this->limit = $limit;
$this->quota = $quota;
}

public function getLimit(): Limit
public function getQuota(): Quota
{
return $this->limit;
return $this->quota;
}

public function getRetryAfter(): \DateTimeImmutable
{
return $this->limit->getRetryAfter();
return $this->quota->getRetryAfter();
}

public function getRemainingTokens(): int
{
return $this->limit->getRemainingTokens();
return $this->quota->getRemainingTokens();
}

public function getLimit(): int
{
return $this->quota->getLimit();
}
}
12 changes: 6 additions & 6 deletions src/Symfony/Component/RateLimiter/FixedWindowLimiter.php
Expand Up @@ -64,19 +64,19 @@ public function reserve(int $tokens = 1, ?float $maxTime = null): Reservation
if ($availableTokens >= $tokens) {
$window->add($tokens);

$reservation = new Reservation($now, new Limit($window->getAvailableTokens($now), \DateTimeImmutable::createFromFormat('U', floor($now)), true, $this->limit));
$reservation = new Reservation($now, new Quota($window->getAvailableTokens($now), \DateTimeImmutable::createFromFormat('U', floor($now)), true, $this->limit));
} else {
$remainingTokens = $tokens - $availableTokens;
$waitDuration = $window->calculateTimeForTokens($remainingTokens);

if (null !== $maxTime && $waitDuration > $maxTime) {
// process needs to wait longer than set interval
throw new MaxWaitDurationExceededException(sprintf('The rate limiter wait time ("%d" seconds) is longer than the provided maximum time ("%d" seconds).', $waitDuration, $maxTime), new Limit($window->getAvailableTokens($now), \DateTimeImmutable::createFromFormat('U', floor($now + $waitDuration)), false, $this->limit));
throw new MaxWaitDurationExceededException(sprintf('The rate limiter wait time ("%d" seconds) is longer than the provided maximum time ("%d" seconds).', $waitDuration, $maxTime), new Quota($window->getAvailableTokens($now), \DateTimeImmutable::createFromFormat('U', floor($now + $waitDuration)), false, $this->limit));
}

$window->add($tokens);

$reservation = new Reservation($now + $waitDuration, new Limit($window->getAvailableTokens($now), \DateTimeImmutable::createFromFormat('U', floor($now + $waitDuration)), false, $this->limit));
$reservation = new Reservation($now + $waitDuration, new Quota($window->getAvailableTokens($now), \DateTimeImmutable::createFromFormat('U', floor($now + $waitDuration)), false, $this->limit));
}
$this->storage->save($window);
} finally {
Expand All @@ -89,12 +89,12 @@ public function reserve(int $tokens = 1, ?float $maxTime = null): Reservation
/**
* {@inheritdoc}
*/
public function consume(int $tokens = 1): Limit
public function consume(int $tokens = 1): Quota
{
try {
return $this->reserve($tokens, 0)->getLimit();
return $this->reserve($tokens, 0)->getQuota();
} catch (MaxWaitDurationExceededException $e) {
return $e->getLimit();
return $e->getQuota();
}
}

Expand Down
2 changes: 1 addition & 1 deletion src/Symfony/Component/RateLimiter/LimiterInterface.php
Expand Up @@ -43,7 +43,7 @@ public function reserve(int $tokens = 1, ?float $maxTime = null): Reservation;
*
* @param int $tokens the number of tokens required
*/
public function consume(int $tokens = 1): Limit;
public function consume(int $tokens = 1): Quota;

/**
* Resets the limit.
Expand Down
6 changes: 3 additions & 3 deletions src/Symfony/Component/RateLimiter/NoLimiter.php
Expand Up @@ -25,12 +25,12 @@ final class NoLimiter implements LimiterInterface
{
public function reserve(int $tokens = 1, ?float $maxTime = null): Reservation
{
return new Reservation(time(), new Limit(\INF, new \DateTimeImmutable(), true, \INF));
return new Reservation(time(), new Quota(\INF, new \DateTimeImmutable(), true, \INF));
}

public function consume(int $tokens = 1): Limit
public function consume(int $tokens = 1): Quota
{
return new Limit(\INF, new \DateTimeImmutable(), true, \INF);
return new Quota(\INF, new \DateTimeImmutable(), true, \INF);
}

public function reset(): void
Expand Down
Expand Up @@ -18,7 +18,7 @@
*
* @experimental in 5.2
*/
class Limit
class Quota
{
private $availableTokens;
private $retryAfter;
Expand Down
10 changes: 5 additions & 5 deletions src/Symfony/Component/RateLimiter/Reservation.php
Expand Up @@ -19,15 +19,15 @@
final class Reservation
{
private $timeToAct;
private $limit;
private $quota;

/**
* @param float $timeToAct Unix timestamp in seconds when this reservation should act
*/
public function __construct(float $timeToAct, Limit $limit)
public function __construct(float $timeToAct, Quota $quota)
{
$this->timeToAct = $timeToAct;
$this->limit = $limit;
$this->quota = $quota;
}

public function getTimeToAct(): float
Expand All @@ -40,9 +40,9 @@ public function getWaitDuration(): float
return max(0, (-microtime(true)) + $this->timeToAct);
}

public function getLimit(): Limit
public function getQuota(): Quota
{
return $this->limit;
return $this->quota;
}

public function wait(): void
Expand Down
6 changes: 3 additions & 3 deletions src/Symfony/Component/RateLimiter/SlidingWindowLimiter.php
Expand Up @@ -76,7 +76,7 @@ public function reserve(int $tokens = 1, ?float $maxTime = null): Reservation
/**
* {@inheritdoc}
*/
public function consume(int $tokens = 1): Limit
public function consume(int $tokens = 1): Quota
{
$this->lock->acquire(true);

Expand All @@ -91,13 +91,13 @@ public function consume(int $tokens = 1): Limit
$hitCount = $window->getHitCount();
$availableTokens = $this->getAvailableTokens($hitCount);
if ($availableTokens < $tokens) {
return new Limit($availableTokens, $window->getRetryAfter(), false, $this->limit);
return new Quota($availableTokens, $window->getRetryAfter(), false, $this->limit);
}

$window->add($tokens);
$this->storage->save($window);

return new Limit($this->getAvailableTokens($window->getHitCount()), $window->getRetryAfter(), true, $this->limit);
return new Quota($this->getAvailableTokens($window->getHitCount()), $window->getRetryAfter(), true, $this->limit);
} finally {
$this->lock->release();
}
Expand Down
24 changes: 12 additions & 12 deletions src/Symfony/Component/RateLimiter/Tests/FixedWindowLimiterTest.php
Expand Up @@ -41,12 +41,12 @@ public function testConsume()
sleep(5);
}

$limit = $limiter->consume();
$this->assertSame(10, $limit->getLimit());
$this->assertTrue($limit->isAccepted());
$limit = $limiter->consume();
$this->assertFalse($limit->isAccepted());
$this->assertSame(10, $limit->getLimit());
$quota = $limiter->consume();
$this->assertSame(10, $quota->getLimit());
$this->assertTrue($quota->isAccepted());
$quota = $limiter->consume();
$this->assertFalse($quota->isAccepted());
$this->assertSame(10, $quota->getLimit());
}

public function testConsumeOutsideInterval()
Expand All @@ -60,18 +60,18 @@ public function testConsumeOutsideInterval()
$limiter->consume(9);
// ...try bursting again at the start of the next window
sleep(10);
$limit = $limiter->consume(10);
$this->assertEquals(0, $limit->getRemainingTokens());
$this->assertTrue($limit->isAccepted());
$quota = $limiter->consume(10);
$this->assertEquals(0, $quota->getRemainingTokens());
$this->assertTrue($quota->isAccepted());
}

public function testWrongWindowFromCache()
{
$this->storage->save(new DummyWindow());
$limiter = $this->createLimiter();
$limit = $limiter->consume();
$this->assertTrue($limit->isAccepted());
$this->assertEquals(9, $limit->getRemainingTokens());
$quota = $limiter->consume();
$this->assertTrue($quota->isAccepted());
$this->assertEquals(9, $quota->getRemainingTokens());
}

private function createLimiter(): FixedWindowLimiter
Expand Down
Expand Up @@ -13,25 +13,25 @@

use PHPUnit\Framework\TestCase;
use Symfony\Component\RateLimiter\Exception\RateLimitExceededException;
use Symfony\Component\RateLimiter\Limit;
use Symfony\Component\RateLimiter\Quota;

class LimitTest extends TestCase
class QuotaTest extends TestCase
{
public function testEnsureAcceptedDoesNotThrowExceptionIfAccepted()
{
$limit = new Limit(10, new \DateTimeImmutable(), true, 10);
$quota = new Quota(10, new \DateTimeImmutable(), true, 10);

$this->assertSame($limit, $limit->ensureAccepted());
$this->assertSame($quota, $quota->ensureAccepted());
}

public function testEnsureAcceptedThrowsRateLimitExceptionIfNotAccepted()
{
$limit = new Limit(10, $retryAfter = new \DateTimeImmutable(), false, 10);
$quota = new Quota(10, $retryAfter = new \DateTimeImmutable(), false, 10);

try {
$limit->ensureAccepted();
$quota->ensureAccepted();
} catch (RateLimitExceededException $exception) {
$this->assertSame($limit, $exception->getLimit());
$this->assertSame($quota, $exception->getQuota());
$this->assertSame(10, $exception->getRemainingTokens());
$this->assertSame($retryAfter, $exception->getRetryAfter());

Expand Down
Expand Up @@ -38,19 +38,19 @@ public function testConsume()
$limiter->consume(8);
sleep(15);

$limit = $limiter->consume();
$this->assertTrue($limit->isAccepted());
$this->assertSame(10, $limit->getLimit());
$quota = $limiter->consume();
$this->assertTrue($quota->isAccepted());
$this->assertSame(10, $quota->getLimit());

// We are 25% into the new window
$limit = $limiter->consume(5);
$this->assertFalse($limit->isAccepted());
$this->assertEquals(3, $limit->getRemainingTokens());
$quota = $limiter->consume(5);
$this->assertFalse($quota->isAccepted());
$this->assertEquals(3, $quota->getRemainingTokens());

sleep(13);
$limit = $limiter->consume(10);
$this->assertTrue($limit->isAccepted());
$this->assertSame(10, $limit->getLimit());
$quota = $limiter->consume(10);
$this->assertTrue($quota->isAccepted());
$this->assertSame(10, $quota->getLimit());
}

public function testReserve()
Expand Down

0 comments on commit 15bf572

Please sign in to comment.