
# Planlægning

Queuety har indbygget understøttelse af tilbagevendende jobs. I stedet for at være afhængig af `wp_cron` gemmes tidsplanerne i databasen og evalueres cirka én gang i minuttet af en langvarigt kørende `wp queuety work`-proces (eller worker-puljens overordnede proces). `wp queuety work --once`, `wp queuety flush` og plugin'ets indbyggede reservefunktion WP-Cron evaluerer ikke tidsplaner ([#14](https://github.com/inline0/queuety/issues/14)); kør selv `wp queuety schedule run`, hvis der ikke kører en langvarig worker.

## Planlægning med intervaller

Brug `every()` til enkle intervalbaserede tidsplaner. Udtrykket kan være enhver streng, der er kompatibel med PHPs `DateTimeImmutable::modify()`:

```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-planlægning

Brug `cron()` til standardudtryk med 5 felter for cron:

```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 * * * *' );
```

## Indstillinger for tidsplaner

Byggeren `PendingSchedule` understøtter følgende metoder:

| Metode | Beskrivelse |
|---|---|
| `every( string $interval )` | Angiv et intervaludtryk |
| `cron( string $expression )` | Angiv et cron-udtryk |
| `on_queue( string $queue )` | Angiv målkøen (standard: `'default'`) |
| `overlap( OverlapPolicy $policy )` | Angiv politik for overlap (standard: `Allow`) |

Du skal kalde enten `every()` eller `cron()`, før tidsplanen sender jobs. Hvis ingen af dem angives, udløses en `RuntimeException`.

## Politikker for overlap

Hvis et planlagt job stadig kører (afventer eller behandles), når næste tidsinterval indtræffer, afgør politikken for overlap, hvad der sker. Angiv den med metoden `overlap()`:

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

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

Enum'en `OverlapPolicy` har tre muligheder:

| Politik | Værdi | Adfærd |
|---|---|---|
| `OverlapPolicy::Allow` | `allow` | Sender altid et nyt job, selv om den forrige kørsel stadig er aktiv. Dette er standardindstillingen. |
| `OverlapPolicy::Skip` | `skip` | Spring det aktuelle tidsinterval over, hvis handleren har jobs, der afventer eller behandles. `next_run` opdateres som normalt, så tidsplanen holder kadencen. |
| `OverlapPolicy::Buffer` | `buffer` | Ligesom med Skip sendes jobbet ikke, og `next_run` rykkes frem til næste normale forekomst. I modsætning til Skip opdateres `last_run` ikke. |

### Allow (standard)

Flere instanser af samme handler kan køre samtidig. Brug denne mulighed til idempotente handlers, hvor overlap ikke gør noget.

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

### Skip

Tidsintervallet springes lydløst over, hvis handleren stadig kører. Brug dette til handlers, hvor det ville være spild eller skabe konflikter at køre endnu en instans.

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

### Buffer

Fungerer i øjeblikket som Skip: Hvis handleren stadig har et job, der afventer eller behandles, sendes jobbet ikke, og `next_run` flyttes frem til næste normale forekomst. Den eneste forskel er, at Buffer lader `last_run` være uændret, mens Skip registrerer det oversprungne tidsinterval som den seneste kørsel.

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

## Sådan fungerer det

Når en tidsplan oprettes, gemmer Queuety den i tabellen `queuety_schedules` sammen med udtrykket, handleren, nyttedataene og det næste beregnede kørselsstarttidspunkt.

Ved hvert planlægningsinterval (cirka én gang i minuttet i en langvarigt kørende worker, eller når du kører `wp queuety schedule run`) undersøger planlæggeren, om nogen tidsplaner har passeret deres `next_run`. For hver tidsplan, der skal køres, sendes et nyt job, og `next_run` opdateres til næste forekomst.

## Administrer tidsplaner via CLI

```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
```

## Administrer tidsplaner via PHP

```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();
```

## Nyttedata

Tidsplaner kan indeholde statiske nyttedata, som sendes med hvert job:

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

Hver gang tidsplanen udløses, sendes et nyt job med disse nyttedata.
