
# Ratenbegrenzung

Queuety unterstützt Ratenbegrenzungen pro Handler, um eine Überlastung externer APIs oder anderer begrenzter Ressourcen zu vermeiden. Hat ein Handler sein Limit erreicht, lassen Worker seine Jobs warten und übernehmen sie erneut, sobald im Zeitfenster wieder Kapazität verfügbar ist.

## Ratenlimit festlegen

Wende beim Dispatchen eines Jobs ein Ratenlimit an:

```php
use Queuety\Queuety;

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

Die beiden Argumente sind:
1. `$max` – Maximale Anzahl von Ausführungen im Zeitfenster
2. `$window` – Dauer des Zeitfensters in Sekunden

Ein beim Dispatch festgelegtes Limit wird nur im Prozess PHP registriert, der den Job dispatcht. Ein Worker in einem separaten Prozess, etwa `wp queuety work`, sieht es nicht. Limits, die prozessübergreifend gelten müssen, solltest du deshalb wie unten gezeigt in der Methode `config()` des Handlers festlegen ([#3](https://github.com/inline0/queuety/issues/3)).

## Ratenlimits auf Handler-Ebene

Lege Ratenlimits in der Methode `config()` des Handlers fest, damit sie für jeden Dispatch gelten:

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

## So funktioniert es

Ratenlimits werden pro Handler-Namen erfasst. Nachdem ein Job übernommen wurde, registriert der Worker das Limit des Handlers (aus `config()`) und prüft den Ratenbegrenzer. Hat der Handler sein Limit erreicht, stellt der Worker den Job ohne Verzögerung wieder in `pending` ein, verbraucht keinen Versuch und wartet dann `QUEUETY_WORKER_SLEEP` Sekunden, bevor er erneut abfragt. Ein Worker, der mit `--once` gestartet wurde, wird stattdessen beendet.

Jeder Worker führt einen Zähler im Speicher und aktualisiert ihn alle 5 Sekunden anhand der Tabelle `queuety_logs`. Dabei zählt er die `completed`-Protokolleinträge des Handlers aus den letzten `$window` Sekunden. Da die Zählung aus der gemeinsam genutzten Protokolltabelle stammt (und gegebenenfalls aus dem gemeinsamen Cache), gelten Limits auf Handler-Ebene für alle Worker-Prozesse, nicht nur für einen einzelnen Worker. Fehlgeschlagene Ausführungen zählen nicht zum Limit.

## Programmatischer Zugriff

Greife direkt auf den Ratenbegrenzer zu:

```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()` und `is_limited()` wirken auf den Begrenzer im aktuellen Prozess PHP. Ein hier registriertes Limit ändert nicht, was ein separat laufender Worker durchsetzt.

## Anwendungsfälle

- **Externe API-Limits.** Stimme dein Ratenlimit auf das Kontingent von API ab (e.g., 60 RPM bei OpenAI).
- **E-Mail-Drosselung.** Verhindere, dass innerhalb kurzer Zeit zu viele E-Mails versendet werden.
- **Ressourcenschutz.** Begrenze CPU-intensive Handler, damit sie andere Jobs nicht verdrängen.
