
# Lucrători

Lucrătorii sunt procese de lungă durată care interoghează baza de date pentru joburi în așteptare, le execută și avansează fluxurile de lucru. Comanda `wp queuety work` rulează în WP-CLI, în timp ce coada este revendicată direct din MySQL prin cel mai potrivit driver DB Queuety disponibil.

Pluginul instalează și o alternativă implicită WordPress cron, care rulează o dată pe minut, pentru mediile fără acces la shell. Această pagină descrie lucrătorii dedicați WP-CLI, modul de implementare recomandat pentru un volum mai mare de procesare.

## Pornirea unui lucrător

```bash
wp queuety work
```

Aceasta pornește un singur lucrător pe coada `default`. Rulează într-o buclă: revendică un job, îl execută și repetă. Când coada este goală, așteaptă `QUEUETY_WORKER_SLEEP` secunde (implicit: 1) înainte de a o interoga din nou.

### Opțiuni

| Opțiune | Descriere |
|---|---|
| `--queue=<queue>` | Queue(s) de procesat, separate prin virgulă pentru ordonarea priorităților (implicit: `default`) |
| `--once` | Procesează cel mult un job și iese |
| `--workers=<n>` | Pornește N procese lucrătoare (necesită extensiile `pcntl` și `posix`) |
| `--min-workers=<n>` | Numărul minim de lucrători pentru un grup adaptiv. Necesită `--max-workers` |
| `--max-workers=<n>` | Numărul maxim de lucrători pentru un grup adaptiv |

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

## Priorități pentru mai multe cozi

Un singur lucrător poate procesa mai multe cozi în ordinea priorităților. Transmiteți o listă de nume de cozi separate prin virgulă către `--queue`:

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

Lucrătorul încearcă să revendice câte un job din fiecare coadă, în ordinea listată. Verifică mai întâi `critical`. Dacă `critical` are un job, îl procesează. Dacă `critical` este goală, trece la `default`, apoi la `low`. Astfel, cozile cu prioritate mai mare se golesc întotdeauna înaintea celor cu prioritate mai mică.

### Cum funcționează ordonarea priorităților

La fiecare iterație, lucrătorul încearcă să revendice un job din prima coadă din listă. Dacă acea coadă este goală sau suspendată, trece la următoarea și continuă până când găsește un job sau epuizează lista. Dacă nu găsește niciun job, așteaptă și încearcă din nou. Acesta este un model strict de priorități, ceea ce înseamnă că un flux constant de joburi `critical` va împiedica procesarea joburilor din cozile `default` și `low`. Dacă cozile cu prioritate mai mică au nevoie de un volum de procesare garantat, folosiți lucrători dedicați.

### Utilizarea mai multor cozi în PHP

Metodele `Worker::run()` și `Worker::flush()` acceptă fie un șir, fie un tablou:

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

### Strategii de implementare

**Lucrătorii dedicați fiecărei cozi** vă oferă control deplin asupra concurenței și izolării. Fiecare coadă primește propriul process(es) de lucrători și nu poate fi împiedicată de celelalte cozi:

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

**Ordonarea priorităților cu un singur lucrător** este mai simplu de gestionat și funcționează bine când cozile cu prioritate mai mică pot tolera întârzieri:

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

Puteți combina și cele două abordări: lucrători dedicați pentru `critical`, care garantează volumul de procesare, și un lucrător cu cozi ordonate după prioritate pentru toate celelalte:

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

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

## Admiterea în funcție de resurse

Lucrătorii fac mai mult decât să revendice următorul rând în așteptare. Înainte de pornirea unui job revendicat, Queuety poate aplica verificări generale de admitere pe baza grupului de concurență al jobului și a profilului recent de execuție pentru acel handler.

Pentru modelul extins, inclusiv modul în care se leagă de metadatele joburilor și bugetele fluxurilor de lucru, consultați [Gestionarea resurselor](https://queuety.inline0.dev/ro/docs/features/resource-management).

Un grup de concurență stabilește o limită comună pentru mai mulți handleri sau clase de joburi. Dacă atribuiți același grup și aceeași limită mai multor furnizori costisitori, un lucrător va elibera lucrările revendicate recent în loc să le pornească atunci când grupul este saturat. Astfel, cozile independente nu vor suprasolicita același sistem din aval doar pentru că există mai mulți lucrători inactivi.

Jurnalele recente de execuție contribuie și ele la un profil simplu de admitere. Când admiterea în funcție de resurse este activată, Queuety analizează durata medie recentă și memoria maximă pentru handlerul pe care urmează să-l execute. Dacă lucrătorul curent este deja aproape de limita de memorie configurată, coada sau grupul de furnizori a atins deja bugetul ponderat, containerul curent nu pare să aibă suficientă memorie disponibilă pentru profilul observat al handlerului sau o execuție de tip `--once` se apropie de limita de timp, Queuety amână jobul în loc să pornească o lucrare care probabil nu se va încheia fără probleme.

Activați această politică în definiția lucrării, nu în comanda lucrătorului:

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

Aceleași metadate pot fi atașate claselor de joburi care pot fi distribuite, prin proprietăți publice, sau unei distribuiri individuale prin `PendingJob::concurrency_group()` și `PendingJob::cost_units()`.

Profilul de admitere de pe partea lucrătorului este controlat de aceste constante:

| Constantă | Descriere |
|---|---|
| `QUEUETY_RESOURCE_ADMISSION` | Activează sau dezactivează verificările de admitere bazate pe profil |
| `QUEUETY_RESOURCE_PROFILE_WINDOW_MINUTES` | Intervalul retrospectiv folosit la construirea profilurilor recente ale handlerelor |
| `QUEUETY_RESOURCE_PROFILE_TTL` | Durata de păstrare în cache a profilurilor recente ale handlerelor |
| `QUEUETY_RESOURCE_MEMORY_HEADROOM_MB` | Rezerva de memorie necesară înainte de admiterea unui alt job |
| `QUEUETY_RESOURCE_SYSTEM_MEMORY_AWARENESS` | Activează sau dezactivează verificările aproximative ale memoriei containerului sau gazdei |
| `QUEUETY_RESOURCE_SYSTEM_MEMORY_HEADROOM_MB` | Rezerva de memorie a sistemului necesară înainte de admiterea unui alt job |
| `QUEUETY_RESOURCE_QUEUE_COST_BUDGETS` | Bugete de cost ponderat, grupate după numele cozii |
| `QUEUETY_RESOURCE_GROUP_COST_BUDGETS` | Bugete de cost ponderat, grupate după `concurrency_group` |
| `QUEUETY_RESOURCE_TIME_HEADROOM_MS` | Rezerva de timp pentru execuțiile lucrătorilor care rulează o singură dată |

## Ciclul de viață al lucrătorului

Un lucrător se oprește în mod controlat, după ce își termină jobul curent, dacă este îndeplinită oricare dintre aceste condiții:

| Condiție | Configurare |
|---|---|
| Numărul maxim de joburi procesate | `QUEUETY_WORKER_MAX_JOBS` (implicit: 1000) |
| Memorie maximă depășită | `QUEUETY_WORKER_MAX_MEMORY` (implicit: 128 MB) |

După oprire, lucrătorul iese fără erori. În producție, un manager de procese îl repornește automat.

Semnalele nu sunt încă gestionate în mod controlat ([#4](https://github.com/inline0/queuety/issues/4)). Un singur proces `wp queuety work` nu instalează niciun handler de semnale, așa că `SIGTERM` sau `SIGINT` (Ctrl+C) îl oprește imediat, chiar și în mijlocul unui job. Jobul rămâne în starea `processing` până când recuperarea joburilor expirate îl resetează după `QUEUETY_STALE_TIMEOUT` secunde, așadar asigurați-vă că handlerii pot fi reîncercați în siguranță. Un grup de lucrători transmite `SIGTERM` proceselor copil, însă acestea nu îl tratează, astfel că grupul continuă să ruleze până când se opresc din unul dintre motivele de mai sus. Când trebuie să opriți un grup, configurați managerul de procese cu un timp de așteptare la oprire, după care trimite `SIGKILL`.

## Concurența între mai mulți lucrători

Opțiunea `--workers=N` pornește N procese copil folosind `pcntl_fork()`. Fiecare copil creează propria conexiune la baza de date și procesează joburi independent. Procesul părinte monitorizează copiii și îi repornește pe cei care se blochează.

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

Cerințe:
Extensiile `pcntl` și `posix` PHP trebuie instalate (fără `pcntl`, comanda eșuează cu „pcntl extension is required for --workers=N.”), iar fiecare copil folosește propria conexiune la baza de date, deci setarea MySQL `max_connections` trebuie să fie suficientă pentru grup. Grupurile adaptive (`--min-workers`/`--max-workers`) au aceleași cerințe.

## Grupuri adaptive de lucrători

Grupurile adaptive mențin un număr minim de lucrători pregătiți, apoi cresc sau scad în intervalul configurat:

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

Procesul părinte monitorizează volumul de joburi eligibile pentru revendicare din setul de cozi selectat și ajustează numărul de procese copil pentru a-l aproxima, fără a depăși maximul configurat. Când coada se liniștește, grupul așteaptă perioada de grație pentru inactivitate înainte de a reduce numărul de lucrători, astfel încât creșterile scurte de volum să nu modifice imediat lista de procese.

Extinderea grupului rămâne prudentă. Când Queuety poate citi datele de utilizare a memoriei containerului sau gazdei, refuză să adauge procese copil dacă memoria disponibilă nu este suficientă pentru încă un lucrător cu necesarul său complet de resurse.

Bucla de scalare din procesul părinte este controlată de:

| Constantă | Descriere |
|---|---|
| `QUEUETY_WORKER_POOL_SCALE_INTERVAL` | Cât de des ajustează procesul părinte dimensiunea grupului |
| `QUEUETY_WORKER_POOL_IDLE_GRACE` | Cât timp trebuie să rămână inactiv volumul de joburi înainte de reducerea grupului |

## Recuperarea joburilor expirate

Dacă un lucrător se blochează în timp ce procesează un job, jobul rămâne în starea `processing`. Un lucrător de lungă durată verifică aproximativ o dată pe minut dacă există joburi aflate în starea `processing` de mai mult de `QUEUETY_STALE_TIMEOUT` secunde. Un job expirat care mai are încercări disponibile este reîncercat după întârzierea configurată; unul care și-a epuizat toate încercările este îngropat, iar fluxul său de lucru este marcat ca eșuat. Lucrătorii `--once` și alternativa WP-Cron nu efectuează această verificare.

Recuperați manual joburile expirate:

```bash
wp queuety recover
```

## Modul de golire

Procesează toate joburile în așteptare și iese. Util pentru testare sau procesarea loturilor o singură dată:

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

## Implementarea în producție

Pentru producție, folosiți un manager de procese care menține lucrătorii în execuție. Iată două metode frecvente.

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

Activați și porniți:

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

### Sfaturi pentru implementare

Rulați lucrători separați pentru cozi distincte atunci când aveți nevoie de o izolare mai bună. Folosiți `--workers=N` când volumul de lucru este limitat de CPU și preferați lucrători monitorizați individual când volumul de lucru implică mai ales operațiuni de I/O. Setați `QUEUETY_WORKER_MAX_JOBS` dacă doriți reporniri periodice care să elibereze memoria și monitorizați numărul de `buried` prin [webhookuri](https://queuety.inline0.dev/ro/docs/features/webhooks) sau [metrice](https://queuety.inline0.dev/ro/docs/features/metrics).
