
# スケジューリング

Queuetyは定期ジョブを標準でサポートしています。`wp_cron`に頼る代わりに、スケジュールはデータベースに保存され、長時間稼働する`wp queuety work`プロセス（またはワーカープールの親プロセス）によって約1分ごとに評価されます。`wp queuety work --once`、`wp queuety flush`、およびプラグイン組み込みのWP-Cronフォールバックはスケジュールを評価しません（[#14](https://github.com/inline0/queuety/issues/14)）。長時間稼働するワーカーがない場合は、自分で`wp queuety schedule run`を実行してください。

## 間隔スケジュール

単純な間隔ベースのスケジュールには`every()`を使います。式にはPHPの`DateTimeImmutable::modify()`と互換性のある任意の文字列を指定できます。

```php
use Queuety\Queuety;

Queuety::schedule( 'cleanup_handler' )
    ->every( '1 hour' )
    ->on_queue( 'maintenance' );

Queuety::schedule( 'sync_inventory' )
    ->every( '30 minutes' )
    ->on_queue( 'sync' );

Queuety::schedule( 'daily_report', [ 'type' => 'summary' ] )
    ->every( '1 day' );
```

## Cronスケジュール

標準的な5フィールドのcron式には`cron()`を使います。

```php
// Every day at 3am
Queuety::schedule( 'nightly_cleanup' )
    ->cron( '0 3 * * *' );

// Every Monday at 9am
Queuety::schedule( 'weekly_digest' )
    ->cron( '0 9 * * 1' )
    ->on_queue( 'emails' );

// Every 15 minutes
Queuety::schedule( 'health_check' )
    ->cron( '*/15 * * * *' );
```

## スケジュールのオプション

`PendingSchedule`ビルダーでは次のメソッドを利用できます。

| メソッド | 説明 |
|---|---|
| `every( string $interval )` | 間隔式を設定 |
| `cron( string $expression )` | cron式を設定 |
| `on_queue( string $queue )` | 対象キューを設定（デフォルト: `'default'`） |
| `overlap( OverlapPolicy $policy )` | 重複ポリシーを設定（デフォルト: `Allow`） |

スケジュールがディスパッチされる前に、`every()`または`cron()`のいずれかを呼び出す必要があります。どちらも設定されていない場合は、`RuntimeException`がスローされます。

## 重複ポリシー

次のティックが発生した時点でスケジュール済みジョブがまだ実行中（保留中または処理中）の場合、重複ポリシーによって動作が決まります。`overlap()`メソッドで設定します。

```php
use Queuety\Queuety;
use Queuety\Enums\OverlapPolicy;

Queuety::schedule( 'sync_inventory' )
    ->every( '5 minutes' )
    ->overlap( OverlapPolicy::Skip )
    ->on_queue( 'sync' );
```

`OverlapPolicy`列挙型には3つのケースがあります。

| ポリシー | 値 | 動作 |
|---|---|---|
| `OverlapPolicy::Allow` | `allow` | 前回の実行がまだ進行中でも、常に新しいジョブをディスパッチします。これがデフォルトです。 |
| `OverlapPolicy::Skip` | `skip` | ハンドラーに保留中または処理中のジョブがある場合、現在のティックをスキップします。スケジュールが予定どおりに進むよう、`next_run`は通常どおり更新されます。 |
| `OverlapPolicy::Buffer` | `buffer` | Skipと同様に、ティックはディスパッチされず、`next_run`は次の定期実行時刻に進みます。Skipとは異なり、`last_run`は更新されません。 |

### Allow（デフォルト）

同じハンドラーの複数のインスタンスを同時に実行できます。重複しても問題のない冪等なハンドラーに使います。

```php
Queuety::schedule( 'send_heartbeat_ping' )
    ->every( '1 minute' )
    ->overlap( OverlapPolicy::Allow );
```

### スキップ

ハンドラーがまだ実行中の場合、ティックは通知なくスキップされます。2つ目のインスタンスを実行すると無駄が生じたり、競合したりするハンドラーに使います。

```php
Queuety::schedule( 'rebuild_search_index' )
    ->every( '10 minutes' )
    ->overlap( OverlapPolicy::Skip );
```

### バッファー

現在の動作はSkipと同じです。ハンドラーに保留中または処理中のジョブがあると、ティックはディスパッチされず、`next_run`は次の定期実行時刻に進みます。違いは、Bufferでは`last_run`が変更されず、Skipではスキップしたティックが最終実行として記録される点です。

```php
Queuety::schedule( 'sync_inventory' )
    ->every( '5 minutes' )
    ->overlap( OverlapPolicy::Buffer );
```

## 仕組み

スケジュールの作成時、Queuetyは式、ハンドラー、ペイロード、次回の計算済み実行時刻とともに`queuety_schedules`テーブルへ保存します。

スケジューラーの各ティック（長時間稼働するワーカーでは約1分ごと、または`wp queuety schedule run`を実行したとき）に、スケジュールは`next_run`を過ぎたものを確認します。実行時刻を迎えた各スケジュールについて、新しいジョブをディスパッチし、`next_run`を次の実行時刻に更新します。

## CLIでスケジュールを管理する

```bash
# List all schedules
wp queuety schedule list

# Add a recurring schedule
wp queuety schedule add cleanup_handler --every="1 hour"
wp queuety schedule add nightly_report --cron="0 3 * * *" --queue=reports

# Remove a schedule
wp queuety schedule remove cleanup_handler

# Manually trigger the scheduler
wp queuety schedule run
```

## PHPでスケジュールを管理する

```php
// Get the scheduler instance
$scheduler = Queuety::scheduler();

// List all schedules
$schedules = $scheduler->list();

// Remove a schedule by handler name
$scheduler->remove( 'cleanup_handler' );

// Manually trigger a tick
$count = $scheduler->tick();
```

## ペイロード

スケジュールには、ディスパッチされる各ジョブに渡す固定ペイロードを含められます。

```php
Queuety::schedule( 'generate_report', [ 'type' => 'daily', 'format' => 'pdf' ] )
    ->every( '1 day' )
    ->on_queue( 'reports' );
```

スケジュールが実行されるたびに、このペイロードを含む新しいジョブがディスパッチされます。
