
# Scadenze dei workflow

È possibile assegnare una scadenza ai workflow. Se un workflow in esecuzione non viene completato entro la durata specificata, Queuety lo contrassegna come non riuscito e, facoltativamente, chiama un gestore. I workflow in pausa o in attesa di un segnale o di altri workflow non scadono durante l'attesa. Le scadenze vengono controllate circa una volta al minuto da un processo `wp queuety work` a esecuzione prolungata, non dai worker `--once` né dal fallback WP-Cron. Funziona come un dispositivo di sicurezza per workflow di lunga durata che non devono rimanere in esecuzione all'infinito.

## Impostare una scadenza

Usate `must_complete_within()` nel generatore del workflow per impostare la durata massima consentita. Usate `on_deadline()` per registrare un gestore da eseguire quando la scadenza viene superata.

```php
use Queuety\Queuety;

Queuety::workflow( 'data_import' )
    ->then( FetchDataHandler::class )
    ->then( TransformDataHandler::class )
    ->then( LoadDataHandler::class )
    ->must_complete_within( hours: 24 )
    ->on_deadline( ImportTimeoutHandler::class )
    ->dispatch( [ 'source' => $url ] );
```

Il metodo `must_complete_within()` accetta parametri denominati che vengono sommati:

```php
->must_complete_within( hours: 2, minutes: 30 )  // 2.5 hours
->must_complete_within( days: 1 )                 // 24 hours
->must_complete_within( seconds: 3600 )           // 1 hour
->must_complete_within( days: 1, hours: 12 )      // 36 hours
```

## Gestore della scadenza

La classe del gestore `on_deadline()` deve implementare un metodo `handle( array $state ): void`. Riceve lo stato pubblico del workflow (tutte le chiavi che non iniziano con `_`).

```php
class ImportTimeoutHandler {

    public function handle( array $state ): void {
        // Alert the team.
        wp_mail(
            'ops@example.com',
            'Import workflow timed out',
            'Workflow for source ' . ( $state['source'] ?? 'unknown' ) . ' exceeded its deadline.'
        );

        // Clean up partial work.
        if ( isset( $state['temp_file'] ) ) {
            unlink( $state['temp_file'] );
        }
    }
}
```

Il gestore della scadenza è facoltativo. Se volete che il workflow non riesca senza eseguire logica personalizzata, usate `must_complete_within()` senza `on_deadline()`.

## Come funziona il controllo delle scadenze

Il worker controlla le scadenze superate ogni 60 secondi, nell'ambito del proprio ciclo di elaborazione. Quando trova un workflow il cui `status` è ancora `running` e il cui `deadline_at` è nel passato, chiama il gestore `on_deadline`, se registrato, contrassegna il workflow come non riuscito con il messaggio di errore `Deadline exceeded` e registra un evento di log `workflow_deadline_exceeded`. La scadenza viene memorizzata come timestamp `deadline_at` nella riga del workflow e viene calcolata al momento dell'avvio in base alla durata configurata.

## Cosa accade alla scadenza

Quando un workflow supera la propria scadenza, il suo stato passa a `failed`, il messaggio di errore viene impostato su `Deadline exceeded`, il gestore della scadenza viene eseguito in modo sincrono se registrato e viene aggiunta una voce di log `workflow_deadline_exceeded`. I job in esecuzione non vengono interrotti a metà, ma il workflow non avanza oltre il limite del passaggio scaduto. Se il gestore della scadenza genera un'eccezione, questa viene ignorata silenziosamente e il workflow risulta comunque non riuscito.

## Metodi di WorkflowBuilder

| Metodo | Descrizione |
|---|---|
| `must_complete_within( int $seconds = 0, int $minutes = 0, int $hours = 0, int $days = 0 )` | Imposta la durata massima consentita. Tutti i parametri vengono sommati. |
| `on_deadline( string $handler_class )` | Registra un gestore da chiamare quando la scadenza viene superata. |

## Esempio: scadenza con nuovo tentativo

Di rado è utile riprovare un workflow non riuscito a causa della scadenza. `retry_workflow()` lo reimposta su `running` e rimette in coda il passaggio non riuscito, ma non cancella né estende la scadenza, quindi il controllo successivo, entro circa un minuto, lo farà fallire di nuovo. Avviate invece un nuovo workflow.

```php
$status = Queuety::workflow_status( $workflow_id );

if ( 'failed' === $status->status->value ) {
    // Check if it was a deadline failure.
    // You may want to dispatch a new workflow instead of retrying.
    Queuety::retry_workflow( $workflow_id );
}
```

## Scadenza e ritardi di attesa

Le scadenze si applicano all'intera durata del workflow. I ritardi di attesa (`->delay()`) mettono in pausa il workflow per una durata fissa all'interno della sequenza di passaggi. Un workflow con un ritardo di attesa di 1 ora e una scadenza di 2 ore non riuscirà se i passaggi che non sono di attesa richiedono in totale più di 1 ora.
