# 機能設計書 11-Active Job

## 概要

本ドキュメントは、Ruby on Railsフレームワークにおけるジョブフレームワーク「Active Job」の機能設計を記載する。Active Jobは様々なキューイングバックエンドで実行可能なジョブクラスを宣言するための統一インターフェースを提供する。

### 本機能の処理概要

Active Jobは、非同期処理を行うためのジョブフレームワークである。バックグラウンドで実行すべき処理をジョブクラスとして定義し、キューイングシステムを通じて非同期実行する仕組みを提供する。

**業務上の目的・背景**：
Webアプリケーションでは、メール送信、ファイル処理、外部API呼び出しなど、時間のかかる処理がある。これらをリクエスト/レスポンスサイクル内で同期実行すると、ユーザー体験が低下する。Active Jobは、このような処理を非同期でバックグラウンド実行することで、レスポンス時間を短縮し、システムのスケーラビリティを向上させる。また、複数のキューイングバックエンド（Sidekiq、Resque、Delayed Jobなど）に対して統一されたAPIを提供することで、バックエンドの切り替えを容易にする。

**機能の利用シーン**：
- メール送信処理（Action Mailerとの連携）
- ファイルアップロード後の処理（Active Storageとの連携）
- 定期的なデータ集計・レポート生成
- 外部APIとの連携処理
- 大量データのインポート/エクスポート
- キャッシュのウォームアップ

**主要な処理内容**：
1. ジョブクラスの定義と`perform`メソッドの実装
2. `perform_later`によるジョブのキューイング
3. `perform_now`による即時実行
4. ジョブの遅延実行（`set(wait:)`、`set(wait_until:)`）
5. キュー名とプライオリティの指定
6. ジョブ引数のシリアライズ/デシリアライズ
7. コールバック（before_perform、after_perform等）の実行
8. 例外処理とリトライ機能

**関連システム・外部連携**：
- Sidekiq、Resque、Delayed Job、Sneakers、Backburner等のキューイングバックエンド
- Action Mailer（メール送信の非同期化）
- Active Storage（ファイル分析・処理の非同期化）
- Active Record（GlobalIDによるモデルのシリアライズ）

**権限による制御**：
Active Job自体には権限制御機能はない。ジョブ内で実行される処理において、アプリケーション側で適切な権限チェックを実装する必要がある。

## 関連画面

本機能はバックエンド処理のため、直接関連する画面はない。

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| - | - | - | - |

## 機能種別

非同期処理 / ジョブキューイング / バックグラウンド処理

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| arguments | Array | No | performメソッドに渡される引数 | シリアライズ可能な型のみ |
| queue | String/Symbol | No | ジョブを投入するキュー名 | - |
| priority | Integer | No | ジョブの優先度（低い値が高優先） | - |
| wait | ActiveSupport::Duration | No | 実行を遅延させる時間 | - |
| wait_until | Time | No | 実行予定時刻 | - |

### 入力データソース

- アプリケーションコード（perform_laterまたはperform_now呼び出し）
- キューイングバックエンドからのジョブデータ（デシリアライズ時）

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| job_id | String | ジョブの一意識別子（UUID） |
| provider_job_id | String/Integer | キューイングアダプタが発行するID |
| enqueued_at | Time | キューイングされた時刻 |
| scheduled_at | Time | 予定実行時刻（遅延実行の場合） |
| successfully_enqueued? | Boolean | キューイング成功フラグ |
| executions | Integer | 実行回数（リトライ含む） |

### 出力先

- キューイングバックエンドのキュー（perform_later時）
- 呼び出し元（perform_now時の戻り値）

## 処理フロー

### 処理シーケンス

```
1. ジョブのインスタンス化
   └─ ActiveJob::Core#initialize で引数、job_id、キュー名等を設定

2. ジョブのエンキュー（perform_later）
   └─ ActiveJob::Enqueuing#enqueue でキューアダプタに投入
      └─ before_enqueueコールバック実行
      └─ キューアダプタのenqueue/enqueue_at呼び出し
      └─ after_enqueueコールバック実行

3. ジョブの実行（perform_now）
   └─ ActiveJob::Execution#perform_now
      └─ 引数のデシリアライズ
      └─ before_performコールバック実行
      └─ performメソッド実行
      └─ after_performコールバック実行
      └─ 例外発生時はrescue_from/retry_on/discard_on処理
```

### フローチャート

```mermaid
flowchart TD
    A[ジョブクラス.perform_later] --> B{即時実行?}
    B -->|Yes| C[perform_now]
    B -->|No| D[enqueue]
    D --> E[before_enqueue callbacks]
    E --> F{scheduled_at設定?}
    F -->|Yes| G[queue_adapter.enqueue_at]
    F -->|No| H[queue_adapter.enqueue]
    G --> I[after_enqueue callbacks]
    H --> I
    I --> J[キューに保存]
    J --> K[ワーカーが取得]
    K --> C
    C --> L[引数デシリアライズ]
    L --> M[before_perform callbacks]
    M --> N[perform実行]
    N --> O{例外発生?}
    O -->|No| P[after_perform callbacks]
    O -->|Yes| Q{retry_on設定?}
    Q -->|Yes| R[retry_job]
    Q -->|No| S{discard_on設定?}
    S -->|Yes| T[ジョブ破棄]
    S -->|No| U[例外再発生]
    P --> V[完了]
    R --> D
    T --> V
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-001 | 引数シリアライズ | ジョブ引数はシリアライズ可能な型である必要がある | 常時 |
| BR-002 | GlobalID変換 | Active Recordオブジェクトは自動的にGlobalIDに変換される | Active Record使用時 |
| BR-003 | リトライ制限 | retry_onで指定された回数を超えるとリトライしない | retry_on設定時 |
| BR-004 | キュー名決定 | queue_asまたはsetで指定されたキュー名を使用 | 常時 |

### 計算ロジック

**リトライ待ち時間の計算（polynomially_longer）**：
```ruby
delay = executions ** 4 + 2
delay_jitter = delay * jitter * random
total_wait = delay + delay_jitter
```

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

Active Job自体はデータベースを直接操作しない。ただし、一部のキューイングアダプタ（Delayed Job等）はデータベースを使用する。

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

| 操作 | 対象テーブル | 操作種別 | 概要 |
|-----|-------------|---------|------|
| - | - | - | キューイングアダプタ依存 |

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| DeserializationError | 例外 | ジョブ引数のデシリアライズ失敗 | discard_onで破棄するか、リトライ |
| SerializationError | 例外 | ジョブ引数のシリアライズ失敗 | 引数の型を確認 |
| EnqueueError | 例外 | キューイングアダプタでのエンキュー失敗 | バックエンドの状態を確認 |
| NotImplementedError | 例外 | performメソッド未実装 | ジョブクラスでperformを実装 |

### リトライ仕様

- `retry_on`メソッドで例外クラスごとにリトライ設定可能
- デフォルトリトライ回数: 5回
- デフォルト待ち時間: 3秒
- 待ち時間アルゴリズム: `:polynomially_longer`（多項式的増加）
- ジッター: デフォルト15%（0.15）

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

- `enqueue_after_transaction_commit`オプションにより、Active Recordトランザクションコミット後にエンキュー可能
- トランザクション内でのperform_later呼び出しは、コミット後まで遅延される（設定時）

## パフォーマンス要件

- ジョブのシリアライズ/デシリアライズは高速に行う
- 大量の引数を持つジョブは避ける
- GlobalIDを使用してActive Recordオブジェクトを参照することで、シリアライズサイズを削減

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

- ジョブ引数にセンシティブな情報を直接含めない
- ジョブ実行時の権限チェックをアプリケーション側で実装
- デシリアライズ時のクラス名検証（UnknownJobClassError）

## 備考

- Active JobはRails 4.2で導入された
- 本番環境では非同期アダプタ（Sidekiq等）の使用を推奨
- テスト環境では`:test`アダプタを使用することで、ジョブの実行を制御可能

---

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

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

### 推奨読解順序

#### Step 1: データ構造を理解する

ジョブの属性とシリアライズ形式を理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | core.rb | `activejob/lib/active_job/core.rb` | ジョブの属性定義（job_id、arguments、queue_name等） |
| 1-2 | arguments.rb | `activejob/lib/active_job/arguments.rb` | 引数のシリアライズ/デシリアライズ処理 |

**読解のコツ**: `serialize`メソッドと`deserialize`メソッドの対応関係を確認する。ジョブデータはハッシュ形式でキューイングバックエンドに渡される。

#### Step 2: エントリーポイントを理解する

ジョブの作成と実行の起点を理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | base.rb | `activejob/lib/active_job/base.rb` | ActiveJob::Baseクラスの構成、includeされるモジュール一覧 |

**主要処理フロー**:
1. **63-77行目**: Baseクラスの定義、Core/QueueAdapter/Enqueuing/Execution等のinclude

#### Step 3: エンキュー処理を理解する

ジョブをキューに投入する処理の流れを理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | enqueuing.rb | `activejob/lib/active_job/enqueuing.rb` | perform_later、enqueueメソッドの実装 |

**主要処理フロー**:
- **81-88行目**: `perform_later`クラスメソッド、ジョブインスタンス化とenqueue呼び出し
- **112-123行目**: `enqueue`インスタンスメソッド、オプション設定とraw_enqueue呼び出し
- **126-142行目**: `raw_enqueue`、コールバック実行とアダプタ呼び出し

#### Step 4: 実行処理を理解する

ジョブの実行処理の流れを理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | execution.rb | `activejob/lib/active_job/execution.rb` | perform_now、executeメソッドの実装 |

**主要処理フロー**:
- **22-31行目**: クラスメソッド`perform_now`と`execute`
- **45-58行目**: インスタンスメソッド`perform_now`、実行回数カウントと例外処理

#### Step 5: コールバックとリトライを理解する

ジョブのライフサイクルとエラーハンドリングを理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 5-1 | callbacks.rb | `activejob/lib/active_job/callbacks.rb` | before/after/around_perform、before/after/around_enqueue |
| 5-2 | exceptions.rb | `activejob/lib/active_job/exceptions.rb` | retry_on、discard_on、retry_jobの実装 |

**主要処理フロー**（exceptions.rb）:
- **66-85行目**: `retry_on`メソッド、リトライ条件とバックオフ計算
- **111-119行目**: `discard_on`メソッド、例外時のジョブ破棄
- **160-167行目**: `retry_job`メソッド、リトライ実行

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

```
MyJob.perform_later(args)
    │
    ├─ ActiveJob::Enqueuing::ClassMethods#perform_later
    │      │
    │      ├─ job_or_instantiate (ジョブインスタンス化)
    │      │
    │      └─ ActiveJob::Enqueuing#enqueue
    │             │
    │             ├─ set (オプション適用)
    │             │
    │             └─ raw_enqueue
    │                    │
    │                    ├─ run_callbacks :enqueue
    │                    │      ├─ before_enqueue
    │                    │      ├─ around_enqueue
    │                    │      └─ after_enqueue
    │                    │
    │                    └─ _raw_enqueue
    │                           └─ queue_adapter.enqueue / enqueue_at
    │
    └─ [キューイングバックエンド]
           │
           └─ ActiveJob::Execution::ClassMethods#execute
                  │
                  ├─ deserialize (ジョブ復元)
                  │
                  └─ ActiveJob::Execution#perform_now
                         │
                         ├─ deserialize_arguments_if_needed
                         │
                         └─ _perform_job
                                │
                                ├─ run_callbacks :perform
                                │      ├─ before_perform
                                │      ├─ around_perform
                                │      └─ after_perform
                                │
                                └─ perform(*arguments)
```

### データフロー図

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

ジョブクラス ───▶ ActiveJob::Base.new ───▶ ジョブインスタンス
    │                                              │
    │              ┌─────────────────────┐         │
引数 ─────────────▶│  Arguments.serialize │─────────▶ シリアライズ済み引数
    │              └─────────────────────┘         │
    │                                              │
    └──────────────▶ enqueue ──────────────────────▶ キューアダプタ
                         │                              │
                         │                              ▼
                         │                         キューバックエンド
                         │                              │
                         │                              ▼
                         │              ┌─────────────────────┐
                         └─────────────▶│  Arguments.deserialize │
                                        └─────────────────────┘
                                                    │
                                                    ▼
                                               perform実行
                                                    │
                                                    ▼
                                               処理結果/例外
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| base.rb | `activejob/lib/active_job/base.rb` | ソース | ジョブ基底クラス |
| core.rb | `activejob/lib/active_job/core.rb` | ソース | ジョブの基本属性とシリアライズ |
| enqueuing.rb | `activejob/lib/active_job/enqueuing.rb` | ソース | エンキュー処理 |
| execution.rb | `activejob/lib/active_job/execution.rb` | ソース | ジョブ実行処理 |
| callbacks.rb | `activejob/lib/active_job/callbacks.rb` | ソース | ライフサイクルコールバック |
| exceptions.rb | `activejob/lib/active_job/exceptions.rb` | ソース | 例外処理とリトライ |
| queue_adapter.rb | `activejob/lib/active_job/queue_adapter.rb` | ソース | キューアダプタ設定 |
| queue_adapters.rb | `activejob/lib/active_job/queue_adapters.rb` | ソース | アダプタ一覧と機能比較 |
| queue_name.rb | `activejob/lib/active_job/queue_name.rb` | ソース | キュー名設定 |
| queue_priority.rb | `activejob/lib/active_job/queue_priority.rb` | ソース | 優先度設定 |
| arguments.rb | `activejob/lib/active_job/arguments.rb` | ソース | 引数シリアライズ |
| configured_job.rb | `activejob/lib/active_job/configured_job.rb` | ソース | オプション付きジョブ |
| async_adapter.rb | `activejob/lib/active_job/queue_adapters/async_adapter.rb` | ソース | 非同期アダプタ（開発用） |
| inline_adapter.rb | `activejob/lib/active_job/queue_adapters/inline_adapter.rb` | ソース | 同期アダプタ |
| test_adapter.rb | `activejob/lib/active_job/queue_adapters/test_adapter.rb` | ソース | テスト用アダプタ |
