
# Omezování rychlosti

Queuety podporuje omezení rychlosti pro jednotlivé handlery, aby nedocházelo k přetížení externích API nebo jiných omezených zdrojů. Když handler dosáhne svého limitu, workeři ponechají jeho úlohy čekat a znovu je převezmou, jakmile se v okně uvolní místo.

## Nastavení limitu rychlosti

Použití limitu rychlosti při odesílání úlohy:

```php
use Queuety\Queuety;

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

Dva argumenty jsou:
1. `$max` – Maximální počet spuštění povolených v okně
2. `$window` – Délka okna v sekundách

Limit nastavený při odeslání se registruje pouze v procesu PHP, který úlohu odesílá. Worker běžící v samostatném procesu, například `wp queuety work`, jej nevidí, proto nastavte limity, které musí platit napříč procesy, v metodě `config()` handleru, jak je ukázáno níže ([#3](https://github.com/inline0/queuety/issues/3)).

## Limity rychlosti na úrovni handleru

Nastavte limity rychlosti v metodě `config()` handleru, aby se uplatnily při každém odeslání:

```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
        ];
    }
}
```

## Jak to funguje

Limity rychlosti se sledují podle názvu handleru. Po převzetí úlohy worker zaregistruje limit handleru (z jeho `config()`) a zkontroluje omezovač rychlosti. Pokud handler dosáhl limitu, worker úlohu okamžitě vrátí do `pending` bez prodlevy a bez spotřebování pokusu, poté před dalším dotazem na úlohy počká `QUEUETY_WORKER_SLEEP` sekund. Worker spuštěný s `--once` se místo toho ukončí.

Každý worker uchovává čítač v paměti a každých 5 sekund jej obnovuje z tabulky `queuety_logs`, přičemž počítá záznamy protokolu `completed` daného handleru za posledních `$window` sekund. Protože počet vychází ze sdílené tabulky protokolů (a ze sdílené mezipaměti, je-li nastavena), uplatňují se limity na úrovni handleru napříč všemi workery, nejen v rámci jednoho workeru. Neúspěšná spuštění se do limitu nepočítají.

## Programový přístup

Přímý přístup k omezovači rychlosti:

```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()` a `is_limited()` pracují s omezovačem v aktuálním procesu PHP. Registrace limitu zde nemění pravidla, která uplatňuje samostatně běžící worker.

## Příklady použití

- **Limity externích API.** Nastavte limit rychlosti podle kvóty API (e.g např. 60 požadavků za minutu u OpenAI).
- **Omezení odesílání e-mailů.** Zabraňte odeslání příliš mnoha e-mailů v krátkém časovém úseku.
- **Ochrana zdrojů.** Omezte handlery náročné na CPU, aby nebraly prostředky ostatním úlohám.
