Zum Inhalt springen

Jobs

Jobs sind die zentrale Arbeitseinheit in Queuety. Löse einen Job mit Handler-Namen und Payload aus; ein Worker übernimmt ihn und führt ihn im Hintergrund aus.

Jobs auslösen

PHP
use Queuety\Queuety;

Queuety::dispatch( 'send_email', [ 'to' => 'user@example.com' ] );

dispatch() gibt einen PendingJob-Builder zurück. Du kannst Optionen verketten, bevor der Job automatisch ausgelöst wird:

PHP
use Queuety\Enums\Priority;

Queuety::dispatch( 'send_email', [ 'to' => 'user@example.com' ] )
    ->on_queue( 'emails' )
    ->with_priority( Priority::High )
    ->delay( 300 )
    ->max_attempts( 5 );

Rufe ->id() auf, um sofort die Job-ID zu erhalten:

PHP
$job_id = Queuety::dispatch( 'send_email', $payload )->id();

Handler schreiben

Ein Handler ist eine beliebige Klasse, die das Interface Queuety\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,
            'backoff'         => 'exponential',
        ];
    }
}

Die Methode handle() erhält die Job-Payload und führt die Arbeit aus. Die Methode config() gibt optionale Standardwerte zurück:

Schlüssel Typ Beschreibung
queue string Standardname der Warteschlange
max_attempts int Maximale Anzahl Ausführungen einschließlich des ersten Versuchs, bevor der Job zurückgestellt wird
backoff string Backoff-Strategie für Wiederholungen: exponential, linear oder fixed
rate_limit array Ratenbegrenzung als [max_executions, window_seconds]
concurrency_group string Gemeinsame Worker-Aufnahmegruppe für aufwendige Handler
concurrency_limit int Maximale Anzahl gleichzeitig verarbeiteter Jobs in dieser Gruppe
cost_units int Relative Ausführungskosten für gewichtete Warteschlangen- oder Anbieterbudgets, Workflow-Budgets und Aufnahmeheuristiken

Diese Ressourcenschlüssel sind Teil des umfassenderen Modells für Ressourcenverwaltung von Queuety, in dem Job-Metadaten, Workflow-Budgets und Worker-Aufnahme zusammenwirken.

Registriere den Handler:

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

Oder verwende PHP-Attribute zur automatischen Registrierung.

Priorität

Jobs haben vier Prioritätsstufen. Worker beanspruchen Jobs mit höherer Priorität zuerst.

PHP
use Queuety\Enums\Priority;

Queuety::dispatch( 'handler', $payload )->with_priority( Priority::Urgent );
Stufe Wert Beschreibung
Priority::Low 0 Standard. Hintergrundarbeit, die warten kann.
Priority::Normal 1 Normale Priorität.
Priority::High 2 Wird vor Low und Normal verarbeitet.
Priority::Urgent 3 Wird zuerst verarbeitet. Sparsam verwenden.

Verzögerung

Verzögere einen Job, damit er erst nach der angegebenen Anzahl Sekunden verfügbar wird:

PHP
Queuety::dispatch( 'send_reminder', $payload )->delay( 3600 ); // 1 hour

Der Job wird sofort eingefügt, aber Worker beanspruchen ihn erst nach Ablauf der Verzögerung.

Eindeutige Jobs

Verhindere doppelte Auslösungen für denselben Handler und dieselbe Payload. Wenn bereits ein ausstehender oder verarbeiteter Job mit demselben Handler und derselben Payload existiert, wird dessen Job-ID zurückgegeben, statt einen neuen Job anzulegen.

PHP
Queuety::dispatch( 'sync_inventory', [ 'sku' => 'ABC-123' ] )->unique();

Job-Abhängigkeiten

Ein Job kann von einem anderen Job abhängen. Ein Worker beansprucht ihn erst, wenn die Abhängigkeit abgeschlossen ist.

PHP
$first_id = Queuety::dispatch( 'import_data', $payload )->id();

Queuety::dispatch( 'send_notification', [ 'import_id' => $first_id ] )
    ->after( $first_id );

Batch-Auslösung

Löse mehrere Jobs in einem einzigen INSERT mit mehreren Zeilen aus, um den Durchsatz zu maximieren:

PHP
$job_ids = Queuety::batch( [
    [ 'handler' => 'send_email', 'payload' => [ 'to' => 'a@example.com' ] ],
    [ 'handler' => 'send_email', 'payload' => [ 'to' => 'b@example.com' ] ],
    [ 'handler' => 'send_email', 'payload' => [ 'to' => 'c@example.com' ] ],
] );

Jeder Eintrag kann optionale Schlüssel enthalten: queue, priority, delay, max_attempts.

Wiederholungen und Backoff

Wenn ein Job eine Ausnahme auslöst, setzt Queuety ihn mit einem verzögerten available_at zurück auf pending, bis er insgesamt max_attempts-mal ausgeführt wurde (der erste Lauf zählt; max_attempts( 3 ) bedeutet also bis zu zwei Wiederholungen). Die Wartezeit vor jedem erneuten Versuch richtet sich nach der Backoff-Strategie. Dabei bezeichnet attempt die Nummer des gerade fehlgeschlagenen Laufs:

Strategie Verzögerungsformel Beispiel (nach Lauf 1, 2, 3)
exponential 2^attempt * 30 Sekunden, begrenzt auf 3600 60s, 120s, 240s
linear attempt * 60 Sekunden 60s, 120s, 180s
fixed 60 Sekunden 60s, 60s, 60s

Lege den globalen Standard in wp-config.php fest:

PHP
define( 'QUEUETY_RETRY_BACKOFF', 'linear' );

Oder pro Handler in config():

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

Oder per Dispatch:

PHP
Queuety::dispatch( 'handler', $payload )->max_attempts( 10 );

Zurückgestellte Jobs

Wenn alle Wiederholungsversuche ausgeschöpft sind, wird ein Job als begraben markiert. Begrabene Jobs bleiben zur Prüfung in der Datenbank, werden aber nicht verarbeitet. Du kannst sie manuell erneut versuchen. Bei einem manuellen erneuten Versuch wird der Job wieder zu pending, ohne den Versuchszähler zurückzusetzen. Ein erneut versuchter begrabener Job erhält also einen weiteren Lauf und wird wieder begraben, wenn dieser Lauf fehlschlägt:

PHP
// Retry a specific buried job
Queuety::retry( $job_id );

// Retry all buried jobs
$count = Queuety::retry_buried();

Oder über CLI:

Shell
wp queuety retry 42
wp queuety retry-buried

Timeout

Queuety startet einen pcntl_alarm() für die maximale Ausführungszeit und soll den Versuch mit einem TimeoutException fehlschlagen lassen, wenn diese überschritten wird. Dieses Timeout wird derzeit nicht erzwungen (#10): Der Worker aktiviert keine asynchronen Signale, sodass ein lang laufender Handler bis zum Ende ausgeführt wird. Ein in sleep() blockierter Handler wird beim Auslösen des Alarms abgebrochen und anschließend als abgeschlossen markiert. Konfiguriere das Timeout global:

PHP
define( 'QUEUETY_MAX_EXECUTION_TIME', 300 ); // 5 minutes

Jobs, die länger als QUEUETY_STALE_TIMEOUT Sekunden den Status processing haben, werden automatisch wiederhergestellt:

PHP
define( 'QUEUETY_STALE_TIMEOUT', 600 ); // 10 minutes

Veraltete Jobs manuell wiederherstellen:

Shell
wp queuety recover

Warteschlangen

Jobs werden an benannte Warteschlangen gesendet. Jeder Worker verarbeitet eine Warteschlange oder eine geordnete Liste von Warteschlangen (siehe Workers):

PHP
Queuety::dispatch( 'handler', $payload )->on_queue( 'emails' );
Shell
wp queuety work --queue=emails

Die Standardwarteschlange ist default. Du kannst für jede Warteschlange einen eigenen Worker-Prozess ausführen, um Workloads voneinander zu isolieren.

Ratenbegrenzung

Begrenze, wie oft ein Handler innerhalb eines Zeitfensters ausgeführt werden kann:

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

Weitere Details findest du unter Ratenbegrenzung.

Dispatchbare Jobklassen

Queuety v0.6.0 führt ein klassenbasiertes Job-API ein. Statt Handlernamen, Payload und Handlerklasse getrennt zu verwalten, definierst du eine einzelne Klasse, die alles kapselt: die Payload (als typisierte öffentliche Eigenschaften), die Ausführungslogik und optionale Middleware.

Die Schnittstelle Contracts\Job

PHP
use Queuety\Contracts\Job;

interface Job {
    public function handle(): void;
}

Eine Job-Klasse speichert ihre Payload in öffentlichen Eigenschaften und führt ihre eigene Logik in handle() aus. Eine separate Methode config() oder Handlerregistrierung ist nicht erforderlich.

Das Trait Dispatchable

Füge deiner Jobklasse das Trait Dispatchable hinzu, damit sie eine statische Methode dispatch() erhält:

PHP
use Queuety\Contracts\Job;
use Queuety\Dispatchable;

class SendEmailJob implements Job {
    use Dispatchable;

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

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

Sende den Job, indem du die Konstruktorargumente übergibst:

PHP
SendEmailJob::dispatch( 'user@example.com', 'Welcome!', 'Hello there.' )
    ->on_queue( 'emails' )
    ->with_priority( Priority::High );

Das Trait Dispatchable ruft intern Queuety::dispatch() auf, sodass alle Optionen von PendingJob (queue, priority, delay, max_attempts, unique, after) genauso funktionieren.

So funktioniert die Serialisierung

Wenn du eine Instanz von Job sendest, extrahiert Queuetys JobSerializer den vollständig qualifizierten Klassennamen als Handler und alle öffentlichen Eigenschaften als Payload. Auf der Worker-Seite rekonstruiert der Serializer die Instanz, indem er die Payload-Schlüssel den Namen der Konstruktorparameter zuordnet. Backed Enums werden in ihren zugrunde liegenden Wert serialisiert und automatisch wiederhergestellt.

Job-Eigenschaften

Jobklassen können öffentliche Eigenschaften definieren, um Wiederholungs- und Timeout-Verhalten zu steuern. Der Worker liest diese per Reflection aus und wendet sie automatisch an.

PHP
use Queuety\Contracts\Job;
use Queuety\Dispatchable;

class ImportProductsJob implements Job {
    use Dispatchable;

    public int $tries = 5;
    public int $timeout = 120;
    public array $backoff = [ 10, 30, 60, 120 ];
    public string $concurrency_group = 'imports';
    public int $concurrency_limit = 2;
    public int $cost_units = 4;

    public function __construct(
        public readonly int $supplier_id,
    ) {}

    public function handle(): void {
        // Import products...
    }
}
Eigenschaft Typ Beschreibung
$tries int Maximale Anzahl von Versuchen, bevor der Job begraben wird. Überschreibt den Wert max_attempts beim Dispatch.
$timeout int Maximale Ausführungszeit in Sekunden. Überschreibt die globale Konstante QUEUETY_MAX_EXECUTION_TIME. Erfordert pcntl und wird noch nicht erzwungen (siehe Timeout).
$backoff array Array mit Verzögerungswerten in Sekunden für jeden Wiederholungsversuch. Der letzte Wert wird für alle weiteren Versuche wiederverwendet.
$concurrency_group string Gemeinsame Worker-Zulassungsgruppe für aufwendige Jobs.
$concurrency_limit int Maximale Anzahl gleichzeitiger Jobs vom Typ processing in dieser Gruppe. Wird ohne $concurrency_group ignoriert.
$cost_units int Relative Ausführungskosten für Workflow-Budgets und die Zulassung durch Worker.

Das Array $backoff legt zunehmend längere Verzögerungen zwischen Wiederholungsversuchen fest. Zum Beispiel bedeutet [10, 30, 60, 120]: Vor dem ersten Wiederholungsversuch wird 10 Sekunden gewartet, vor dem zweiten 30 Sekunden, vor dem dritten 60 Sekunden und vor allen weiteren 120 Sekunden.

Eigenschaften zu Ressourcen sind nützlich, wenn Jobklassen unterschiedliche Kostenprofile abbilden. Eine schnelle Cache-Aktualisierung und ein umfangreicher Aufruf bei einem Anbieter können beide „ein Job“ sein, sollten aber nicht unbedingt gleich gewichtet werden, wenn ein Workflow-Budget oder eine Worker-Zulassungsentscheidung den Prozess innerhalb sicherer Grenzen halten soll.

Das vollständige Modell einschließlich Workflow-Budgets und Zulassung auf Worker-Seite findest du unter Ressourcenverwaltung.

Hook bei Fehlschlag

Wenn ein Job nach Ausschöpfen aller Wiederholungsversuche begraben wird, ruft Queuety eine Methode failed() auf der Jobinstanz auf, sofern eine definiert ist. Nutze sie für Bereinigungen, Warnmeldungen oder Protokollierung.

PHP
use Queuety\Contracts\Job;
use Queuety\Dispatchable;

class ProcessPaymentJob implements Job {
    use Dispatchable;

    public int $tries = 3;

    public function __construct(
        public readonly int $order_id,
    ) {}

    public function handle(): void {
        // Process the payment...
    }

    public function failed( \Throwable $exception ): void {
        // Notify the team that payment processing failed permanently.
        error_log( "Payment failed for order {$this->order_id}: {$exception->getMessage()}" );
    }
}

Die Methode failed() erhält die Ausnahme, die zum endgültigen Fehlschlag geführt hat. Die Jobinstanz wird mit ihrer ursprünglichen Payload deserialisiert, sodass alle öffentlichen Eigenschaften verfügbar sind.

Bedingter Dispatch

Das Trait Dispatchable stellt dispatch_if() und dispatch_unless() für bedingtes Senden bereit. Beide erwarten als erstes Argument eine boolesche Bedingung, gefolgt von den Konstruktorargumenten des Jobs.

PHP
// Only dispatch if the user has opted in to notifications.
SendEmailJob::dispatch_if( $user->wants_notifications, $user->email, 'Update', $body );

// Dispatch unless the feature flag is disabled.
SyncInventoryJob::dispatch_unless( $maintenance_mode, $sku );

dispatch_if() sendet den Job, wenn die Bedingung true ist, und gibt das PendingJob zurück. Wenn die Bedingung false ist, gibt es null zurück.

dispatch_unless() sendet den Job, wenn die Bedingung false ist, und gibt das PendingJob zurück. Wenn die Bedingung true ist, gibt es null zurück.

Beide Methoden unterstützen das Verketten von PendingJob-Optionen beim Senden:

PHP
SendEmailJob::dispatch_if( $should_send, $to, $subject, $body )
    ?->on_queue( 'emails' )
    ?->with_priority( Priority::High );

Synchrone Ausführung

Nutze Queuety::dispatch_sync(), um einen Job sofort im aktuellen Prozess auszuführen, ohne ihn zur Warteschlange hinzuzufügen. Das ist hilfreich beim Testen und für Jobs, die direkt ausgeführt werden müssen.

PHP
use Queuety\Queuety;

// With a Job instance
Queuety::dispatch_sync( new SendEmailJob( to: 'user@example.com', subject: 'Hello', body: 'Hi!' ) );

// With a handler name and payload
Queuety::dispatch_sync( 'send_email', [ 'to' => 'user@example.com' ] );

Die Methode handle() des Jobs wird direkt aufgerufen. Es wird kein Datenbankeintrag erstellt, kein Worker eingesetzt und keine Middleware angewendet.

Jobverkettung

Bei einer Jobverkettung wird eine Folge von Jobs nacheinander ausgeführt. Jeder Job in der Kette hängt davon ab, dass der vorherige erfolgreich abgeschlossen wird. Wenn ein Job fehlschlägt, bleiben die nachfolgenden Jobs in der Kette ausstehend.

Mit Queuety::chain()

PHP
use Queuety\Queuety;

$first_job_id = Queuety::chain( [
    new FetchDataJob( $url ),
    new ProcessDataJob(),
    new NotifyCompleteJob(),
] )
    ->on_queue( 'pipeline' )
    ->catch( ChainFailedHandler::class )
    ->dispatch();

Queuety::chain() gibt ein ChainBuilder mit Fluent-Methoden zum Festlegen der Warteschlange und eines Fehlerhandlers zurück. Die Methode dispatch() erstellt alle Jobs mit sequenziellen depends_on-Beziehungen und gibt die ID des ersten Jobs zurück.

Mit with_chain()

Das Trait Dispatchable stellt eine Methode with_chain() bereit. Sie entspricht derzeit Queuety::chain( $jobs ): Die Klasse, auf der sie aufgerufen wird, wird nicht zur Kette hinzugefügt (#13). Führe daher auch den ersten Job im Array auf:

PHP
FetchDataJob::with_chain( [
    new FetchDataJob(),
    new ProcessDataJob(),
    new NotifyCompleteJob(),
] )
    ->on_queue( 'pipeline' )
    ->dispatch();

Fehlerbehandlung bei Ketten

Registriere mit catch() eine Fehlerhandlerklasse. Die Handlerklasse muss über eine Methode handle() verfügen. Sie wird aufgerufen, wenn ein Job in der Kette endgültig begraben wird. Der Worker unterstützt handle(), handle( Job $job ) und handle( Job $job, \Throwable $exception ).

PHP
class ChainFailedHandler {
    public function handle(): void {
        // Alert, clean up, etc.
    }
}

Middleware hinzufügen

Jobklassen können eine Methode middleware() definieren, die Middleware zurückgibt, um die Ausführung zu umschließen:

PHP
use Queuety\Contracts\Job;
use Queuety\Dispatchable;
use Queuety\Middleware\RateLimited;
use Queuety\Middleware\Timeout;

class CallOpenAIJob implements Job {
    use Dispatchable;

    public function __construct(
        public readonly string $prompt,
    ) {}

    public function handle(): void {
        // Call the API...
    }

    public function middleware(): array {
        return [
            new RateLimited( max: 60, window: 60 ),
            new Timeout( seconds: 120 ),
        ];
    }
}

Details zu integrierter Middleware und zum Schreiben eigener Middleware findest du unter Middleware.

Vergleich mit handlerbasierten Jobs

Handlerbasiert (v0.1+) Klassenbasiert (v0.6+)
Dispatch Queuety::dispatch( 'send_email', $payload ) SendEmailJob::dispatch( $to, $subject )
Handler Separate Klasse, die Handler implementiert Dieselbe Klasse implementiert Contracts\Job
Payload Ungetyptes array $payload Typisierte öffentliche Eigenschaften
Registrierung Queuety::register() oder #[QueuetyHandler] Nicht erforderlich (verwendet FQCN)
Middleware Nicht verfügbar Optionale Methode middleware()
Konfiguration Methode config() Job-Eigenschaften + PendingJob-Kette

Beide Ansätze werden vollständig unterstützt. Bestehende handlerbasierte Jobs funktionieren unverändert weiter. Du kannst beide Stile im selben Projekt kombinieren.

Job-Lebenszyklus

Code
dispatch() -> pending -> processing -> completed
                  |           |
                  |           +-> exception -> pending (retry after backoff)
                  |                       |
                  |                       +-> buried (max attempts reached)
                  |
                  +-> delayed (available_at in the future)
Status Beschreibung
pending Wartet darauf, von einem Worker übernommen zu werden
processing Wird gerade ausgeführt
completed Erfolgreich abgeschlossen
failed Reserviert; der Worker setzt diesen Status derzeit nicht. Ein fehlgeschlagener Versuch, der wiederholt wird, erhält wieder den Status pending
buried Alle Wiederholungsversuche ausgeschöpft