
# Uzdevumi

Uzdevumi ir Queuety darba pamatvienība. Nosūtiet uzdevumu, norādot apdarinātāja nosaukumu un datus; darba process to paņems un izpildīs fonā.

## Uzdevumu nosūtīšana

```php
use Queuety\Queuety;

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

`dispatch()` atgriež `PendingJob` veidotāju. Varat savienot opcijas, pirms tas automātiski nosūta uzdevumu:

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

Lai uzreiz iegūtu uzdevuma ID, izsauciet `->id()`:

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

## Apdarinātāju rakstīšana

Apdarinātājs ir jebkura klase, kas implementē saskarni `Queuety\Handler`:

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

Metode `handle()` saņem uzdevuma datus un veic darbu. Metode `config()` atgriež neobligātās noklusējuma vērtības:

| Atslēga | Tips | Apraksts |
|---|---|---|
| `queue` | `string` | Noklusējuma rindas nosaukums |
| `max_attempts` | `int` | Maksimālais izpildes reižu skaits, ieskaitot pirmo reizi, pirms uzdevums tiek pārvietots uz neizpildīto uzdevumu krātuvi |
| `backoff` | `string` | Atkārtoto mēģinājumu atkāpšanās stratēģija: `exponential`, `linear` vai `fixed` |
| `rate_limit` | `array` | Ātruma ierobežojums kā `[max_executions, window_seconds]` |
| `concurrency_group` | `string` | Koplietojama darba procesu piekļuves grupa dārgiem apdarinātājiem |
| `concurrency_limit` | `int` | Maksimālais vienlaicīgi apstrādājamo uzdevumu skaits grupā |
| `cost_units` | `int` | Relatīvās izpildes izmaksas, ko izmanto svērtām rindu vai pakalpojumu sniedzēju budžetēšanas, darbplūsmu budžetēšanas un piekļuves pārvaldības heuristikām |

Šie resursu taustiņi ir daļa no plašākā [Resursu pārvaldības](https://queuety.inline0.dev/lv/docs/features/resource-management) modeļa, kurā kopā darbojas uzdevumu metadati, darbplūsmu budžeti un darba procesu piekļuves kontrole Queuety.

Reģistrējiet apdarinātāju:

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

Vai izmantojiet [PHP atribūtus](https://queuety.inline0.dev/lv/docs/features/attributes) automātiskai reģistrēšanai.

## Prioritāte

Uzdevumiem ir četri prioritātes līmeņi. Darba procesi vispirms paņem augstākas prioritātes uzdevumus.

```php
use Queuety\Enums\Priority;

Queuety::dispatch( 'handler', $payload )->with_priority( Priority::Urgent );
```

| Līmenis | Vērtība | Apraksts |
|---|---|---|
| `Priority::Low` | `0` | Noklusējuma līmenis. Fona darbs, kas var pagaidīt. |
| `Priority::Normal` | `1` | Standarta prioritāte. |
| `Priority::High` | `2` | Tiek apstrādāts pirms līmeņiem Low un Normal. |
| `Priority::Urgent` | `3` | Tiek apstrādāts pirmais. Lietojiet taupīgi. |

## Aizkave

Aizkavējiet uzdevumu, lai tas kļūtu pieejams tikai pēc norādītā sekunžu skaita:

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

Uzdevums tiek ievietots uzreiz, taču darba procesi to nepaņems, līdz būs pagājis aizkaves laiks.

## Unikāli uzdevumi

Novērsiet viena apdarinātāja un datu dublētu nosūtīšanu. Ja jau pastāv gaidošs vai apstrādājams uzdevums ar tādu pašu apdarinātāju un datiem, jauna uzdevuma vietā tiek atgriezts esošā uzdevuma ID.

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

## Uzdevumu atkarības

Uzdevums var būt atkarīgs no cita uzdevuma. Darba process to nepaņems, līdz būs pabeigts atkarīgais uzdevums.

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

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

## Uzdevumu nosūtīšana partijā

Nosūtiet vairākus uzdevumus vienā vairāku rindu INSERT, lai sasniegtu maksimālu caurlaidspēju:

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

Katram elementam var norādīt neobligātās atslēgas: `queue`, `priority`, `delay`, `max_attempts`.

## Atkārtoti mēģinājumi un aizkaves palielināšana

Ja uzdevums rada izņēmumu, Queuety to ievieto atpakaļ rindā `pending` ar aizkavētu `available_at`, līdz tas kopumā būs izpildīts `max_attempts` reizes (skaitot pirmo izpildes reizi; tātad `max_attempts( 3 )` nozīmē, ka būs līdz diviem atkārtotiem mēģinājumiem). Aizkavi pirms katra atkārtotā mēģinājuma nosaka atkāpšanās stratēģija, kur `attempt` ir tikko neveiksmīgās izpildes kārtas numurs:

| Stratēģija | Aizkaves formula | Piemērs (pēc 1., 2., 3. izpildes) |
|---|---|---|
| `exponential` | `2^attempt * 30` sekundes, ne vairāk par 3600 | 60 s, 120 s, 240 s |
| `linear` | `attempt * 60` sekundes | 60 s, 120 s, 180 s |
| `fixed` | `60` sekundes | 60 s, 60 s, 60 s |

Iestatiet globālo noklusējuma vērtību šeit: `wp-config.php`:

```php
define( 'QUEUETY_RETRY_BACKOFF', 'linear' );
```

Vai katram apstrādātājam `config()`:

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

Vai katrai izsūtīšanai:

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

## Apbedītie darbi

Kad visi atkārtotie mēģinājumi ir izsmelti, uzdevumam tiek piešķirts statuss **buried**. Šādi uzdevumi paliek datubāzē pārbaudei, bet netiek apstrādāti. Tos var mēģināt izpildīt vēlreiz manuāli. Manuāls atkārtots mēģinājums atjauno uzdevuma statusu uz `pending`, neatiestatot mēģinājumu skaitu, tāpēc šādam uzdevumam būs vēl viena izpilde, un kļūmes gadījumā tas atkal tiks apglabāts:

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

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

Vai ar CLI:

```bash
wp queuety retry 42
wp queuety retry-buried
```

## Noildze

Queuety iestata `pcntl_alarm()` uz maksimālo izpildes laiku, un, to pārsniedzot, mēģinājums ir jāatzīmē kā neveiksmīgs ar `TimeoutException`. Pašlaik šis taimauts netiek piemērots ([#10](https://github.com/inline0/queuety/issues/10)): darbinieks neiespējo asinhronos signālus, tāpēc ilgstoši izpildāms apstrādātājs darbojas līdz galam, savukārt apstrādātāja darbība, kas gaida `sleep()`, tiek pārtraukta, kad nostrādā signāls, un pēc tam atzīmēta kā pabeigta. Konfigurējiet taimautu globāli:

```php
define( 'QUEUETY_MAX_EXECUTION_TIME', 300 ); // 5 minutes
```

Uzdevumi, kas statusā `processing` iestrēguši ilgāk par `QUEUETY_STALE_TIMEOUT` sekundēm, tiek automātiski atjaunoti:

```php
define( 'QUEUETY_STALE_TIMEOUT', 600 ); // 10 minutes
```

Novecojušos darbus var atkopt manuāli:

```bash
wp queuety recover
```

## Rindas

Uzdevumi tiek nosūtīti uz nosauktām rindām. Katrs darbinieks apstrādā vienu rindu vai sakārtotu rindu sarakstu (skatiet [Darbinieki](https://queuety.inline0.dev/lv/docs/features/workers#multi-queue-priorities)):

```php
Queuety::dispatch( 'handler', $payload )->on_queue( 'emails' );
```

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

Noklusējuma rinda ir `default`. Varat palaist atsevišķus darbinieku procesus katrai rindai, lai nodalītu darba slodzes.

## Ātruma ierobežošana

Ierobežojiet, cik bieži apstrādātājs var darboties noteiktā laika periodā:

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

Plašāku informāciju skatiet sadaļā [Ātruma ierobežošana](https://queuety.inline0.dev/lv/docs/features/rate-limiting).

## Nosūtāmas uzdevumu klases

Queuety v0.6.0 ievieš uz klasēm balstītus uzdevumus, izmantojot API. Tā vietā, lai atsevišķi norādītu apstrādātāja nosaukumu, datus un apstrādātāja klasi, definējiet vienu klasi, kas ietver visu: datus (kā tipizētas publiskās īpašības), izpildes loģiku un neobligāto starpprogrammatūru.

### Interfeiss `Contracts\Job`

```php
use Queuety\Contracts\Job;

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

`Job` klase glabā datus publiskās īpašībās un izpilda savu loģiku metodē `handle()`. Atsevišķa metode `config()` vai apstrādātāja reģistrācija nav nepieciešama.

### `Dispatchable` traits

Pievienojiet `Dispatchable` trait, lai uzdevuma klasei būtu statiska metode `dispatch()`:

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

Nosūtiet uzdevumu, padodot konstruktora argumentus:

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

`Dispatchable` traits iekšēji izsauc `Queuety::dispatch()`, tāpēc visas `PendingJob` opcijas (rinda, prioritāte, aizkave, max_attempts, unique, after) darbojas tieši tāpat.

### Kā darbojas serializācija

Nosūtot `Job` instanci, Queuety `JobSerializer` iegūst pilno klases nosaukumu, ko izmanto kā apstrādātāju, un visas publiskās īpašības, ko izmanto kā datus. Darbinieka pusē serializētājs atjauno instanci, sasaistot datu atslēgas ar konstruktora parametru nosaukumiem. Enum vērtības ar pamatā esošu vērtību tiek serializētas uz šo vērtību un automātiski atjaunotas.

### Uzdevumu rekvizīti

Uzdevumu klases var deklarēt publiskās īpašības, lai noteiktu atkārtotu mēģinājumu un taimauta darbību. Darbinieks tās nolasa, izmantojot refleksiju, un piemēro automātiski.

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

| Īpašība | Tips | Apraksts |
|---|---|---|
| `$tries` | `int` | Maksimālais mēģinājumu skaits, pirms uzdevums tiek apglabāts. Aizstāj nosūtīšanas brīdī norādīto vērtību `max_attempts`. |
| `$timeout` | `int` | Maksimālais izpildes laiks sekundēs. Aizstāj globālo konstanti `QUEUETY_MAX_EXECUTION_TIME`. Nepieciešams `pcntl`, un pašlaik netiek piemērots (skatiet [Taimauts](https://queuety.inline0.dev/lv/docs/jobs#timeout)). |
| `$backoff` | `array` | Aizkaves vērtību masīvs sekundēs katram atkārtotajam mēģinājumam. Pēdējā vērtība tiek izmantota arī turpmākajiem mēģinājumiem. |
| `$concurrency_group` | `string` | Koplietojama darbinieku piekļuves grupa dārgiem uzdevumiem. |
| `$concurrency_limit` | `int` | Maksimālais vienlaicīgo `processing` uzdevumu skaits šajā grupā. Bez `$concurrency_group` tiek ignorēts. |
| `$cost_units` | `int` | Relatīvās izpildes izmaksas, ko izmanto darbplūsmu budžeti un darbinieku piekļuves kontrole. |

Masīvs `$backoff` nosaka pieaugošas aizkaves starp atkārtotiem mēģinājumiem. Piemēram, `[10, 30, 60, 120]` nozīmē, ka pirms pirmā atkārtotā mēģinājuma tiek gaidītas 10 sekundes, pirms otrā — 30 sekundes, pirms trešā — 60 sekundes, bet pirms katra nākamā mēģinājuma — 120 sekundes.

Ar resursiem saistītās īpašības noder, ja uzdevumu klases atbilst dažādiem izmaksu profiliem. Ātra kešatmiņas atjaunošana un liels izsaukums pakalpojumu sniedzējam abi var būt "viens uzdevums", taču tos nevajadzētu obligāti skaitīt vienādi, kad darbplūsmas budžets vai darbinieku piekļuves kontroles lēmums cenšas noturēt procesu drošās robežās.

Pilnu modeli, tostarp darbplūsmu budžetus un darbinieku puses piekļuves kontroli, skatiet sadaļā [Resursu pārvaldība](https://queuety.inline0.dev/lv/docs/features/resource-management).

### Kļūmes āķis

Kad pēc visu atkārtoto mēģinājumu izsmelšanas uzdevums tiek apglabāts, Queuety izsauc uzdevuma instances metodi `failed()`, ja tā ir definēta. Izmantojiet to uzkopšanai, brīdinājumu nosūtīšanai vai reģistrēšanai.

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

Metode `failed()` saņem izņēmumu, kas izraisīja pēdējo kļūmi. Uzdevuma instance tiek deserializēta ar tās sākotnējiem datiem, tāpēc ir pieejamas visas publiskās īpašības.

### Nosacījuma izpilde

`Dispatchable` traits nodrošina `dispatch_if()` un `dispatch_unless()` nosacījuma izpildei. Abas metodes pieņem Būla tipa nosacījumu kā pirmo argumentu, kam seko uzdevuma konstruktora argumenti.

```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()` nosūta uzdevumu, ja nosacījums ir `true`, un atgriež `PendingJob`. Ja nosacījums ir `false`, tiek atgriezts `null`.

`dispatch_unless()` nosūta uzdevumu, ja nosacījums ir `false`, un atgriež `PendingJob`. Ja nosacījums ir `true`, tiek atgriezts `null`.

Abas metodes, nosūtot uzdevumu, atbalsta `PendingJob` opciju ķēdēšanu:

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

### Sinhronā izpilde

Izmantojiet `Queuety::dispatch_sync()`, lai nekavējoties izpildītu uzdevumu pašreizējā procesā, nepievienojot to rindai. Tas noder testēšanai un uzdevumiem, kas jāizpilda uzreiz.

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

Uzdevuma metode `handle()` tiek izsaukta tieši. Netiek izveidota datubāzes rinda, netiek iesaistīts darbplūsmu izpildītājs un starpprogrammatūra netiek lietota.

### Uzdevumu ķēdēšana

Uzdevumu ķēdēšana izpilda uzdevumu virkni citu pēc cita. Katra uzdevuma izpilde ir atkarīga no iepriekšējā uzdevuma sekmīgas pabeigšanas. Ja kāds uzdevums neizdodas, turpmākie ķēdes uzdevumi paliek gaidīšanas statusā.

#### Izmantojot `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()` atgriež `ChainBuilder` ar metodēm rindas un kļūmes apstrādātāja iestatīšanai. Metode `dispatch()` izveido visus uzdevumus ar secīgām `depends_on` attiecībām un atgriež pirmā uzdevuma ID.

#### Izmantojot `with_chain()`

`Dispatchable` traits nodrošina metodi `with_chain()`. Pašlaik tā ir līdzvērtīga `Queuety::chain( $jobs )`: klase, kurai metode tiek izsaukta, ķēdei netiek pievienota ([#13](https://github.com/inline0/queuety/issues/13)), tāpēc masīvā norādiet arī pirmo uzdevumu:

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

#### Ķēdes kļūmju apstrāde

Reģistrējiet kļūmju apstrādātāja klasi, izmantojot `catch()`. Apstrādātāja klasei jābūt metodei `handle()`. Tā tiek izsaukta, kad jebkurš ķēdes uzdevums tiek galīgi apglabāts. Darbinieks atbalsta `handle()`, `handle( Job $job )` un `handle( Job $job, \Throwable $exception )`.

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

### Starpprogrammatūras pievienošana

Uzdevumu klases var definēt metodi `middleware()`, kas atgriež izpildi aptverošu starpprogrammatūru:

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

Plašāku informāciju par iebūvēto starpprogrammatūru un pielāgotas starpprogrammatūras izveidi skatiet sadaļā [Starpprogrammatūra](https://queuety.inline0.dev/lv/docs/features/middleware).

### Salīdzinājums ar apstrādātājos balstītiem uzdevumiem

| | Apstrādātājos balstīti (v0.1+) | Uz klasēm balstīti (v0.6+) |
|---|---|---|
| Nosūtīšana | `Queuety::dispatch( 'send_email', $payload )` | `SendEmailJob::dispatch( $to, $subject )` |
| Apstrādātājs | Atsevišķa klase, kas implementē `Handler` | Tā pati klase implementē `Contracts\Job` |
| Dati | Netipizēts `array $payload` | Tipizētas publiskās īpašības |
| Reģistrācija | `Queuety::register()` vai `#[QueuetyHandler]` | Nav nepieciešama (tiek izmantots FQCN) |
| Starpprogrammatūra | Nav pieejama | Neobligāta metode `middleware()` |
| Konfigurācija | Metode `config()` | Uzdevuma īpašības + `PendingJob` ķēde |

Abas pieejas ir pilnībā atbalstītas. Esošie apstrādātājos balstītie uzdevumi turpina darboties bez izmaiņām. Vienā projektā varat izmantot abus stilus.

## Uzdevuma dzīves cikls

```
dispatch() -> pending -> processing -> completed
                  |           |
                  |           +-> exception -> pending (retry after backoff)
                  |                       |
                  |                       +-> buried (max attempts reached)
                  |
                  +-> delayed (available_at in the future)
```

| Statuss | Apraksts |
|---|---|
| `pending` | Gaida, kad darbinieks to pārņems |
| `processing` | Pašlaik tiek izpildīts |
| `completed` | Sekmīgi pabeigts |
| `failed` | Rezervēts; darbinieks pašlaik šo statusu neiestata. Neveiksmīgs mēģinājums, kuram sekos atkārtots mēģinājums, atgriežas statusā `pending` |
| `buried` | Izsmelti visi atkārtotie mēģinājumi |
