
# Prazos de workflows

É possível definir um prazo para workflows. Se um workflow em execução não for concluído dentro do período especificado, Queuety o marca como com falha e, opcionalmente, chama um handler. Workflows pausados ou aguardando um sinal ou outros workflows não expiram enquanto aguardam. Os prazos são verificados aproximadamente uma vez por minuto por um processo `wp queuety work` de longa duração, não pelos workers `--once` nem pelo fallback WP-Cron. Isso funciona como um mecanismo de segurança para workflows de longa duração que não devem ser executados indefinidamente.

## Definindo um prazo

Use `must_complete_within()` no construtor do workflow para definir a duração máxima permitida. Use `on_deadline()` para registrar um handler executado quando o prazo é excedido.

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

O método `must_complete_within()` aceita parâmetros nomeados que são somados:

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

## Handler de prazo

A classe do handler de prazo `on_deadline()` deve implementar um método `handle( array $state ): void`. Ela recebe o estado público do workflow (todas as chaves que não começam com `_`).

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

O handler de prazo é opcional. Se você quiser apenas que o workflow falhe sem executar uma lógica personalizada, use `must_complete_within()` sem `on_deadline()`.

## Como funciona a verificação de prazo

O worker verifica prazos expirados a cada 60 segundos como parte do seu ciclo de processamento. Quando encontra um workflow cujo `status` ainda é `running` e cujo `deadline_at` está no passado, ele chama o handler `on_deadline`, se houver um registrado, marca o workflow como com falha com a mensagem de erro `Deadline exceeded` e registra um evento de log `workflow_deadline_exceeded`. O prazo é armazenado como um timestamp `deadline_at` na linha do workflow e calculado no momento do despacho com base na duração configurada.

## O que acontece quando o prazo expira

Quando um workflow ultrapassa o prazo, seu status muda para `failed`, a mensagem de erro é definida como `Deadline exceeded`, o handler de prazo é executado de forma síncrona se houver um registrado e uma entrada de log `workflow_deadline_exceeded` é registrada. Jobs em execução não são interrompidos no meio da execução, mas o workflow não avança além do limite da etapa expirada. Se o próprio handler de prazo lançar uma exceção, ela será capturada silenciosamente e o workflow ainda terminará com falha.

## Métodos de WorkflowBuilder

| Método | Descrição |
|---|---|
| `must_complete_within( int $seconds = 0, int $minutes = 0, int $hours = 0, int $days = 0 )` | Define a duração máxima permitida. Todos os parâmetros são somados. |
| `on_deadline( string $handler_class )` | Registra um handler chamado quando o prazo é excedido. |

## Exemplo: prazo com repetição

Raramente é útil repetir um workflow que falhou por causa do prazo. `retry_workflow()` o redefine para `running` e volta a enfileirar a etapa com falha, mas não limpa nem prolonga o prazo; portanto, a próxima verificação, em cerca de um minuto, fará com que ele falhe novamente. Despache um novo 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 );
}
```

## Prazo vs. atrasos de espera

Os prazos se aplicam à duração total do workflow. Atrasos de espera (`->delay()`) pausam o workflow por um período fixo como parte da sequência de etapas. Um workflow com um atraso de espera de 1 hora e um prazo de 2 horas falhará se as etapas sem espera levarem mais de 1 hora no total.
