
# Zeitplanung

Queuety unterstützt wiederkehrende Jobs direkt. Statt `wp_cron` zu verwenden, werden Zeitpläne in der Datenbank gespeichert und etwa einmal pro Minute von einem dauerhaft laufenden `wp queuety work`-Prozess (oder dem übergeordneten Prozess eines Worker-Pools) ausgewertet. `wp queuety work --once`, `wp queuety flush` und der integrierte WP-Cron-Fallback des Plugins werten keine Zeitpläne aus ([#14](https://github.com/inline0/queuety/issues/14)); ohne dauerhaft laufenden Worker musst du `wp queuety schedule run` selbst ausführen.

## Intervallbasierte Zeitplanung

Verwende `every()` für einfache, intervallbasierte Zeitpläne. Der Ausdruck kann eine beliebige Zeichenfolge sein, die mit PHP's `DateTimeImmutable::modify()` kompatibel ist:

```php
use Queuety\Queuety;

Queuety::schedule( 'cleanup_handler' )
    ->every( '1 hour' )
    ->on_queue( 'maintenance' );

Queuety::schedule( 'sync_inventory' )
    ->every( '30 minutes' )
    ->on_queue( 'sync' );

Queuety::schedule( 'daily_report', [ 'type' => 'summary' ] )
    ->every( '1 day' );
```

## Cron-Zeitplanung

Verwende `cron()` für standardmäßige cron-Ausdrücke mit 5 Feldern:

```php
// Every day at 3am
Queuety::schedule( 'nightly_cleanup' )
    ->cron( '0 3 * * *' );

// Every Monday at 9am
Queuety::schedule( 'weekly_digest' )
    ->cron( '0 9 * * 1' )
    ->on_queue( 'emails' );

// Every 15 minutes
Queuety::schedule( 'health_check' )
    ->cron( '*/15 * * * *' );
```

## Zeitplanoptionen

Der Builder `PendingSchedule` unterstützt diese Methoden:

| Methode | Beschreibung |
|---|---|
| `every( string $interval )` | Legt einen Intervall-Ausdruck fest |
| `cron( string $expression )` | Legt einen cron-Ausdruck fest |
| `on_queue( string $queue )` | Legt die Zielwarteschlange fest (Standard: `'default'`) |
| `overlap( OverlapPolicy $policy )` | Legt die Überlappungsrichtlinie fest (Standard: `Allow`) |

Du musst vor der Ausführung des Zeitplans entweder `every()` oder `cron()` aufrufen. Ist keines von beiden festgelegt, wird eine `RuntimeException` ausgelöst.

## Überlappungsrichtlinien

Wenn ein geplanter Job noch läuft (ausstehend oder in Bearbeitung), sobald der nächste Zeitimpuls ausgelöst wird, bestimmt die Überlappungsrichtlinie das weitere Vorgehen. Lege sie mit der Methode `overlap()` fest:

```php
use Queuety\Queuety;
use Queuety\Enums\OverlapPolicy;

Queuety::schedule( 'sync_inventory' )
    ->every( '5 minutes' )
    ->overlap( OverlapPolicy::Skip )
    ->on_queue( 'sync' );
```

Das Enum `OverlapPolicy` hat drei Fälle:

| Richtlinie | Wert | Verhalten |
|---|---|---|
| `OverlapPolicy::Allow` | `allow` | Führt immer einen neuen Job aus, auch wenn der vorherige Lauf noch aktiv ist. Dies ist die Standardeinstellung. |
| `OverlapPolicy::Skip` | `skip` | Überspringt den aktuellen Zeitimpuls, wenn für den Handler Jobs ausstehen oder in Bearbeitung sind. `next_run` wird normal aktualisiert, damit der Zeitplan im Takt bleibt. |
| `OverlapPolicy::Buffer` | `buffer` | Wie bei Skip wird der Zeitimpuls nicht ausgeführt und `next_run` rückt zum nächsten regulären Zeitpunkt vor. Anders als bei Skip wird `last_run` nicht aktualisiert. |

### Allow (Standard)

Mehrere Instanzen desselben Handlers können gleichzeitig laufen. Verwende diese Richtlinie für idempotente Handler, bei denen eine Überlappung unproblematisch ist.

```php
Queuety::schedule( 'send_heartbeat_ping' )
    ->every( '1 minute' )
    ->overlap( OverlapPolicy::Allow );
```

### Skip

Der Zeitimpuls wird stillschweigend übersprungen, wenn der Handler noch läuft. Verwende diese Richtlinie für Handler, bei denen eine zweite Instanz unnötig wäre oder Konflikte verursachen würde.

```php
Queuety::schedule( 'rebuild_search_index' )
    ->every( '10 minutes' )
    ->overlap( OverlapPolicy::Skip );
```

### Buffer

Verhält sich derzeit wie Skip: Wenn für den Handler noch ein Job aussteht oder in Bearbeitung ist, wird der Zeitimpuls nicht ausgeführt und `next_run` rückt zum nächsten regulären Zeitpunkt vor. Der einzige Unterschied: Buffer lässt `last_run` unverändert, während Skip den übersprungenen Zeitimpuls als letzten Lauf speichert.

```php
Queuety::schedule( 'sync_inventory' )
    ->every( '5 minutes' )
    ->overlap( OverlapPolicy::Buffer );
```

## So funktioniert es

Beim Erstellen eines Zeitplans speichert Queuety ihn mit dem Ausdruck, Handler, Payload und dem nächsten berechneten Ausführungszeitpunkt in der Tabelle `queuety_schedules`.

Bei jedem Zeitplan-Tick (etwa einmal pro Minute in einem dauerhaft laufenden Worker oder wenn du `wp queuety schedule run` ausführst) prüft der Scheduler, bei welchen Zeitplänen `next_run` bereits verstrichen ist. Für jeden fälligen Zeitplan führt er einen neuen Job aus und aktualisiert `next_run` auf den nächsten Zeitpunkt.

## Zeitpläne über CLI verwalten

```bash
# List all schedules
wp queuety schedule list

# Add a recurring schedule
wp queuety schedule add cleanup_handler --every="1 hour"
wp queuety schedule add nightly_report --cron="0 3 * * *" --queue=reports

# Remove a schedule
wp queuety schedule remove cleanup_handler

# Manually trigger the scheduler
wp queuety schedule run
```

## Zeitpläne über PHP verwalten

```php
// Get the scheduler instance
$scheduler = Queuety::scheduler();

// List all schedules
$schedules = $scheduler->list();

// Remove a schedule by handler name
$scheduler->remove( 'cleanup_handler' );

// Manually trigger a tick
$count = $scheduler->tick();
```

## Payload

Zeitpläne können eine statische Payload enthalten, die an jeden ausgeführten Job übergeben wird:

```php
Queuety::schedule( 'generate_report', [ 'type' => 'daily', 'format' => 'pdf' ] )
    ->every( '1 day' )
    ->on_queue( 'reports' );
```

Bei jeder Ausführung des Zeitplans wird ein neuer Job mit dieser Payload ausgeführt.
