# 機能設計書 22-ページキャッシュ

## 概要

本ドキュメントは、SQLiteのページキャッシュ（PCache）サブシステムの機能設計を記述する。ページキャッシュはデータベースページのメモリキャッシュ管理を担当し、ディスクI/Oの削減とパフォーマンス向上を実現する。

### 本機能の処理概要

ページキャッシュは、データベースから読み込んだページをメモリ上に保持し、同じページへの再アクセス時にディスクI/Oを省略することで、データベース操作の高速化を図る。

**業務上の目的・背景**：データベースアクセスにおいて、ディスクI/Oは最も時間のかかる操作である。ページキャッシュにより頻繁にアクセスされるページをメモリに保持することで、クエリ応答時間を大幅に短縮できる。また、ダーティページ（変更されたページ）の管理により、トランザクションのコミット時に効率的な書き込みが可能となる。

**機能の利用シーン**：
- データベースページの読み取り時（キャッシュヒット判定）
- ページ変更時のダーティフラグ管理
- メモリ圧迫時の不要ページ解放（LRUアルゴリズム）
- トランザクションコミット時のダーティページリスト取得

**主要な処理内容**：
1. ページのフェッチとピン留め（参照カウント管理）
2. ダーティページの追跡とリスト管理
3. LRU（Least Recently Used）アルゴリズムによるページ解放
4. キャッシュサイズとスピルサイズの設定管理
5. メモリ圧迫時のページスピル（ディスクへの書き出し）

**関連システム・外部連携**：ページャーからの要求を受け、ページを管理する。B-Treeモジュールは間接的にページキャッシュを利用する。

**権限による制御**：特になし（内部サブシステム）

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| - | - | - | 画面から直接利用されない内部サブシステム |

## 機能種別

メモリ管理 / キャッシュ制御

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| szPage | int | Yes | ページサイズ（バイト） | 正の整数 |
| szExtra | int | Yes | 追加領域サイズ | 0以上 |
| bPurgeable | int | Yes | パージ可能フラグ | 0または1 |
| xStress | function | No | メモリ圧迫時コールバック | NULL許容 |

### 入力データソース

- ページャーからのページフェッチ要求
- PRAGMA cache_size設定による容量変更

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| sqlite3_pcache_page* | ポインタ | ページオブジェクト |
| PgHdr* | ポインタ | ページヘッダ |

### 出力先

- ページャーへのページ提供
- メモリ圧迫時のストレスコールバック呼び出し

## 処理フロー

### 処理シーケンス

```
1. キャッシュ初期化 (sqlite3PcacheOpen)
   └─ 構造体初期化、ストレスコールバック設定

2. ページフェッチ (sqlite3PcacheFetch)
   └─ ハッシュテーブルから検索
   └─ 見つからなければ新規割り当て

3. ページピン留め完了 (sqlite3PcacheFetchFinish)
   └─ PgHdr初期化、参照カウント設定

4. ページ変更通知 (sqlite3PcacheMakeDirty)
   └─ ダーティリストへ追加

5. ページ解放 (sqlite3PcacheRelease)
   └─ 参照カウントデクリメント
   └─ LRUリストへ追加（ピン解除時）

6. キャッシュクリア (sqlite3PcacheClear)
   └─ 全ページ解放
```

### フローチャート

```mermaid
flowchart TD
    A[ページフェッチ要求] --> B{キャッシュに存在?}
    B -->|Yes| C[ピン留め・参照カウント増加]
    B -->|No| D{空きスロット有り?}
    D -->|Yes| E[新規ページ割り当て]
    D -->|No| F{パージ可能ページ有り?}
    F -->|Yes| G[LRU解放してから割り当て]
    F -->|No| H[スピル実行後再試行]
    E --> C
    G --> C
    H --> D
    C --> I[PgHdr返却]
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-22-01 | LRU解放順序 | 最も長く使われていないページから解放 | メモリ圧迫時 |
| BR-22-02 | ダーティページ保護 | ダーティページは書き出し前に解放不可 | 常時 |
| BR-22-03 | 参照カウント管理 | 参照カウント>0のページは解放不可 | 常時 |
| BR-22-04 | スピルサイズ | 指定サイズ以上のダーティページでスピル開始 | cache_spill有効時 |

### 計算ロジック

**ページアドレス計算**:
ページキャッシュラインは以下の構造:
```
[database page content] | [PgHdr1] | [MemPage] | [PgHdr]
```
sqlite3_pcache_page.pBuf はページコンテンツの先頭を指す。

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

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

| 操作 | 対象 | 操作種別 | 概要 |
|-----|-------------|---------|------|
| スピル | DBファイル | WRITE | ダーティページのディスク書き出し |

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

ページキャッシュはメモリ上のデータ構造であり、直接テーブル操作は行わない。

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| SQLITE_NOMEM | メモリ不足 | 新規ページ割り当て失敗 | スピル試行後再試行 |
| SQLITE_IOERR | I/Oエラー | スピル中の書き込み失敗 | エラー返却 |

### リトライ仕様

メモリ割り当て失敗時、sqlite3PcacheFetchStress()によりストレスコールバックを呼び出してメモリ解放を試みる。

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

- ダーティページリストはトランザクション単位で管理
- コミット時にsqlite3PcacheDirtyList()でダーティページを取得
- ロールバック時にsqlite3PcacheCleanAll()でダーティフラグをクリア

## パフォーマンス要件

- PRAGMA cache_size: キャッシュページ数（デフォルト-2000、約2MBに相当）
- PRAGMA cache_spill: スピル閾値（デフォルトはcache_sizeと同じ）
- ハッシュテーブルによるO(1)ページ検索

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

- キャッシュ上のページはメモリダンプで読み取り可能
- sqlite3_release_memory()で明示的にメモリ解放可能

## 備考

- SQLITE_CONFIG_PAGECACHEで静的メモリを事前割り当て可能
- pcache1.cはデフォルト実装、sqlite3_pcache_methods2で差し替え可能

---

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

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

### 推奨読解順序

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

まず、ページキャッシュの中核となるデータ構造を理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | pcache.h | `src/pcache.h` | PgHdr構造体（25-48行目）、API宣言 |
| 1-2 | pcache1.c | `src/pcache1.c` | PgHdr1構造体（117-127行目）、PCache1構造体（175-203行目） |

**読解のコツ**:
- PgHdr構造体のflags（PGHDR_CLEAN, PGHDR_DIRTY等）がページ状態を表す
- PgHdr1はpcache1.cの内部実装で使用されるページヘッダ
- pLruNext/pLruPrevがLRU双方向リストを構成

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | pcache.h | `src/pcache.h` | sqlite3PcacheFetch(), sqlite3PcacheRelease()の宣言 |
| 2-2 | pcache1.c | `src/pcache1.c` | pcache1Fetch()内部実装 |

**主要処理フロー**:
1. **94-96行目** (pcache.h): Fetch API宣言
2. **117-127行目** (pcache1.c): PgHdr1構造体、ページはハッシュテーブルで管理
3. **133-134行目**: PAGE_IS_PINNED/PAGE_IS_UNPINNEDマクロでピン状態判定

#### Step 3: メモリ管理を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | pcache1.c | `src/pcache1.c` | pcache1Alloc() - メモリ割り当て（341-374行目） |
| 3-2 | pcache1.c | `src/pcache1.c` | pcache1Free() - メモリ解放（379-400行目） |
| 3-3 | pcache1.c | `src/pcache1.c` | pcache1InitBulk() - バルク初期化（297-330行目） |

**主要処理フロー**:
- **271-291行目**: sqlite3PCacheBufferSetup() - SQLITE_CONFIG_PAGECACHE用バッファ設定
- **297-330行目**: pcache1InitBulk() - ローカルバルクメモリ初期化
- **341-374行目**: pcache1Alloc() - 3段階のメモリソース（設定済みバッファ→バルク→malloc）

#### Step 4: LRU管理を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | pcache1.c | `src/pcache1.c` | PGroup構造体（158-165行目）- LRUリストのアンカー |
| 4-2 | pcache1.c | `src/pcache1.c` | LRUリストの追加・削除操作 |

**主要処理フロー**:
- **158-165行目**: PGroup構造体、lruがリストのアンカー（isAnchor=1）
- pLruNext/pLruPrevによる双方向循環リスト

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

```
sqlite3PcacheFetch() [pcache.c]
    │
    ├─ pcache1Fetch() [pcache1.c]
    │      │
    │      ├─ ハッシュテーブル検索
    │      │
    │      └─ 見つからない場合
    │             │
    │             ├─ pcache1AllocPage()
    │             │      └─ pcache1Alloc()
    │             │             ├─ 設定済みバッファから
    │             │             └─ sqlite3Malloc()
    │             │
    │             └─ ハッシュテーブルへ登録
    │
    └─ sqlite3PcacheFetchFinish()
           └─ PgHdr初期化

sqlite3PcacheRelease() [pcache.c]
    │
    ├─ 参照カウントデクリメント
    │
    └─ ピン解除時
           └─ LRUリストへ追加
```

### データフロー図

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

ページ番号 ─────────▶ sqlite3PcacheFetch()
                            │
                   ハッシュテーブル検索
                            │
               ┌────────────┴────────────┐
               │                         │
          キャッシュヒット           キャッシュミス
               │                         │
          ピン留め                  新規割り当て
               │                         │
               └────────────┬────────────┘
                            │
                            ▼
                      PgHdr* ──────────────▶ ページャーへ

ダーティ通知 ───────▶ sqlite3PcacheMakeDirty()
                            │
                   ダーティリスト追加
                            │
                            ▼
                      ダーティフラグ設定

スピル要求 ─────────▶ xStress()
                            │
                   ダーティページ書き出し
                            │
                            ▼
                      メモリ解放 ───────▶ 新規割り当て可能
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| pcache.h | `src/pcache.h` | ヘッダ | 公開インターフェース定義 |
| pcache.c | `src/pcache.c` | ソース | キャッシュ抽象層実装 |
| pcache1.c | `src/pcache1.c` | ソース | デフォルトキャッシュ実装 |
| pager.c | `src/pager.c` | ソース | ページャー（キャッシュの利用者） |
| sqliteInt.h | `src/sqliteInt.h` | ヘッダ | sqlite3_pcache_page定義 |
