
# ワークフローの期限

ワークフローには期限を設定できます。実行中のワークフローが指定された時間内に完了しない場合、Queuetyはそれを失敗としてマークし、任意でハンドラーを呼び出します。一時停止中、シグナル待ち、または他のワークフロー待ちのワークフローは、待機中に期限切れになりません。期限は、長時間稼働する`wp queuety work`プロセスによって約1分ごとに確認されます。`--once`ワーカーやWP-Cronの代替処理では確認されません。これは、無期限に実行されるべきでない長時間稼働のワークフローに対するデッドマンスイッチとして機能します。

## 期限の設定

ワークフロービルダーで`must_complete_within()`を使い、許容する最大実行時間を設定します。期限を超過したときに実行するハンドラーを登録するには、`on_deadline()`を使います。

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

`must_complete_within()`メソッドは、合計される名前付きパラメーターを受け取ります。

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

## 期限ハンドラー

`on_deadline()`ハンドラークラスは`handle( array $state ): void`メソッドを実装する必要があります。ワークフローの公開状態（`_`で始まらないすべてのキー）が渡されます。

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

期限ハンドラーは任意です。独自のロジックを実行せずにワークフローを失敗させるだけの場合は、`on_deadline()`を指定せずに`must_complete_within()`を使います。

## 期限の確認方法

ワーカーは処理ループの一環として、60秒ごとに期限切れを確認します。`status`がまだ`running`で、`deadline_at`が過去になっているワークフローが見つかると、登録済みの場合は`on_deadline`ハンドラーを呼び出し、エラーメッセージ`Deadline exceeded`を付けてワークフローを失敗としてマークし、`workflow_deadline_exceeded`ログイベントを記録します。期限はワークフロー行の`deadline_at`タイムスタンプとして保存され、設定された期間を基にディスパッチ時に計算されます。

## 期限切れ時の動作

ワークフローが期限を超過すると、ステータスが`failed`に変わり、エラーメッセージに`Deadline exceeded`が設定され、登録されている場合は期限切れハンドラーが同期的に実行され、`workflow_deadline_exceeded`ログエントリーが記録されます。実行中のジョブが途中で中断されることはありませんが、ワークフローは期限切れのステップ境界を越えて進みません。期限切れハンドラー自体が例外をスローした場合、その例外は黙って捕捉され、ワークフローは失敗状態になります。

## WorkflowBuilderのメソッド

| メソッド | 説明 |
|---|---|
| `must_complete_within( int $seconds = 0, int $minutes = 0, int $hours = 0, int $days = 0 )` | 許可する最大時間を設定します。すべてのパラメーターが合算されます。 |
| `on_deadline( string $handler_class )` | 期限を超過したときに呼び出すハンドラーを登録します。 |

## 例: 再試行を伴う期限設定

期限切れで失敗したワークフローを再試行しても、ほとんど役に立ちません。`retry_workflow()`は状態を`running`にリセットし、失敗したステップを再キューイングしますが、期限を解除も延長もしないため、約1分以内の次の期限チェックで再び失敗します。代わりに新しいワークフローをディスパッチしてください。

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

## 期限とスリープ遅延の違い

期限はワークフロー全体の実行時間に適用されます。スリープ遅延（`->delay()`）は、ステップ列の一部としてワークフローを一定時間停止します。1時間のスリープ遅延と2時間の期限を設定したワークフローでは、スリープ以外のステップにかかる合計時間が1時間を超えると失敗します。
