
# Workery

Workery sú dlho bežiace procesy, ktoré kontrolujú databázu, či neobsahuje čakajúce úlohy, vykonávajú ich a posúvajú pracovné postupy ďalej. Príkaz `wp queuety work` sa spúšťa v WP-CLI, pričom samotný front sa získava priamo z MySQL prostredníctvom najvhodnejšieho dostupného ovládača DB Queuety.

Plugin tiež inštaluje predvolenú záložnú možnosť WordPress cron, ktorá sa spúšťa raz za minútu a je určená pre prostredia bez prístupu k shellu. Táto stránka sa zaoberá samostatnými workermi WP-CLI, ktoré sú odporúčaným režimom nasadenia na dosiahnutie vyššej priepustnosti.

## Spustenie workera

```bash
wp queuety work
```

Týmto sa spustí jeden worker vo fronte `default`. Beží v slučke: získa úlohu, vykoná ju a opakuje postup. Keď je front prázdny, pred ďalšou kontrolou čaká `QUEUETY_WORKER_SLEEP` sekundu (predvolene: 1).

### Možnosti

| Možnosť | Opis |
|---|---|
| `--queue=<queue>` | Fronty Queue(s) na spracovanie, oddelené čiarkami v poradí priorít (predvolene: `default`) |
| `--once` | Spracuje najviac jednu úlohu a skončí |
| `--workers=<n>` | Rozvetví procesy N workerov (vyžaduje rozšírenia `pcntl` a `posix`) |
| `--min-workers=<n>` | Minimálny počet workerov v adaptívnom poole. Vyžaduje `--max-workers` |
| `--max-workers=<n>` | Maximálny počet workerov v adaptívnom poole |

```bash
# Process a specific queue
wp queuety work --queue=emails

# Process at most one job and exit
wp queuety work --once

# Run 4 concurrent workers
wp queuety work --workers=4

# Scale between 2 and 6 workers as backlog changes
wp queuety work --queue=providers --min-workers=2 --max-workers=6
```

## Priority viacerých frontov

Jeden worker môže spracúvať viacero frontov v poradí podľa priority. Príkazu `--queue` odovzdajte zoznam názvov frontov oddelených čiarkami:

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

Worker sa pokúsi získať úlohu z každého frontu v uvedenom poradí. Najprv skontroluje `critical`. Ak `critical` obsahuje úlohu, spracuje ju. Ak je `critical` prázdny, prejde na `default` a potom na `low`. To znamená, že fronty s vyššou prioritou sa vždy vyprázdnia skôr než fronty s nižšou prioritou.

### Ako funguje poradie priorít

Pri každom opakovaní sa worker pokúsi získať úlohu z prvého frontu v zozname. Ak je front prázdny alebo pozastavený, prejde na ďalší a pokračuje, kým nenájde úlohu alebo neprejde celý zoznam. Ak nikde úlohu nenájde, počká a skúsi to znova. Ide o striktný model priorít, čo znamená, že nepretržitý prúd úloh `critical` spôsobí, že fronty `default` a `low` budú hladovať. Ak fronty s nižšou prioritou potrebujú zaručenú priepustnosť, použite samostatné workery.

### Používanie viacerých frontov v PHP

Metódy `Worker::run()` a `Worker::flush()` prijímajú reťazec alebo pole:

```php
// Comma-separated string
Queuety::worker()->run( 'critical,default,low' );

// Array
Queuety::worker()->run( [ 'critical', 'default', 'low' ] );

// flush() also supports multi-queue
$count = Queuety::worker()->flush( 'critical,default,low' );
```

### Stratégie nasadenia

**Samostatné workery pre každý front** poskytujú úplnú kontrolu nad súbežnosťou a izoláciou. Každý front má vlastnú worker process(es) a ostatné fronty ho nemôžu odstaviť od spracovania:

```ini
[program:queuety-critical]
command=wp queuety work --queue=critical
numprocs=4

[program:queuety-default]
command=wp queuety work --queue=default
numprocs=2

[program:queuety-low]
command=wp queuety work --queue=low
numprocs=1
```

**Poradie priorít s jedným workerom** sa spravuje jednoduchšie a funguje dobre, keď fronty s nižšou prioritou znesú oneskorenie:

```ini
[program:queuety-worker]
command=wp queuety work --queue=critical,default,low
numprocs=2
```

Môžete tiež kombinovať oba prístupy: samostatné workery pre `critical` na zaručenie priepustnosti a worker s poradím priorít pre všetko ostatné:

```ini
[program:queuety-critical]
command=wp queuety work --queue=critical
numprocs=2

[program:queuety-fallback]
command=wp queuety work --queue=default,low
numprocs=2
```

## Prijímanie úloh s ohľadom na zdroje

Workery robia viac než len získavajú ďalší čakajúci riadok. Pred spustením získanej úlohy môže Queuety uplatniť hrubé kontroly prijatia na základe skupiny súbežnosti úlohy a nedávneho profilu vykonávania daného obslužného programu.

Podrobnejší opis modelu vrátane toho, ako súvisí s metadátami úloh a rozpočtami pracovných postupov, nájdete v časti [Správa zdrojov](https://queuety.inline0.dev/sk/docs/features/resource-management).

Skupina súbežnosti určuje spoločný limit pre viaceré obslužné programy alebo triedy úloh. Ak viacerým náročným poskytovateľom priradíte rovnakú skupinu a limit, worker po dosiahnutí limitu uvoľní novozískané úlohy namiesto toho, aby ich spustil. Nezávislé fronty tak nezaťažia jeden nadväzujúci systém len preto, že je k dispozícii viacero nečinných workerov.

Nedávne záznamy vykonávania slúžia aj na vytvorenie jednoduchého profilu prijímania. Keď je táto funkcia zapnutá, Queuety skontroluje nedávne priemerné trvanie a špičkovú spotrebu pamäte obslužného programu, ktorý sa chystá spustiť. Ak je aktuálny worker už blízko nastaveného limitu pamäte, front alebo skupina poskytovateľov už dosiahla svoj vážený rozpočet, v aktuálnom kontajneri zrejme nezostáva dosť pamäte pre pozorovaný profil obslužného programu alebo sa spustenie v štýle `--once` blíži k časovému limitu, Queuety úlohu odloží namiesto spustenia práce, ktorá sa pravdepodobne nedokončí bez problémov.

Politiku zapnete v definícii práce, nie v príkaze workera:

```php
class CallProviderStep implements \Queuety\Step {
    public function handle( array $state ): array {
        // ...
    }

    public function config(): array {
        return [
            'concurrency_group' => 'providers',
            'concurrency_limit' => 3,
            'cost_units' => 5,
        ];
    }
}
```

Rovnaké metadáta možno pridať do tried úloh, ktoré možno odoslať, prostredníctvom verejných vlastností alebo k jednotlivému odoslaniu cez `PendingJob::concurrency_group()` a `PendingJob::cost_units()`.

Profil prijímania na strane workera sa nastavuje pomocou týchto konštánt:

| Konštanta | Opis |
|---|---|
| `QUEUETY_RESOURCE_ADMISSION` | Zapnutie alebo vypnutie kontrol prijímania založených na profiloch |
| `QUEUETY_RESOURCE_PROFILE_WINDOW_MINUTES` | Obdobie, za ktoré sa zhromažďujú nedávne profily obslužných programov |
| `QUEUETY_RESOURCE_PROFILE_TTL` | TTL vyrovnávacej pamäte pre nedávne profily obslužných programov |
| `QUEUETY_RESOURCE_MEMORY_HEADROOM_MB` | Rezerva pamäte pred prijatím ďalšej úlohy |
| `QUEUETY_RESOURCE_SYSTEM_MEMORY_AWARENESS` | Zapnutie alebo vypnutie približných kontrol pamäte kontajnera alebo hostiteľa |
| `QUEUETY_RESOURCE_SYSTEM_MEMORY_HEADROOM_MB` | Rezerva systémovej pamäte pred prijatím ďalšej úlohy |
| `QUEUETY_RESOURCE_QUEUE_COST_BUDGETS` | Rozpočty vážených nákladov podľa názvu frontu |
| `QUEUETY_RESOURCE_GROUP_COST_BUDGETS` | Rozpočty vážených nákladov podľa `concurrency_group` |
| `QUEUETY_RESOURCE_TIME_HEADROOM_MS` | Časová rezerva pre jednorazové spustenia workerov |

## Životný cyklus workera

Worker sa po dokončení aktuálnej úlohy korektne zastaví, keď nastane niektorá z týchto podmienok:

| Podmienka | Nastavenie |
|---|---|
| Maximálny počet spracovaných úloh | `QUEUETY_WORKER_MAX_JOBS` (predvolene: 1000) |
| Prekročenie maximálnej spotreby pamäte | `QUEUETY_WORKER_MAX_MEMORY` (predvolene: 128 MB) |

Po zastavení worker korektne skončí. V produkčnom prostredí ho správca procesov automaticky znova spustí.

Spracovanie signálov zatiaľ nie je korektne podporované ([#4](https://github.com/inline0/queuety/issues/4)). Samostatný proces `wp queuety work` nenainštaluje obslužný program signálov, takže `SIGTERM` alebo `SIGINT` (Ctrl+C) ho okamžite ukončí, aj uprostred úlohy. Táto úloha zostane v stave `processing`, kým jej obnovenie po zastaranej úlohe tento stav po `QUEUETY_STALE_TIMEOUT` sekundách nevynuluje, preto musia byť obslužné programy bezpečné na opakovanie. Worker pool posiela signál `SIGTERM` svojim podriadeným procesom, tie naň však nereagujú. Pool preto beží ďalej, kým sa nezastavia z niektorého z vyššie uvedených dôvodov. Ak potrebujete zastaviť pool, nastavte správcovi procesov časový limit zastavenia, po ktorom odošle signál `SIGKILL`.

## Súbežnosť viacerých workerov

Príznak `--workers=N` rozvetví N podriadených procesov pomocou `pcntl_fork()`. Každý podriadený proces si vytvorí vlastné databázové pripojenie a nezávisle spracúva úlohy. Rodičovský proces sleduje podriadené procesy a každý, ktorý zlyhá, znova spustí.

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

Požiadavky:
Musia byť nainštalované rozšírenia `pcntl` a `posix` PHP (bez `pcntl` príkaz zlyhá s hlásením „pcntl extension is required for --workers=N.“). Každý podriadený proces používa vlastné databázové pripojenie, preto musí byť vaše nastavenie MySQL `max_connections` dostatočné pre celý pool. Rovnaké požiadavky platia aj pre adaptívne pooly (`--min-workers`/`--max-workers`).

## Adaptívne pooly workerov

Adaptívne pooly udržiavajú minimálny počet pripravených workerov a potom sa rozrastajú alebo zmenšujú v nastavenom rozsahu:

```bash
wp queuety work --queue=providers --min-workers=2 --max-workers=6
```

Rodičovský proces sleduje nevybavené úlohy, ktoré možno získať, vo vybraných frontoch a upravuje počet podriadených procesov podľa ich množstva, najviac však na nastavený limit. Keď sa vo frontoch upokojí prevádzka, pool pred zmenšením počká počas nastavenej doby nečinnosti, aby krátke nárazové zaťaženia nespôsobovali neustále zmeny zoznamu procesov.

Zvyšovanie počtu workerov je stále opatrné. Keď Queuety dokáže čítať údaje o pamäti kontajnera alebo hostiteľa, nepridá ďalšie podriadené procesy, ak zostávajúca pamäť nestačí na plný pamäťový limit ďalšieho workera.

Slučka úprav na strane rodičovského procesu sa nastavuje pomocou:

| Konštanta | Opis |
|---|---|
| `QUEUETY_WORKER_POOL_SCALE_INTERVAL` | Ako často rodičovský proces zosúlaďuje veľkosť poolu |
| `QUEUETY_WORKER_POOL_IDLE_GRACE` | Ako dlho musia byť fronty bez náporu, kým sa pool zmenší |

## Obnovenie po zastaranej úlohe

Ak worker počas spracúvania úlohy zlyhá, úloha zostane v stave `processing`. Dlho bežiaci worker približne raz za minútu kontroluje úlohy, ktoré sú v stave `processing` dlhšie než `QUEUETY_STALE_TIMEOUT` sekúnd. Zastaraná úloha, ktorej zostávajú pokusy, sa po uplynutí čakacej doby pred ďalším pokusom zopakuje; úloha, ktorá vyčerpala všetky pokusy, sa odloží a jej pracovný postup sa označí ako neúspešný. Túto kontrolu nevykonávajú workery `--once` ani záložný proces WP-Cron.

Zastarane úlohy obnovte ručne:

```bash
wp queuety recover
```

## Režim vyprázdnenia frontu

Spracuje všetky čakajúce úlohy a skončí. Hodí sa na testovanie alebo jednorazové dávkové spracovanie:

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

## Nasadenie do produkcie

V produkčnom prostredí použite správcu procesov, aby workery zostali spustené. Tu sú dva bežné prístupy.

### Supervisord

```ini
[program:queuety-default]
command=wp queuety work --queue=default
directory=/var/www/html
autostart=true
autorestart=true
numprocs=2
process_name=%(program_name)s_%(process_num)02d
stdout_logfile=/var/log/queuety-default.log
stderr_logfile=/var/log/queuety-default-error.log

[program:queuety-emails]
command=wp queuety work --queue=emails
directory=/var/www/html
autostart=true
autorestart=true
numprocs=1
stdout_logfile=/var/log/queuety-emails.log
stderr_logfile=/var/log/queuety-emails-error.log
```

### Systemd

```ini
[Unit]
Description=Queuety Worker (default queue)
After=mysql.service

[Service]
Type=simple
User=www-data
WorkingDirectory=/var/www/html
ExecStart=/usr/local/bin/wp queuety work --queue=default
Restart=always
RestartSec=5

[Install]
WantedBy=multi-user.target
```

Zapnite a spustite:

```bash
sudo systemctl enable queuety-worker
sudo systemctl start queuety-worker
```

### Tipy na nasadenie

Ak potrebujete lepšiu izoláciu, spúšťajte samostatné workery pre jednotlivé fronty. Pri práci náročnej na procesor použite `--workers=N` a pri práci, ktorá prevažne čaká na vstupno-výstupné operácie, uprednostnite samostatne spravované workery. Ak chcete pravidelným reštartovaním uvoľňovať pamäť, nastavte `QUEUETY_WORKER_MAX_JOBS` a sledujte počet `buried` cez [webhooky](https://queuety.inline0.dev/sk/docs/features/webhooks) alebo [metriky](https://queuety.inline0.dev/sk/docs/features/metrics).
