8000 [Bug][RateLimiter] `retryAfter` returns bad value · Issue #46875 · symfony/symfony · GitHub
[go: up one dir, main page]

Skip to content
[Bug][RateLimiter] retryAfter returns bad value #46875
Closed
@bastien70

Description

@bastien70

Symfony version(s) affected

6.1.2

Description

Based on this part of the documentation : https://symfony.com/doc/current/rate_limiter.html#exposing-the-rate-limiter-status
We can retrieve the statuses of the RateLimiter.

$limiter = $anonymousApiLimiter->create($request->getClientIp());
$limit = $limiter->consume();
$headers = [
    'X-RateLimit-Remaining' => $limit->getRemainingTokens(),
    'X-RateLimit-Retry-After' => $limit->getRetryAfter()->getTimestamp(),
    'X-RateLimit-Limit' => $limit->getLimit(),
];

But the retryAfter does not return interesting information in the current case in my opinion.

Take for example this configuration with fixed_window rate limiter :

rate_limiter.yaml

framework:
    rate_limiter:
        custom_thing:
            policy: 'fixed_window'
            limit: 5
            interval: '60 minutes'

And we use it :

    public function myControllerAction(RateLimiterFactory $customThingLimiter): void
    {
        $limiter = $customThingLimiter->create('custom_thing_limit');

        $limit = $limiter->consume();

        $headers = [
            'X-RateLimit-Remaining' => $limit->getRemainingTokens(),
            'X-RateLimit-Retry-After' => $limit->getRetryAfter()->getTimestamp(),
            'X-RateLimit-Limit' => $limit->getLimit(),
        ];

        dump($headers);

      //...
    }

Now let's start with this:

  • We can make 5 requests by hour
  1. The first request of the interval begin at 10:05
  2. The last request of the interval begin at 10:45

From this moment, the user is supposed to be able to make 5 new requests 1 hour after making the very 1st request of the interval, so : 11:05AM

During the 5 requests, the value of $limit->getRetryAfter() will be the current date, which is OK.

If after my 5 requests, I try a new request (Who therefore should not be accepted), the value of $limit->getRetryAfter() should be something like : 2022-07-07 11:05 because I started the first interval request at 10:05.

image

But, his value will be something like : 2022-07-07 11:45. It just added 1 hour from the last request date, and not from the first interval request date.

Now, it's 10:50, I still shouldn't be able to reapply. If I try one, it won't be consumed. The value of $limit->getRetryAfter() should be, a new time 11:05. But it will be 11:50 (One hour after last request date).

The value of retryAfter is meant to represent the date from which a user is expected to be able to make at least one request again.

But, as we can see, that will not be the case.

How to reproduce

Follow the reproducer : https://github.com/bastien70/symfony-rate-limiter/tree/main
Instructions are on the README.

Here's what you might get as results from first request :

image

image

image

image

image

At this moment, the retryAfter should already be the date of the 1st request, plus 1 hour, so 15:30:51.

image

At this moment, it just added 1 hour to the CURRENT date (should be to the FIRST interval request date)

image

A new time, it just added 1 hour to the CURRENT date

Same thing for next tries.

Possible Solution

  1. Keep in memory the exact date of the 1st request of the interval.

  2. When the last request is consumed, then that's when it should add 1 hour to the retryAfter, but still from the 1st request.

  3. If the user retries, then the retryAfter date will not change until the interval is over.

Additional Context

No response

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions

      0