# 機能設計書 15-バッチ処理

## 概要

本ドキュメントは、Laravel Frameworkのバッチ処理機能の設計を詳細に記述したものである。複数のジョブをバッチとしてグループ化し、進捗追跡、完了コールバック、キャンセル機能を提供する。

### 本機能の処理概要

バッチ処理は、複数の関連するジョブを論理的にグループ化し、バッチ全体の進捗状況の追跡、全ジョブ完了時のコールバック実行、バッチ単位でのキャンセルを可能にする機能である。

**業務上の目的・背景**：大量データの処理や複数の関連タスクを実行する際、個々のジョブを個別に管理するのは困難である。バッチ処理は、これらのジョブをグループとして管理することで、処理全体の進捗把握、エラー発生時の適切な対応、完了後の後処理を容易にする。例えば、大量の請求書生成、一括メール送信、データインポートなどのシナリオで活用される。

**機能の利用シーン**：
- 大量データのバッチインポート・エクスポート
- 複数宛先への一括通知送信
- 並列処理による画像・動画の一括変換
- 定期的なデータ同期処理
- 大規模レポートの生成

**主要な処理内容**：
1. バッチの作成と保存（store メソッド）
2. バッチ情報の取得（get, find メソッド）
3. ジョブ数の増減管理（incrementTotalJobs, decrementPendingJobs メソッド）
4. 失敗ジョブの追跡（incrementFailedJobs メソッド）
5. バッチの完了・キャンセル（markAsFinished, cancel メソッド）
6. 古いバッチの削除（prune, pruneUnfinished, pruneCancelled メソッド）

**関連システム・外部連携**：
- データベース：バッチ情報の永続化（DatabaseBatchRepository）
- キュー：バッチに属するジョブの実行
- イベント：バッチ完了・失敗時のコールバック

**権限による制御**：バッチ処理自体は権限制御を持たないが、バッチ内のジョブで個別の権限チェックを実装可能。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| - | - | - | 本機能は画面に直接関連しない |

## 機能種別

バッチ処理 / ジョブ管理 / 進捗追跡

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| batch | PendingBatch | Yes（store時） | 作成するバッチ情報 | - |
| batchId | string | Yes（find/update時） | バッチID（UUID） | 有効なUUID |
| amount | int | Yes（incrementTotalJobs時） | 増加するジョブ数 | 正の整数 |
| jobId | string | Yes（decrement/increment時） | ジョブID | - |
| limit | int | No（get時） | 取得件数上限 | デフォルト50 |
| before | mixed | No（get時） | ページネーション用 | - |

### 入力データソース

- アプリケーションコード：Bus::batch() によるバッチ作成
- ワーカー：ジョブ実行結果によるバッチ状態更新

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| id | string | バッチID（Ordered UUID） |
| name | string | バッチ名 |
| total_jobs | int | 総ジョブ数 |
| pending_jobs | int | 未処理ジョブ数 |
| failed_jobs | int | 失敗ジョブ数 |
| failed_job_ids | array | 失敗ジョブIDリスト |
| options | array | バッチオプション（コールバック等） |
| created_at | CarbonImmutable | 作成日時 |
| cancelled_at | CarbonImmutable/null | キャンセル日時 |
| finished_at | CarbonImmutable/null | 完了日時 |

### 出力先

- データベース：job_batchesテーブル

## 処理フロー

### 処理シーケンス

```
1. バッチ作成
   └─ Bus::batch([...jobs])->dispatch()
2. ジョブの追加
   └─ incrementTotalJobs でtotal_jobs/pending_jobsを増加
3. ジョブ実行
   └─ ワーカーがジョブをキューから取得し実行
4. 完了/失敗の記録
   └─ decrementPendingJobs または incrementFailedJobs
5. バッチ完了判定
   └─ pending_jobs == 0 で完了コールバック実行
6. バッチ終了
   └─ markAsFinished で finished_at を設定
```

### フローチャート

```mermaid
flowchart TD
    A[バッチ作成] --> B[ジョブ追加]
    B --> C[ジョブ実行開始]
    C --> D{ジョブ成功?}
    D -->|Yes| E[decrementPendingJobs]
    D -->|No| F[incrementFailedJobs]
    E --> G{pending_jobs == 0?}
    F --> H{allowFailures?}
    H -->|Yes| G
    H -->|No| I[バッチキャンセル]
    G -->|Yes| J[完了コールバック実行]
    G -->|No| C
    J --> K[markAsFinished]
    I --> K
    K --> L[終了]
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-15-01 | Ordered UUID | バッチIDにはOrdered UUIDを使用 | store時 |
| BR-15-02 | 原子的更新 | ジョブカウントの更新はトランザクション内で排他ロック | update時 |
| BR-15-03 | PostgreSQL対応 | PostgreSQLではoptionsをbase64エンコード | serialize時 |
| BR-15-04 | バッチ削除条件 | finished_atが設定されたバッチのみprune対象 | prune時 |
| BR-15-05 | 失敗ジョブID管理 | 失敗ジョブIDはJSON配列で重複なく管理 | incrementFailedJobs時 |

### 計算ロジック

```php
// バッチ進捗率
progress = ($totalJobs - $pendingJobs) / $totalJobs * 100

// 完了判定
isFinished = $pendingJobs === 0

// キャンセル判定
isCancelled = $cancelledAt !== null
```

## データベース操作仕様

### 操作別データベース影響一覧

| 操作 | 対象テーブル | 操作種別 | 概要 |
|-----|-------------|---------|------|
| store | job_batches | INSERT | 新規バッチを作成 |
| get | job_batches | SELECT | バッチ一覧を取得 |
| find | job_batches | SELECT | 個別バッチを取得（WriteP do使用） |
| incrementTotalJobs | job_batches | UPDATE | ジョブ数を増加 |
| decrementPendingJobs | job_batches | UPDATE | 未処理ジョブ数を減少（排他ロック） |
| incrementFailedJobs | job_batches | UPDATE | 失敗ジョブ数を増加（排他ロック） |
| markAsFinished | job_batches | UPDATE | 完了日時を設定 |
| cancel | job_batches | UPDATE | キャンセル日時を設定 |
| delete | job_batches | DELETE | バッチを削除 |
| prune | job_batches | DELETE | 古いバッチを削除 |

### テーブル別操作詳細

#### job_batches

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| INSERT | id | Str::orderedUuid() | プライマリキー |
| INSERT | name | $batch->name | バッチ名 |
| INSERT | total_jobs | 0 | 初期値 |
| INSERT | pending_jobs | 0 | 初期値 |
| INSERT | failed_jobs | 0 | 初期値 |
| INSERT | failed_job_ids | '[]' | 初期値（空配列JSON） |
| INSERT | options | serialize($batch->options) | シリアライズ |
| INSERT | created_at | time() | UNIXタイムスタンプ |
| UPDATE | total_jobs | total_jobs + $amount | 式による更新 |
| UPDATE | pending_jobs | pending_jobs + $amount | 式による更新 |

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| - | QueryException | DB接続エラー | DB接続設定を確認 |
| - | DeserializationException | options復元失敗 | 空配列を返却 |

### リトライ仕様

バッチ処理自体にリトライ機構はない。個々のジョブのリトライは通常のジョブリトライ機構に従う。

## トランザクション仕様

- store：単一INSERT、トランザクション不要
- updateAtomicValues：排他ロック（lockForUpdate）を使用したトランザクション内更新
- prune：1000件単位のバッチ削除、各バッチ個別トランザクション

## パフォーマンス要件

- find：useWritePdo()で書き込みコネクションを使用し、レプリカ遅延を回避
- prune：1000件単位の削除でメモリ効率を確保
- 原子的更新：lockForUpdateで競合を防止

## セキュリティ考慮事項

- optionsにはクロージャがシリアライズされるため、信頼できるソースからのみ受け入れ
- PostgreSQL対応のbase64エンコードは難読化ではなく、バイナリ安全性のため

## 備考

- BatchRepositoryインターフェース：基本操作を定義
- PrunableBatchRepository：prune操作を追加したインターフェース
- BatchFactory：Batchインスタンスの生成を担当

---

## コードリーディングガイド

本機能を理解するために参照すべきファイルと、推奨する読み解き順序を以下に示す。

### 推奨読解順序

#### Step 1: インターフェースを理解する

まず、バッチリポジトリのインターフェースを理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | BatchRepository.php | `src/Illuminate/Bus/BatchRepository.php` | 基本操作の契約を確認 |

**読解のコツ**: BatchRepositoryインターフェースは10のメソッドを定義。get, find, store, incrementTotalJobs, decrementPendingJobs, incrementFailedJobs, markAsFinished, cancel, delete, transaction, rollBack。

#### Step 2: DatabaseBatchRepositoryを理解する

データベース永続化の実装を把握する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | DatabaseBatchRepository.php | `src/Illuminate/Bus/DatabaseBatchRepository.php` | コンストラクタとプロパティ（44-49行目） |
| 2-2 | DatabaseBatchRepository.php | `src/Illuminate/Bus/DatabaseBatchRepository.php` | storeメソッド（95-113行目） |
| 2-3 | DatabaseBatchRepository.php | `src/Illuminate/Bus/DatabaseBatchRepository.php` | updateAtomicValuesメソッド（184-195行目） |

**主要処理フロー**:
1. **44-49行目**: コンストラクタでBatchFactory、Connection、テーブル名を設定
2. **58-69行目**: `get` - ページネーション付きでバッチ一覧を取得
3. **77-87行目**: `find` - useWritePdo()を使用して単一バッチを取得
4. **95-113行目**: `store` - Ordered UUIDを生成してINSERT
5. **122-129行目**: `incrementTotalJobs` - Expressionを使用した原子的更新
6. **138-152行目**: `decrementPendingJobs` - lockForUpdateを使用した排他的更新

#### Step 3: シリアライズ処理を理解する

PostgreSQL対応のシリアライズ処理を把握する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | DatabaseBatchRepository.php | `src/Illuminate/Bus/DatabaseBatchRepository.php` | serialize/unserializeメソッド（331-358行目） |
| 3-2 | DatabaseBatchRepository.php | `src/Illuminate/Bus/DatabaseBatchRepository.php` | toBatchメソッド（366-381行目） |

**主要処理フロー**:
- **331-338行目**: `serialize` - PostgreSQLの場合はbase64エンコード
- **346-358行目**: `unserialize` - base64デコードと例外ハンドリング
- **366-381行目**: `toBatch` - DBレコードからBatchインスタンスを生成

### プログラム呼び出し階層図

```
Bus::batch([...jobs])->dispatch()
    │
    └─ PendingBatch::dispatch()
           │
           ├─ BatchRepository::store($pendingBatch)
           │      └─ DatabaseBatchRepository::store()
           │             ├─ Str::orderedUuid()
           │             ├─ $this->serialize($options)
           │             └─ $this->connection->table()->insert(...)
           │
           └─ foreach ($jobs as $job)
                  └─ Queue::push($job)

Worker::process() -> Job::fire()
    │
    └─ BatchRepository::decrementPendingJobs($batchId, $jobId)
           └─ DatabaseBatchRepository::updateAtomicValues()
                  ├─ $this->connection->transaction()
                  ├─ lockForUpdate()
                  └─ $this->connection->table()->update(...)
```

### データフロー図

```
[入力]                    [処理]                         [出力]

PendingBatch      ───▶  store()                   ───▶  job_batches INSERT
    │                        │
    │                  orderedUuid()              ───▶  バッチID
    │                        │
options           ───▶  serialize()               ───▶  シリアライズ済みoptions

ジョブ完了        ───▶  decrementPendingJobs()    ───▶  pending_jobs更新
    │                        │
    │                  lockForUpdate()            ───▶  排他ロック取得
    │                        │
    │                  JSON更新                   ───▶  failed_job_ids更新

DBレコード        ───▶  toBatch()                 ───▶  Batchインスタンス
    │                        │
options           ───▶  unserialize()             ───▶  PHP配列
    │                        │
timestamps        ───▶  CarbonImmutable           ───▶  日時オブジェクト
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| BatchRepository.php | `src/Illuminate/Bus/BatchRepository.php` | インターフェース | 基本操作契約 |
| PrunableBatchRepository.php | `src/Illuminate/Bus/PrunableBatchRepository.php` | インターフェース | プルーニング契約 |
| DatabaseBatchRepository.php | `src/Illuminate/Bus/DatabaseBatchRepository.php` | ソース | DB永続化実装 |
| Batch.php | `src/Illuminate/Bus/Batch.php` | ソース | バッチエンティティ |
| PendingBatch.php | `src/Illuminate/Bus/PendingBatch.php` | ソース | 作成中バッチ |
| BatchFactory.php | `src/Illuminate/Bus/BatchFactory.php` | ソース | Batchファクトリ |
| UpdatedBatchJobCounts.php | `src/Illuminate/Bus/UpdatedBatchJobCounts.php` | ソース | 更新結果DTO |
| Batchable.php | `src/Illuminate/Bus/Batchable.php` | トレイト | ジョブ用トレイト |
| BusServiceProvider.php | `src/Illuminate/Bus/BusServiceProvider.php` | ソース | サービスプロバイダー |
