
# Worker

I worker sono processi a lunga esecuzione che interrogano il database alla ricerca di job in attesa, li eseguono e fanno avanzare i workflow. Il comando `wp queuety work` viene eseguito all'interno di WP-CLI, mentre la coda stessa viene acquisita direttamente da MySQL tramite il driver DB Queuety migliore disponibile.

Il plugin installa anche un fallback WordPress cron predefinito, eseguito una volta al minuto, per gli ambienti senza accesso alla shell. Questa pagina illustra i worker WP-CLI dedicati, la modalità di distribuzione consigliata per una maggiore capacità di elaborazione.

## Avviare un worker

```bash
wp queuety work
```

Avvia un singolo worker sulla coda `default`. Viene eseguito in un ciclo: acquisisce un job, lo esegue e ripete l'operazione. Quando la coda è vuota, attende `QUEUETY_WORKER_SLEEP` secondi (valore predefinito: 1) prima di interrogarla di nuovo.

### Opzioni

| Opzione | Descrizione |
|---|---|
| `--queue=<queue>` | Queue(s) da elaborare, separati da virgole per indicare l'ordine di priorità (predefinito: `default`) |
| `--once` | Elabora al massimo un job e termina |
| `--workers=<n>` | Avvia N processi worker (richiede le estensioni `pcntl` e `posix`) |
| `--min-workers=<n>` | Numero minimo di worker per un pool adattivo. Richiede `--max-workers` |
| `--max-workers=<n>` | Numero massimo di worker per un pool adattivo |

```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à tra più code

Un singolo worker può elaborare più code in ordine di priorità. Passate un elenco di nomi di code separati da virgole a `--queue`:

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

Il worker tenta di acquisire un job da ciascuna coda nell'ordine indicato. Controlla prima `critical`. Se `critical` contiene un job, lo elabora. Se `critical` è vuota, passa a `default` e poi a `low`. In questo modo le code con priorità più alta vengono svuotate prima di quelle con priorità inferiore.

### Funzionamento dell'ordine di priorità

A ogni iterazione, il worker tenta di acquisire un job dalla prima coda dell'elenco. Se la coda è vuota o in pausa, passa alla successiva e continua finché non trova un job o esaurisce l'elenco. Se non trova job in nessuna coda, attende e riprova. Il modello applica priorità rigorose: un flusso costante di job `critical` impedirà l'elaborazione di `default` e `low`. Se le code con priorità inferiore devono garantire una certa capacità di elaborazione, usate worker dedicati.

### Usare più code in PHP

I metodi `Worker::run()` e `Worker::flush()` accettano una stringa o un array:

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

### Strategie di distribuzione

**I worker dedicati per coda** offrono il pieno controllo sulla concorrenza e sull'isolamento. Ogni coda ha il proprio process(es) worker e non può essere ostacolata dalle altre:

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

**L'ordine di priorità con un solo worker** è più semplice da gestire e funziona bene quando le code con priorità inferiore possono tollerare ritardi:

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

Potete anche combinare entrambi gli approcci: worker dedicati per `critical` per garantire la capacità di elaborazione e un worker con code ordinate per priorità per tutto il resto:

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

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

## Ammissione in base alle risorse

I worker non si limitano ad acquisire la prossima riga in attesa. Prima di avviare un job acquisito, Queuety può applicare controlli di ammissione generali in base al gruppo di concorrenza del job e al profilo di esecuzione recente per quel gestore.

Per il modello più ampio, incluso il modo in cui si integra con i metadati dei job e i budget dei workflow, consultate [Gestione delle risorse](https://queuety.inline0.dev/it/docs/features/resource-management).

Un gruppo di concorrenza assegna un limite condiviso a più gestori o classi di job. Se assegnate lo stesso gruppo e limite a più provider dispendiosi, quando il gruppo raggiunge il limite un worker rilascia il nuovo lavoro acquisito invece di avviarlo. In questo modo le code indipendenti non sovraccaricano un singolo sistema downstream solo perché ci sono più worker inattivi.

Anche i log di esecuzione recenti alimentano un semplice profilo di ammissione. Quando l'ammissione basata sulle risorse è attiva, Queuety esamina la durata media recente e il picco di memoria del gestore che sta per eseguire. Se il worker corrente è già vicino al limite di memoria configurato, se una coda o un gruppo di provider ha già raggiunto il budget ponderato, se il container corrente non sembra avere memoria residua sufficiente per il profilo osservato del gestore oppure se un'esecuzione in stile `--once` è vicina al proprio limite di tempo, Queuety posticipa il job invece di avviare un'attività che probabilmente non terminerà correttamente.

Potete attivare la policy dalla definizione del lavoro, non dal comando del worker:

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

Gli stessi metadati possono essere associati a classi di job inviabili tramite proprietà pubbliche oppure a un singolo invio tramite `PendingJob::concurrency_group()` e `PendingJob::cost_units()`.

Il profilo di ammissione lato worker è controllato da queste costanti:

| Costante | Descrizione |
|---|---|
| `QUEUETY_RESOURCE_ADMISSION` | Attiva o disattiva i controlli di ammissione basati sul profilo |
| `QUEUETY_RESOURCE_PROFILE_WINDOW_MINUTES` | Intervallo temporale da considerare per creare i profili recenti dei gestori |
| `QUEUETY_RESOURCE_PROFILE_TTL` | TTL della cache per i profili recenti dei gestori |
| `QUEUETY_RESOURCE_MEMORY_HEADROOM_MB` | Margine di memoria riservato prima di ammettere un altro job |
| `QUEUETY_RESOURCE_SYSTEM_MEMORY_AWARENESS` | Attiva o disattiva i controlli best effort della memoria del container o dell'host |
| `QUEUETY_RESOURCE_SYSTEM_MEMORY_HEADROOM_MB` | Margine di memoria di sistema riservato prima di ammettere un altro job |
| `QUEUETY_RESOURCE_QUEUE_COST_BUDGETS` | Budget di costo ponderato per nome della coda |
| `QUEUETY_RESOURCE_GROUP_COST_BUDGETS` | Budget di costo ponderato per `concurrency_group` |
| `QUEUETY_RESOURCE_TIME_HEADROOM_MS` | Margine di tempo riservato per le esecuzioni worker avviate una sola volta |

## Ciclo di vita del worker

Un worker si arresta in modo regolare, dopo aver completato il job corrente, quando si verifica una delle seguenti condizioni:

| Condizione | Configurazione |
|---|---|
| Numero massimo di job elaborati | `QUEUETY_WORKER_MAX_JOBS` (valore predefinito: 1000) |
| Memoria massima superata | `QUEUETY_WORKER_MAX_MEMORY` (valore predefinito: 128 MB) |

Dopo l'arresto, il worker termina regolarmente. In produzione, un gestore di processi lo riavvia automaticamente.

La gestione dei segnali non è ancora corretta ([#4](https://github.com/inline0/queuety/issues/4)). Un singolo processo `wp queuety work` non installa gestori di segnali, quindi `SIGTERM` o `SIGINT` (Ctrl+C) lo terminano immediatamente, anche durante un job. Il job resta in stato `processing` finché il ripristino dei job obsoleti non lo reimposta dopo `QUEUETY_STALE_TIMEOUT` secondi; assicuratevi quindi che i gestori possano essere eseguiti di nuovo in sicurezza. Un pool di worker inoltra `SIGTERM` ai processi figli, ma questi non lo gestiscono, quindi il pool continua a funzionare finché non si arrestano per uno dei motivi sopra indicati. Per arrestare un pool, impostate nel gestore di processi un timeout di arresto, al termine del quale verrà inviato `SIGKILL`.

## Concorrenza con più worker

Il flag `--workers=N` avvia N processi figli usando `pcntl_fork()`. Ogni processo figlio crea la propria connessione al database ed elabora i job in modo indipendente. Il processo padre monitora i processi figli e riavvia quelli che si arrestano in modo anomalo.

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

Requisiti:
Devono essere installate le estensioni `pcntl` e `posix` PHP (senza `pcntl`, il comando non riesce e restituisce "pcntl extension is required for --workers=N."); ogni processo figlio usa la propria connessione al database, quindi l'impostazione MySQL `max_connections` deve poter gestire il pool. I pool adattivi (`--min-workers`/`--max-workers`) hanno gli stessi requisiti.

## Pool di worker adattivi

I pool adattivi mantengono attivo un numero minimo di worker, quindi aumentano o riducono il numero entro l'intervallo configurato:

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

Il processo padre controlla il numero di job acquisibili nell'insieme di code selezionato e adegua il numero di processi figli a tale quantità, senza superare il massimo configurato. Quando la coda si calma, il pool attende il periodo di tolleranza per l'inattività prima di ridurre il numero di worker, evitando così che brevi picchi facciano variare continuamente l'elenco dei processi.

L'aumento del pool resta prudente. Quando Queuety può leggere i dati telemetrici sulla memoria del container o dell'host, non aggiunge altri processi figli se la memoria residua non è sufficiente per l'intero fabbisogno di un altro worker.

Il ciclo di scalabilità lato processo padre è controllato da:

| Costante | Descrizione |
|---|---|
| `QUEUETY_WORKER_POOL_SCALE_INTERVAL` | Frequenza con cui il processo padre riconcilia le dimensioni del pool |
| `QUEUETY_WORKER_POOL_IDLE_GRACE` | Durata per cui l'attività in coda deve restare inattiva prima di ridurre il pool |

## Ripristino dei job obsoleti

Se un worker si arresta in modo anomalo mentre elabora un job, il job resta nello stato `processing`. Un worker a lunga esecuzione controlla circa una volta al minuto se ci sono job rimasti in `processing` per più di `QUEUETY_STALE_TIMEOUT` secondi. Un job obsoleto con tentativi residui viene riprovato dopo il ritardo previsto dal backoff; un job che ha esaurito i tentativi viene messo in quarantena e il relativo workflow viene contrassegnato come non riuscito. I worker `--once` e il fallback WP-Cron non eseguono questo controllo.

Ripristinate manualmente i job obsoleti:

```bash
wp queuety recover
```

## Modalità flush

Elabora tutti i job in attesa e termina. Utile per i test o per elaborazioni batch una tantum:

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

## Distribuzione in produzione

In produzione, usate un gestore di processi per mantenere attivi i worker. Ecco due approcci comuni.

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

Abilitate e avviate:

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

### Suggerimenti per la distribuzione

Esegui worker separati per code diverse quando serve un isolamento maggiore. Usa `--workers=N` per carichi di lavoro limitati dalla CPU e preferisci worker supervisionati singolarmente quando il carico è prevalentemente I/O. Imposta `QUEUETY_WORKER_MAX_JOBS` se vuoi riavvii periodici per liberare memoria e tieni d’occhio il conteggio di `buried` tramite [webhook](https://queuety.inline0.dev/it/docs/features/webhooks) o [metriche](https://queuety.inline0.dev/it/docs/features/metrics).
