Final Class Yiisoft\Yii\RateLimiter\Counter
| Inheritance | Yiisoft\ |
|---|---|
| Implements | Yiisoft\ |
Counter implements generic cell rate limit algorithm (GCRA) that ensures that after reaching the limit further increments are distributed equally.
Public Methods
| Method | Description | Defined By |
|---|---|---|
| __construct() | Yiisoft\ |
|
| hit() | Determines when the next hit won't be limited. Should be called on each request. | Yiisoft\ |
Constants
| Constant | Value | Description | Defined By |
|---|---|---|---|
| DEFAULT_MAX_CAS_ATTEMPTS | 10 | Yiisoft\ |
|
| DEFAULT_TTL | 86400 | Yiisoft\ |
|
| ID_PREFIX | 'rate-limiter-' | Yiisoft\ |
|
| MILLISECONDS_PER_SECOND | 1000 | Yiisoft\ |
Method Details
| public mixed __construct ( Yiisoft\ | ||
| $storage | Yiisoft\ |
Storage to use for counter values. |
| $limit | integer |
Maximum number of increments that could be performed before increments are limited. |
| $periodInSeconds | integer |
Period to apply limit to. |
| $storageTtlInSeconds | integer |
Storage TTL. Should be higher than |
| $storagePrefix | string |
Storage prefix. |
| $timer | Yiisoft\ |
Timer instance to get current time from. |
| $maxCasAttempts | integer |
Maximum number of times to retry saveIfNotExists/saveCompareAndSwap operations before returning an error. |
public function __construct(
private StorageInterface $storage,
private int $limit,
int $periodInSeconds,
private int $storageTtlInSeconds = self::DEFAULT_TTL,
private string $storagePrefix = self::ID_PREFIX,
?TimerInterface $timer = null,
private int $maxCasAttempts = self::DEFAULT_MAX_CAS_ATTEMPTS,
) {
if ($limit < 1) {
throw new InvalidArgumentException('The limit must be a positive value.');
}
if ($periodInSeconds < 1) {
throw new InvalidArgumentException('The period must be a positive value.');
}
$this->periodInMilliseconds = $periodInSeconds * self::MILLISECONDS_PER_SECOND;
$this->timer = $timer ?: new MicrotimeTimer();
$this->incrementIntervalInMilliseconds = $this->periodInMilliseconds / $this->limit;
}
Determines when the next hit won't be limited. Should be called on each request.
| public Yiisoft\ | ||
| $id | string |
Counter ID. Counters with distinct IDs do not affect each other. For example, using a current user ID will limit for the current user and using IP will limit by IP. |
| return | Yiisoft\ |
Information about when the next hit won't be limited. |
|---|---|---|
public function hit(string $id): CounterState
{
$attempts = 0;
$isFailStoreUpdatedData = false;
do {
// Last increment time.
// In GCRA it's known as arrival time.
$lastIncrementTimeInMilliseconds = $this->timer->nowInMilliseconds();
$lastStoredTheoreticalNextIncrementTime = $this->getLastStoredTheoreticalNextIncrementTime($id);
$theoreticalNextIncrementTime = $this->calculateTheoreticalNextIncrementTime(
$lastIncrementTimeInMilliseconds,
$lastStoredTheoreticalNextIncrementTime,
);
$remaining = $this->calculateRemaining($lastIncrementTimeInMilliseconds, $theoreticalNextIncrementTime);
$resetAfter = $this->calculateResetAfter($theoreticalNextIncrementTime);
if ($remaining === 0) {
break;
}
$isStored = $this->storeTheoreticalNextIncrementTime(
$id,
$theoreticalNextIncrementTime,
$lastStoredTheoreticalNextIncrementTime,
);
if ($isStored) {
break;
}
$attempts++;
if ($attempts >= $this->maxCasAttempts) {
$isFailStoreUpdatedData = true;
break;
}
} while (true);
return new CounterState($this->limit, $remaining, $resetAfter, $isFailStoreUpdatedData);
}
User Contributed Notes
Leave a comment
Join the conversation to share a note.