
# Erste Schritte

## Installation

### Voraussetzungen

- PHP 8.2 oder höher und WordPress 6.4 oder höher
- MySQL 8.0+ oder MariaDB 10.6+. Worker beanspruchen Jobs und fällige Zeitpläne mit `SELECT ... FOR UPDATE SKIP LOCKED`, was ältere Server mit einem Syntaxfehler ablehnen.
- `mysqli` oder `pdo_mysql` (siehe unten)
- WP-CLI auf dem Server, wenn du dedizierte Worker mit `wp queuety work` ausführst
- Die Erweiterungen `pcntl` und `posix` PHP, wenn du Worker-Pools mit `--workers` oder `--min-workers`/`--max-workers` ausführst

Queuety benötigt entweder `mysqli` oder `pdo_mysql` in der PHP-Laufzeitumgebung, die WordPress und WP-CLI lädt. Der Treiber wird automatisch ausgewählt. Für WordPress-Laufzeitumgebungen wird `mysqli` bevorzugt, während `pdo_mysql` für den eigenständigen oder direkten Einsatz verfügbar bleibt.

Wenn du die laufzeitgestützte Datenbanknutzung ohne die Plugin-Ebene von WordPress möchtest, siehe [Eigenständige Verwendung](https://queuety.inline0.dev/de/docs/features/standalone-use). Dabei werden dieselbe Verbindung und dieselben Laufzeittabellen genutzt, aber die WordPress-Hook-Bridges und die WP-CLI-Registrierung sind nicht enthalten.

### Aus dem Release-Zip

Jedes [GitHub-Release](https://github.com/inline0/queuety/releases) enthält ein `queuety.zip`-Paket: das Plugin mit bereits installierten Composer-Abhängigkeiten. Installiere das neueste Paket mit WP-CLI:

```bash
wp plugin install https://github.com/inline0/queuety/releases/latest/download/queuety.zip --activate
```

Du kannst das Zip auch herunterladen und unter Plugins > Installieren > Plugin hochladen hochladen. Das ist der einfachste Weg für eine normale WordPress-Website.

### Über Composer für Composer-verwaltete WordPress-Installationen

```bash
composer require queuety/queuety
```

Aktiviere anschließend das Plugin:

```bash
wp plugin activate queuety
```

Der Pakettyp lautet `wordpress-plugin`. Composer legt das Paket nur dann in deinem Plugin-Verzeichnis ab, wenn das Projekt `composer/installers` benötigt und diesen Typ einem Installationspfad zuordnet, wie es Bedrock tut. Außerdem muss das Projekt ein eigenes `vendor/autoload.php` laden (auch das übernimmt Bedrock). Ohne `composer/installers` installiert Composer das Paket in `vendor/queuety/queuety`, wo WordPress es nicht findet. Verwende daher bei einer einfachen WordPress-Installation das Release-Zip.

### Aus dem Quellcode während der Entwicklung

Lege das Repository in `wp-content/plugins/queuety` ab und installiere dann die PHP-Abhängigkeiten:

```bash
composer install
wp plugin activate queuety
```

Bei der Aktivierung erstellt Queuety seine Datenbanktabellen (`queuety_jobs`, `queuety_workflows`, `queuety_logs`, `queuety_schedules`, `queuety_queue_states`, `queuety_webhooks`, `queuety_locks`, `queuety_signals`, `queuety_workflow_dependencies`, `queuety_workflow_dispatch_keys`, `queuety_batches`, `queuety_chunks`, `queuety_workflow_events`, `queuety_artifacts`, `queuety_state_machines`, `queuety_state_machine_events`), damit Worker Jobs direkt aus MySQL beanspruchen können.

Standardmäßig plant das Plugin außerdem jede Minute über WordPress cron einen einmaligen Worker-Lauf für die Warteschlange `default`. So kannst du Jobs und Workflows ohne Shell-Zugriff ausführen, solange sie auf `default` bleiben und WordPress cron regelmäßig ausgelöst wird. Jeder Lauf verarbeitet höchstens einen Job. Wiederkehrende Zeitpläne werden nicht ausgeführt, veraltete Jobs nicht wiederhergestellt und Workflow-Fristen nicht geprüft; all das geschieht nur in einem `wp queuety work`-Prozess mit langer Laufzeit ([#14](https://github.com/inline0/queuety/issues/14)). Für benutzerdefinierte Warteschlangen, wiederkehrende Zeitpläne, geringere Latenz oder höhere Auslastung solltest du dedizierte `wp queuety work`-Prozesse verwenden.

## Konfiguration

Alle Konstanten sind optional. Definiere sie in `wp-config.php`, bevor das Plugin geladen wird, oder bevor Queuety bei einer eingebetteten Installation im Stil eines Themes startet:

| Konstante | Standardwert | Beschreibung |
|---|---|---|
| `QUEUETY_RETENTION_DAYS` | `7` | Standardalter in Tagen für `wp queuety purge` und `Queuety::purge()`. Es wird nichts automatisch gelöscht |
| `QUEUETY_LOG_RETENTION_DAYS` | `0` | Wird von `Config::log_retention_days()` gelesen, aber noch nicht verwendet; Protokolle bleiben erhalten, bis du `Queuety::purge_logs()` aufrufst ([#15](https://github.com/inline0/queuety/issues/15)) |
| `QUEUETY_MAX_EXECUTION_TIME` | `300` | Maximale Sekunden pro Job bis zum Timeout (noch nicht erzwungen, siehe [Jobs](https://queuety.inline0.dev/de/docs/jobs#timeout)) |
| `QUEUETY_WORKER_SLEEP` | `1` | Wartezeit in Sekunden bei leerer Warteschlange |
| `QUEUETY_WORKER_MAX_JOBS` | `1000` | Maximale Anzahl Jobs, bevor der Worker neu gestartet wird |
| `QUEUETY_WORKER_MAX_MEMORY` | `128` | Maximale MB, bevor der Worker neu gestartet wird |
| `QUEUETY_RETRY_BACKOFF` | `exponential` | Backoff-Strategie: `exponential`, `linear` oder `fixed` |
| `QUEUETY_STALE_TIMEOUT` | `600` | Sekunden bis zur Wiederherstellung hängender Jobs |
| `QUEUETY_CACHE_TTL` | `5` | Standard-Cache-TTL in Sekunden |
| `QUEUETY_DEBUG` | `false` | Ausführliche Worker-Protokollierung aktivieren |
| `QUEUETY_CLI_COMMAND` | `queuety` | Name des WP-CLI-Root-Befehls |
| `QUEUETY_TABLE_PREFIX` | `queuety_` | Gemeinsamer Basisname für alle Queuety-Tabellen nach dem WordPress-DB-Präfix |
| `QUEUETY_TABLE_JOBS` | `queuety_jobs` | Name der Jobs-Tabelle |
| `QUEUETY_TABLE_WORKFLOWS` | `queuety_workflows` | Name der Workflows-Tabelle |
| `QUEUETY_TABLE_LOGS` | `queuety_logs` | Name der Protokoll-Tabelle |
| `QUEUETY_TABLE_SCHEDULES` | `queuety_schedules` | Name der Zeitpläne-Tabelle |
| `QUEUETY_TABLE_SIGNALS` | `queuety_signals` | Name der Signals-Tabelle |
| `QUEUETY_TABLE_WORKFLOW_DEPENDENCIES` | `queuety_workflow_dependencies` | Name der Tabelle für Workflow-Abhängigkeitswartezeiten |
| `QUEUETY_TABLE_WORKFLOW_DISPATCH_KEYS` | `queuety_workflow_dispatch_keys` | Name der Idempotenz-Tabelle für dauerhafte Workflows |
| `QUEUETY_TABLE_CHUNKS` | `queuety_chunks` | Name der Tabelle für Streaming-Chunks |
| `QUEUETY_TABLE_QUEUE_STATES` | `queuety_queue_states` | Name der Warteschlangenzustände-Tabelle |
| `QUEUETY_TABLE_WEBHOOKS` | `queuety_webhooks` | Name der Webhooks-Tabelle |
| `QUEUETY_TABLE_LOCKS` | `queuety_locks` | Name der Sperren-Tabelle |
| `QUEUETY_TABLE_BATCHES` | `queuety_batches` | Name der Batches-Tabelle |
| `QUEUETY_TABLE_WORKFLOW_EVENTS` | `queuety_workflow_events` | Name der Workflow-Ereignisprotokoll-Tabelle |
| `QUEUETY_TABLE_ARTIFACTS` | `queuety_artifacts` | Name der Workflow-Artefakte-Tabelle |
| `QUEUETY_TABLE_STATE_MACHINES` | `queuety_state_machines` | Name der Zustandsautomaten-Tabelle |
| `QUEUETY_TABLE_STATE_MACHINE_EVENTS` | `queuety_state_machine_events` | Name der Zustandsautomaten-Ereignisprotokoll-Tabelle |
| `QUEUETY_DB_DRIVER` | `auto` | MySQL-Treiber: `auto`, `mysqli` oder `pdo`. `auto` bevorzugt `mysqli`, wenn WordPress geladen ist, andernfalls `pdo_mysql` und danach `mysqli` |

Die Konstanten für Ressourcenaufnahme und adaptive Pools (`QUEUETY_RESOURCE_*`, `QUEUETY_WORKER_POOL_*`) sind auf der Seite [Workers](https://queuety.inline0.dev/de/docs/features/workers) aufgeführt.

`QUEUETY_TABLE_PREFIX` ändert nur den Queuety-Teil der Tabellennamen. Das Datenbankpräfix WordPress wird weiterhin vorangestellt, sodass `$wpdb->prefix = 'wp_'` und `QUEUETY_TABLE_PREFIX = 'themequeue_'` Tabellen wie `wp_themequeue_jobs` ergeben. Explizite `QUEUETY_TABLE_*`-Konstanten haben für einzelne Tabellen weiterhin Vorrang.

Eigenständige Verbindungen können auch pro Verbindung das Tabellenpräfix Queuety festlegen:

```php
$conn = new Connection(
    host: '127.0.0.1:3306',
    dbname: 'wordpress',
    user: 'root',
    password: 'secret',
    prefix: 'wp_',
    table_prefix: 'themequeue_',
);
```

So bleibt das Datenbankpräfix WordPress erhalten und nur das Tabellensegment Queuety ändert sich.

## Dein erster Job

Erstelle eine Handler-Klasse, die das Interface `Handler` implementiert:

```php
use Queuety\Handler;

class SendEmailHandler implements Handler {
    public function handle( array $payload ): void {
        wp_mail( $payload['to'], $payload['subject'], $payload['body'] );
    }

    public function config(): array {
        return [
            'queue'           => 'emails',
            'max_attempts'    => 5,
        ];
    }
}
```

Registriere sie und löse den Job aus:

```php
use Queuety\Queuety;

Queuety::register( 'send_email', SendEmailHandler::class );

Queuety::dispatch( 'send_email', [
    'to'      => 'user@example.com',
    'subject' => 'Welcome',
    'body'    => 'Hello from Queuety!',
] );
```

Oder verwende die moderne auslösbare Job-Klasse:

```php
use Queuety\Contracts\Job;
use Queuety\Dispatchable;

readonly class SendEmailJob implements Job {
    use Dispatchable;

    public function __construct(
        public string $to,
        public string $subject,
        public string $body,
    ) {}

    public function handle(): void {
        wp_mail( $this->to, $this->subject, $this->body );
    }
}

SendEmailJob::dispatch( 'user@example.com', 'Welcome', 'Hello from Queuety!' );
```

## Dein erster Workflow

Definiere Step-Handler, die das Interface `Step` implementieren:

```php
use Queuety\Step;

class FetchDataHandler implements Step {
    public function handle( array $state ): array {
        $user = get_user_by( 'ID', $state['user_id'] );
        return [ 'user_name' => $user->display_name ];
    }

    public function config(): array {
        return [];
    }
}

class CallLLMHandler implements Step {
    public function handle( array $state ): array {
        $response = wp_remote_post( 'https://api.openai.com/v1/chat/completions', [
            'body' => json_encode( [ 'prompt' => "Generate report for {$state['user_name']}" ] ),
        ] );
        return [ 'llm_response' => wp_remote_retrieve_body( $response ) ];
    }

    public function config(): array {
        return [ 'max_attempts' => 5 ];
    }
}

class FormatOutputHandler implements Step {
    public function handle( array $state ): array {
        $url = save_report( $state['user_name'], $state['llm_response'] );
        return [ 'report_url' => $url ];
    }

    public function config(): array {
        return [];
    }
}
```

Löse den Workflow aus:

```php
$workflow_id = Queuety::workflow( 'generate_report' )
    ->then( FetchDataHandler::class )
    ->then( CallLLMHandler::class )
    ->then( FormatOutputHandler::class )
    ->dispatch( [ 'user_id' => 42 ] );
```

## Worker starten

```bash
wp queuety work
```

Der Worker fragt nach ausstehenden Jobs, führt sie aus, setzt Workflows fort und wartet, wenn die Warteschlange leer ist. Drücke `Ctrl+C` zum Stoppen; dadurch wird der Prozess sofort beendet, auch mitten in einem Job (siehe [Workers](https://queuety.inline0.dev/de/docs/features/workers#worker-lifecycle)).

`SendEmailHandler` oben sendet seine Jobs über `config()` an die Warteschlange `emails`, sodass ein Worker auf `default` sie nicht abholt. Benenne jede Warteschlange, die du verwendest:

```bash
wp queuety work --queue=emails,default
```

Wenn du keinen dedizierten Worker ausführst, hat Queuety weiterhin den oben beschriebenen standardmäßigen WordPress-cron-Fallback, der einmal pro Minute läuft und auf die `default`-Warteschlange sowie einen Auftrag pro Ausführung beschränkt ist. Der dedizierte Worker bietet einen höheren Durchsatz und geringere Latenz.

Führe in der Produktion mehrere Worker aus:

```bash
wp queuety work --workers=4
```

Verarbeite mehrere Warteschlangen mit Prioritätsreihenfolge:

```bash
wp queuety work --queue=high,default,low
```

## Wie geht es weiter?

- [Jobs](https://queuety.inline0.dev/de/docs/jobs) für den vollständigen Leitfaden zum Auslösen und Verarbeiten von Jobs
- [Workflows](https://queuety.inline0.dev/de/docs/workflows) für Ketten, parallele Steps, bedingte Verzweigungen und Workflows ausführen
- [Middleware](https://queuety.inline0.dev/de/docs/features/middleware) für Ratenbegrenzung, Drosselung und benutzerdefinierte Pipeline-Logik
- [Batching](https://queuety.inline0.dev/de/docs/features/batching) für das Auslösen von Jobgruppen mit Callbacks
- [Streaming Steps](https://queuety.inline0.dev/de/docs/workflows/streaming) zum Speichern gestreamter Daten Chunk für Chunk
- [Scheduling](https://queuety.inline0.dev/de/docs/features/scheduling) für wiederkehrende Jobs
- [Caching](https://queuety.inline0.dev/de/docs/features/caching) für die erweiterbare Cache-Schicht
- [Testing](https://queuety.inline0.dev/de/docs/features/testing) für QueueFake und Test-Assertions
- [CLI Reference](https://queuety.inline0.dev/de/docs/cli) für alle verfügbaren Befehle
- [PHP API](https://queuety.inline0.dev/de/docs/api) für programmgesteuerten Zugriff
