Skip to content

[RateLimiter] Document reserve() method and rename to RateLimiter #14435

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 35 additions & 10 deletions rate_limiter.rst
Original file line number Diff line number Diff line change
Expand Up @@ -124,12 +124,13 @@ the number of requests to the API::

use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpKernel\Exception\TooManyRequestsHttpException;
use Symfony\Component\RateLimiter\Limiter;
use Symfony\Component\RateLimiter\RateLimiter;

class ApiController extends AbstractController
{
// the variable name must be: "rate limiter name" + "limiter" suffix
public function index(Limiter $anonymousApiLimiter)
// if you're using autowiring for your services
public function index(RateLimiter $anonymousApiLimiter)
{
// create a limiter based on a unique identifier of the client
// (e.g. the client's IP address, a username/email, an API key, etc.)
Expand Down Expand Up @@ -158,35 +159,59 @@ the number of requests to the API::
for the :ref:`kernel.request event <component-http-kernel-kernel-request>`
and check the rate limiter once for all requests.

In other scenarios you may want instead to wait as long as needed until a new
token is available. In those cases, use the ``wait()`` method::
Wait until a Token is Available
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Instead of dropping a request or process when the limit has been reached,
you might want to wait until a new token is available. This can be achieved
using the ``reserve()`` method::

// src/Controller/ApiController.php
namespace App\Controller;

use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\RateLimiter\Limiter;
use Symfony\Component\RateLimiter\RateLimiter;

class ApiController extends AbstractController
{
public function registerUser(Request $request, Limiter $authenticatedApiLimiter)
public function registerUser(Request $request, RateLimiter $authenticatedApiLimiter)
{
$apiKey = $request->headers->get('apikey');
$limiter = $authenticatedApiLimiter->create($apiKey);

// this blocks the application until the given number of tokens can be consumed
do {
$limit = $limiter->consume(1);
$limit->wait();
} while (!$limit->isAccepted());
$limiter->reserve(1)->wait();

// optional, pass a maximum wait time (in seconds), a MaxWaitDurationExceededException
// is thrown if the process has to wait longer. E.g. to wait at most 20 seconds:
//$limiter->reserve(1, 20)->wait();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this example correct? I would expect it to be:

$limiter->reserve(1)->wait(20);

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unfortunately, yes. I would also prefer your way, but the goal of max time is avoid reserving a token if we know the wait time is longer than its value. So the reserve() method must be aware of the max wait time.

Copy link
Member

@javiereguiluz javiereguiluz Oct 17, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thinking out loud: what if we had these methods instead?

reserve(int $tokens = 1);
reserveAndWait(int $tokens = 1, float $maxTime);


reserve();
reserve(2);
reserveAndWait();
reserveAndWait(2);
reserveAndWait(1, 20);

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In any case, I'm merging your nice PR because this discussion is something to discuss (or not) in the code repository instead of here. Thanks!


// ...
}

// ...
}

The ``reserve()`` method is able to reserve a token in the future. Only use
this method if you're planning to wait, otherwise you will block other
processes by reserving unused tokens.

.. note::

Not all strategies allow reservering tokens in the future. These
strategies may throw an ``ReserveNotSupportedException`` when calling
``reserve()``.

In these cases, you can use ``consume()`` together with ``wait()``, but
there is no guarantee that a token is available after the wait::

// ...
do {
$limit = $limiter->consume(1);
$limit->wait();
} while (!$limit->isAccepted());

Rate Limiter Storage and Locking
--------------------------------

Expand Down