
# Ограничение частоты запросов

Queuety поддерживает ограничение частоты запросов для каждого обработчика, чтобы не перегружать внешние API и другие ресурсы с ограниченной пропускной способностью. Когда обработчик достигает своего лимита, обработчики оставляют его задания в ожидании и забирают их снова, когда в окне появляется свободное место.

## Настройка ограничения частоты

Примените ограничение частоты при отправке задания:

```php
use Queuety\Queuety;

Queuety::dispatch( 'call_openai', $payload )
    ->rate_limit( 10, 60 ); // max 10 executions per 60 seconds
```

Два аргумента означают:
1. `$max` — максимальное число выполнений, разрешенных в окне
2. `$window` — длительность окна в секундах

Ограничение, заданное при отправке, регистрируется только в процессе PHP, который отправляет задание. Обработчик, работающий в отдельном процессе, например `wp queuety work`, его не увидит. Поэтому ограничения, которые должны действовать между процессами, задавайте в `config()` обработчика, как показано ниже ([#3](https://github.com/inline0/queuety/issues/3)).

## Ограничения частоты на уровне обработчика

Задайте ограничения частоты в методе `config()` обработчика, чтобы они применялись при каждой отправке:

```php
use Queuety\Handler;

class CallOpenAIHandler implements Handler {
    public function handle( array $payload ): void {
        // Call the API...
    }

    public function config(): array {
        return [
            'rate_limit' => [ 60, 60 ], // 60 requests per minute
        ];
    }
}
```

## Как это работает

Ограничения частоты отслеживаются по имени обработчика. Забрав задание, обработчик регистрирует лимит обработчика (из его `config()`) и проверяет ограничитель частоты. Если обработчик достиг лимита, он сразу возвращает задание в `pending` без задержки и без расходования попытки, а затем ждет `QUEUETY_WORKER_SLEEP` секунд перед следующим опросом. Обработчик, запущенный с `--once`, вместо этого завершает работу.

Каждый обработчик хранит счетчик в памяти и каждые 5 секунд обновляет его из таблицы `queuety_logs`, подсчитывая записи журнала `completed` для обработчика за последние `$window` секунд. Поскольку подсчет ведется по общей таблице журналов (и общему кэшу, если он настроен), ограничения на уровне обработчика действуют во всех процессах обработчиков, а не только внутри одного обработчика. Неудачные выполнения не учитываются в лимите.

## Программный доступ

Получите прямой доступ к ограничителю частоты:

```php
$limiter = Queuety::rate_limiter();

// Register a rate limit
$limiter->register( 'call_openai', 60, 60 );

// Check whether a handler has reached its limit
$limited = $limiter->is_limited( 'call_openai' ); // true while the limit is reached

// Seconds until the current window resets
$wait = $limiter->time_until_available( 'call_openai' );
```

`register()` и `is_limited()` воздействуют на ограничитель в текущем процессе PHP. Регистрация ограничения здесь не меняет правила, применяемые отдельно запущенным обработчиком.

## Сценарии использования

- **Внешние лимиты API.** Согласуйте ограничение частоты с квотой API (e.g., 60 запросов в минуту у OpenAI).
- **Ограничение частоты отправки писем.** Не допускайте отправки слишком большого количества писем за короткий промежуток времени.
- **Защита ресурсов.** Ограничьте обработчики с интенсивной нагрузкой на CPU, чтобы они не лишали другие задания ресурсов.
