# 機能設計書 21-Fragment Caching

## 概要

本ドキュメントは、Ruby on Railsのフラグメントキャッシュ機能の設計仕様を定義する。フラグメントキャッシュは、ビューの一部分をキャッシュしてレスポンス時間を短縮するための機能である。

### 本機能の処理概要

**業務上の目的・背景**：Webアプリケーションにおいて、ページ全体ではなく特定のビュー部分のみを高速化したい場合がある。例えば、ナビゲーションメニュー、サイドバー、頻繁に参照されるリスト表示など、更新頻度が低いがレンダリングコストが高い部分をキャッシュすることで、全体のレスポンス時間を大幅に改善できる。フラグメントキャッシュは、この「部分的なキャッシュ」という要求を満たすために設計された機能である。

**機能の利用シーン**：動的なWebページにおいて、データベースクエリを伴う複雑なビュー部分をキャッシュしたい場合に使用する。具体的には、商品一覧、ニュースフィード、コメントリスト、統計情報の表示など、計算コストが高いが即時性が必須でない部分に適用する。

**主要な処理内容**：
1. ビューテンプレート内でcacheヘルパーを呼び出し、キャッシュ対象のブロックを指定
2. キャッシュキーの生成（モデルオブジェクト、テンプレートダイジェスト、依存関係を考慮）
3. キャッシュストアからの読み取り（キャッシュヒット時）
4. キャッシュミス時のブロック実行とキャッシュへの書き込み
5. キャッシュの手動無効化（expire_fragment）

**関連システム・外部連携**：キャッシュストア（MemoryStore、FileStore、MemCacheStore、RedisCacheStore）と連携する。ActiveRecord モデルのcache_keyメソッドと連携してキーベースのキャッシュ有効期限管理を実現する。

**権限による制御**：特になし。アプリケーション設定（perform_caching）によりキャッシュ機能全体のON/OFFを制御可能。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| - | 任意のビュー | 主機能 | cacheヘルパーによるビュー部分のキャッシュ処理 |

## 機能種別

キャッシュ処理

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| name | Object/Array/Hash | No | キャッシュキーの基となるオブジェクト | cache_keyまたはto_paramに応答すること |
| options[:skip_digest] | Boolean | No | テンプレートダイジェストをスキップするか | true/false |
| options[:expires_in] | Duration | No | キャッシュの有効期限 | 正の数値 |
| options[:race_condition_ttl] | Duration | No | 競合状態回避のための追加TTL | 正の数値 |

### 入力データソース

- ビューテンプレート内のcacheヘルパー呼び出し
- キャッシュ対象のActiveRecordモデルインスタンス
- コントローラで設定されたインスタンス変数

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| cached_content | String | キャッシュされた/レンダリングされたHTMLフラグメント |

### 出力先

- HTTPレスポンスボディ（ビューの一部として）
- キャッシュストア（MemoryStore、FileStore、Redis等）

## 処理フロー

### 処理シーケンス

```
1. cacheヘルパー呼び出し
   └─ perform_cachingが有効か確認
2. キャッシュキー生成
   └─ cache_fragment_name でキー名を決定
   └─ テンプレートダイジェストを計算（skip_digestでない場合）
3. キャッシュ読み取り試行
   └─ read_fragment でキャッシュストアから取得
4. キャッシュヒット時
   └─ キャッシュ内容をそのまま返却
5. キャッシュミス時
   └─ ブロックを実行してコンテンツを生成
   └─ write_fragment でキャッシュストアに保存
   └─ 生成したコンテンツを返却
```

### フローチャート

```mermaid
flowchart TD
    A[cache ヘルパー呼び出し] --> B{perform_caching?}
    B -->|No| C[ブロックを直接実行]
    B -->|Yes| D[キャッシュキー生成]
    D --> E[fragment_for 呼び出し]
    E --> F[read_fragment_for]
    F --> G{キャッシュヒット?}
    G -->|Yes| H[キャッシュ内容を返却]
    G -->|No| I[ブロックを実行]
    I --> J[write_fragment_for]
    J --> K[キャッシュに保存]
    K --> L[コンテンツを返却]
    C --> M[終了]
    H --> M
    L --> M
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-21-01 | キャッシュキー生成 | モデルのcache_key_with_versionを優先使用 | モデルがcache_key_with_versionに応答する場合 |
| BR-21-02 | テンプレートダイジェスト | テンプレートファイルのMD5ハッシュをキーに含める | skip_digest: trueでない場合 |
| BR-21-03 | 依存関係追跡 | パーシャルの依存関係を自動検出してダイジェストに含める | renderヘルパー呼び出しが検出可能な場合 |

### 計算ロジック

キャッシュキー構成：
```
views/{template_path}:{template_digest}/{model_class}/{model_id}
```

テンプレートダイジェスト計算：
```ruby
Digestor.digest(name: template.virtual_path, format: template.format, finder: lookup_context, dependencies: view_cache_dependencies)
```

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

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

| 操作 | 対象テーブル | 操作種別 | 概要 |
|-----|-------------|---------|------|
| - | - | - | データベース操作なし（キャッシュストアのみ） |

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

本機能はデータベースを直接操作しない。キャッシュストア（Memory、File、Memcached、Redis等）に対してread/write/delete操作を行う。

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| UncacheableFragmentError | RuntimeError | uncacheable!がcacheブロック内で呼ばれた | キャッシュ不可能なヘルパーをブロック外に移動 |
| DeserializationError | StandardError | キャッシュエントリのデシリアライズ失敗 | キャッシュミスとして扱い再生成 |

### リトライ仕様

キャッシュ読み取り失敗時は自動的にキャッシュミスとして扱い、コンテンツを再生成する。

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

本機能はデータベーストランザクションを使用しない。キャッシュストアの操作はアトミックではないが、race_condition_ttlオプションで競合状態を軽減可能。

## パフォーマンス要件

- キャッシュヒット時：1ms以下のレスポンス向上が期待される
- テンプレートダイジェスト計算：初回のみ計算、以降はメモリキャッシュ

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

- ユーザー固有の情報をキャッシュする場合は、キャッシュキーにユーザー識別子を含めること
- 機密情報はキャッシュに含めないこと
- キャッシュストアへのアクセス制御を適切に設定すること

## 備考

- コレクションキャッシュ（cached: true オプション）と併用することで、一括読み取りによる高速化が可能
- cache_if / cache_unless で条件付きキャッシュが可能

---

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

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

### 推奨読解順序

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

まず、キャッシュエントリの構造とキャッシュキーの生成ロジックを理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | cache.rb | `activesupport/lib/active_support/cache.rb` | ActiveSupport::Cache::Entry クラスの構造、キャッシュストアの基本インターフェース |

**読解のコツ**: ActiveSupport::Cache::Store クラスのfetch、read、writeメソッドが基本操作。expandedキーの生成ロジック（113-122行目）に注目。

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

ビューテンプレートから呼び出されるcacheヘルパーの実装を確認。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | cache_helper.rb | `actionview/lib/action_view/helpers/cache_helper.rb` | cacheメソッドの引数とオプション、キャッシュキー生成の委譲 |

**主要処理フロー**:
1. **176-187行目**: cacheメソッドのエントリーポイント、perform_cachingの確認
2. **248-254行目**: cache_fragment_nameでキャッシュキー名を生成
3. **278-286行目**: fragment_forでキャッシュ読み取りまたは書き込み

#### Step 3: コントローラ層のキャッシュ処理を理解する

コントローラでのフラグメントキャッシュ操作（read_fragment、write_fragment、expire_fragment）を確認。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | fragments.rb | `actionpack/lib/abstract_controller/caching/fragments.rb` | フラグメントキャッシュの読み書き削除操作 |

**主要処理フロー**:
- **68-76行目**: combined_fragment_cache_keyでフルキーを構築
- **80-89行目**: write_fragmentでキャッシュ書き込み
- **93-101行目**: read_fragmentでキャッシュ読み取り
- **131-142行目**: expire_fragmentでキャッシュ削除

#### Step 4: キャッシュ設定を理解する

コントローラのキャッシュ設定とキャッシュストアの設定を確認。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | caching.rb | `actionpack/lib/abstract_controller/caching.rb` | perform_caching設定、cache_store設定、view_cache_dependencies |

**主要処理フロー**:
- **14-27行目**: ConfigMethodsモジュールでcache_storeアクセサを定義
- **39-41行目**: perform_caching設定（デフォルトtrue）

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

```
View Template (ERBテンプレート)
    │
    ├─ ActionView::Helpers::CacheHelper#cache (176行目)
    │      │
    │      ├─ CachingRegistry.track_caching (178行目)
    │      │
    │      ├─ cache_fragment_name (180行目)
    │      │      └─ fragment_name_with_digest (267行目)
    │      │             └─ Digestor.digest (257行目)
    │      │
    │      └─ fragment_for (180行目)
    │             │
    │             ├─ read_fragment_for (288-290行目)
    │             │      └─ controller.read_fragment
    │             │             └─ AbstractController::Caching::Fragments#read_fragment (93行目)
    │             │                    └─ cache_store.read
    │             │
    │             └─ write_fragment_for (292-295行目)
    │                    └─ controller.write_fragment
    │                           └─ AbstractController::Caching::Fragments#write_fragment (80行目)
    │                                  └─ cache_store.write
    │
    └─ ActiveSupport::Cache::Store
           ├─ #fetch (452行目)
           ├─ #read (506行目)
           └─ #write (672行目)
```

### データフロー図

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

ERBテンプレート ───▶ cache ヘルパー ───▶ HTMLフラグメント
      │                    │
      │                    ▼
モデルオブジェクト ───▶ キャッシュキー生成
      │                    │
      │                    ▼
      │              ┌─────────────────┐
      │              │ キャッシュストア │
      │              │ (Memory/Redis等) │
      │              └─────────────────┘
      │                    │
      │                    ▼
      └──────────────▶ キャッシュ読取/書込
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| cache_helper.rb | `actionview/lib/action_view/helpers/cache_helper.rb` | ソース | ビューヘルパーのcacheメソッド実装 |
| fragments.rb | `actionpack/lib/abstract_controller/caching/fragments.rb` | ソース | フラグメントキャッシュのread/write/expire |
| caching.rb | `actionpack/lib/abstract_controller/caching.rb` | ソース | キャッシュ設定とConfigMethods |
| cache.rb | `activesupport/lib/active_support/cache.rb` | ソース | キャッシュストアの基底クラス |
| memory_store.rb | `activesupport/lib/active_support/cache/memory_store.rb` | ソース | メモリキャッシュストア実装 |
| redis_cache_store.rb | `activesupport/lib/active_support/cache/redis_cache_store.rb` | ソース | Redisキャッシュストア実装 |
| digestor.rb | `actionview/lib/action_view/digestor.rb` | ソース | テンプレートダイジェスト計算 |
