# 機能設計書 35-Microsoft.Extensions.Caching.Memory

## 概要

本ドキュメントは、Microsoft.Extensions.Caching.Memory（メモリキャッシュ）機能の設計内容を記述する。この機能は.NETアプリケーションにおけるインメモリキャッシュ機能を提供し、頻繁にアクセスされるデータの高速な保存・取得を実現する。

### 本機能の処理概要

Microsoft.Extensions.Caching.Memoryは、アプリケーションプロセス内のメモリにデータをキャッシュするフレームワークである。キーと値のペアを保存し、有効期限管理、サイズ制限、優先度ベースのエビクション（追い出し）を提供する。

**業務上の目的・背景**：データベースや外部サービスへのアクセスは、レスポンス時間とリソース消費の両面でコストがかかる。このライブラリは、頻繁にアクセスされるデータをメモリ上にキャッシュすることで、アプリケーションのパフォーマンスを大幅に向上させる。特にWebアプリケーションでは、リクエストごとのデータ取得コストを削減できる。

**機能の利用シーン**：データベースクエリ結果のキャッシュ、外部API応答のキャッシュ、計算コストの高い処理結果のキャッシュ、セッションデータの一時保存など、パフォーマンス最適化が必要なシナリオで使用される。

**主要な処理内容**：
1. IMemoryCacheを通じたキャッシュエントリの作成・取得・削除
2. 絶対有効期限・スライディング有効期限による自動エビクション
3. サイズ制限に基づくキャッシュコンパクション
4. 優先度（Low/Normal/High/NeverRemove）によるエビクション制御
5. 変更トークンによるエントリの無効化
6. キャッシュ統計の収集

**関連システム・外部連携**：Microsoft.Extensions.Caching.Abstractions、Microsoft.Extensions.Options、Microsoft.Extensions.Loggingと連携する。Redis等の分散キャッシュへの移行パスも提供する。

**権限による制御**：キャッシュ自体に権限制御はないが、キャッシュするデータのアクセス権限は別途管理が必要。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| - | - | - | 画面機能マッピングに該当なし |

## 機能種別

データ連携 / キャッシュ管理

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| key | object | Yes | キャッシュキー | null不可 |
| value | object | Yes | キャッシュ値 | - |
| absoluteExpiration | DateTimeOffset | No | 絶対有効期限 | - |
| absoluteExpirationRelativeToNow | TimeSpan | No | 現在からの相対有効期限 | - |
| slidingExpiration | TimeSpan | No | スライディング有効期限 | - |
| priority | CacheItemPriority | No | 優先度 | Low/Normal/High/NeverRemove |
| size | long | No | エントリサイズ | SizeLimit設定時は必須 |

### 入力データソース

- アプリケーションコードからのSet/GetOrCreate呼び出し
- MemoryCacheOptionsによる設定

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| CachedValue | object | キャッシュされた値 |
| CacheEntry | ICacheEntry | キャッシュエントリ情報 |
| MemoryCacheStatistics | MemoryCacheStatistics | キャッシュ統計 |

### 出力先

- アプリケーションのメモリ
- 呼び出し元への値返却

## 処理フロー

### 処理シーケンス

```
1. キャッシュ初期化フェーズ
   └─ MemoryCacheインスタンス作成、オプション設定
2. エントリ作成フェーズ
   └─ CreateEntry()またはSet()でエントリ作成
3. 値設定フェーズ
   └─ 有効期限、優先度、サイズの設定
4. エントリ保存フェーズ
   └─ SetEntry()でCoherentStateに保存
5. 値取得フェーズ
   └─ TryGetValue()でキャッシュから取得
6. エビクションフェーズ（バックグラウンド）
   └─ 期限切れスキャン、容量超過時のコンパクション
```

### フローチャート

```mermaid
flowchart TD
    A[MemoryCache作成] --> B[オプション設定]
    B --> C{キャッシュ操作}
    C -->|Set/GetOrCreate| D[CreateEntry]
    D --> E[有効期限設定]
    E --> F[サイズチェック]
    F --> G{容量OK?}
    G -->|Yes| H[CoherentStateに保存]
    G -->|No| I[コンパクション実行]
    I --> H
    C -->|TryGetValue| J{キーが存在?}
    J -->|Yes| K{期限切れ?}
    K -->|No| L[LastAccessed更新]
    L --> M[値を返却]
    K -->|Yes| N[エントリ削除]
    N --> O[null返却]
    J -->|No| O
    C -->|Remove| P[エントリ削除]
    P --> Q[エビクションコールバック呼び出し]
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-35-01 | スライディング有効期限 | アクセスごとに有効期限をリセット | SlidingExpiration設定時 |
| BR-35-02 | 絶対有効期限優先 | AbsoluteExpirationがSlidingより優先 | 両方設定時 |
| BR-35-03 | LRUエビクション | 最も古いアクセスのエントリから削除 | コンパクション時 |
| BR-35-04 | 優先度順エビクション | Low→Normal→Highの順に削除対象 | コンパクション時 |
| BR-35-05 | NeverRemove保護 | NeverRemove優先度のエントリは削除されない | コンパクション時 |

### 計算ロジック

コンパクション対象選定順序：
1. 期限切れエントリをすべて削除
2. Low優先度エントリをLRU順で削除
3. Normal優先度エントリをLRU順で削除
4. High優先度エントリをLRU順で削除
5. 目標サイズに達するまで繰り返し

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

該当なし（インメモリ操作のみ）

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| - | ObjectDisposedException | 破棄済みキャッシュへのアクセス | 新しいインスタンスを使用 |
| - | InvalidOperationException | SizeLimit設定時にSizeなしでSet | Sizeを設定する |

### リトライ仕様

リトライなし（同期的なインメモリ操作）

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

該当なし

## パフォーマンス要件

- 読み取り: O(1)（ConcurrentDictionaryベース）
- 書き込み: O(1)（ConcurrentDictionaryベース）
- コンパクション: O(n)（全エントリをスキャン）
- 期限切れスキャン: ExpirationScanFrequency間隔でバックグラウンド実行

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

- 機密データのキャッシュは最小限に
- キャッシュはプロセス内でのみ有効（プロセス間共有なし）
- メモリダンプからの情報漏洩に注意

## 備考

- .NET 9でReadOnlySpan<char>キーのオーバーロードが追加され、文字列キーの割り当てを削減可能
- TrackStatisticsオプションでキャッシュヒット率等の統計を収集可能
- TrackLinkedCacheEntriesで依存関係のあるエントリを連動して無効化可能

---

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

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

### 推奨読解順序

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

キャッシュシステムの基本的なインターフェースとデータ構造を理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | IMemoryCache.cs | `src/libraries/Microsoft.Extensions.Caching.Abstractions/src/IMemoryCache.cs` | キャッシュインターフェース。TryGetValue、CreateEntry、Remove |
| 1-2 | ICacheEntry.cs | `src/libraries/Microsoft.Extensions.Caching.Abstractions/src/ICacheEntry.cs` | エントリインターフェース。Key、Value、有効期限、優先度 |
| 1-3 | CacheItemPriority.cs | `src/libraries/Microsoft.Extensions.Caching.Abstractions/src/CacheItemPriority.cs` | 優先度列挙型 |

**読解のコツ**: IMemoryCache→ICacheEntry→CacheItemPriorityの関係を理解する。キャッシュはエントリを管理し、エントリは優先度を持つ。

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

MemoryCacheの基本操作を理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | MemoryCache.cs | `src/libraries/Microsoft.Extensions.Caching.Memory/src/MemoryCache.cs` | キャッシュ実装。CreateEntry、TryGetValue、SetEntry |
| 2-2 | MemoryCacheOptions.cs | `src/libraries/Microsoft.Extensions.Caching.Memory/src/MemoryCacheOptions.cs` | キャッシュオプション。SizeLimit、ExpirationScanFrequency |

**主要処理フロー**:
1. **99-105行目**: CreateEntry()でCacheEntryを作成
2. **107-202行目**: SetEntry()でエントリを保存（サイズチェック、既存エントリの置換）
3. **205-213行目**: TryGetValue()でキャッシュから値を取得
4. **264-316行目**: PostProcessTryGetValue()で期限切れチェック、LastAccessed更新

#### Step 3: キャッシュストレージを理解する

内部データ構造（CoherentState）を理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | MemoryCache.cs | `src/libraries/Microsoft.Extensions.Caching.Memory/src/MemoryCache.cs` | CoherentState内部クラス（714-825行目） |

**主要処理フロー**:
- **714-825行目**: CoherentStateがConcurrentDictionaryを2つ（string用、object用）保持
- **TryGetValue/TryAdd/TryUpdate/TryRemove**: キー型に応じて適切なDictionaryを使用
- **GetAllValues()**: 全エントリを列挙（コンパクション、クリア用）

#### Step 4: エビクションを理解する

期限切れスキャンとコンパクションの仕組みを理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | MemoryCache.cs | `src/libraries/Microsoft.Extensions.Caching.Memory/src/MemoryCache.cs` | ScanForExpiredItems、Compact |

**主要処理フロー**:
- **387-400行目**: StartScanForExpiredItemsIfNeeded()でバックグラウンドスキャンをスケジュール
- **464-477行目**: ScanForExpiredItems()で期限切れエントリを削除
- **575-661行目**: Compact()でLRU順、優先度順にエントリを削除

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

```
MemoryCache.CreateEntry(key)
    │
    └─ new CacheEntry(key, this)

CacheEntry.Dispose()
    │
    └─ MemoryCache.SetEntry(entry)
           │
           ├─ CheckExpired() → 期限切れなら終了
           │
           ├─ UpdateCacheSizeExceedsCapacity()
           │      │
           │      └─ 容量超過時はTriggerOvercapacityCompaction()
           │
           └─ CoherentState.TryAdd/TryUpdate()

MemoryCache.TryGetValue(key, out result)
    │
    └─ CoherentState.TryGetValue(key, out entry)
           │
           └─ PostProcessTryGetValue()
                  │
                  ├─ CheckExpired() → 期限切れならRemoveEntry()
                  │
                  └─ LastAccessed更新、統計更新
```

### データフロー図

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

Set(key, value) ───▶ CreateEntry() ───▶ CacheEntry設定
                                              │
                                              ▼
                                        CacheEntry.Dispose()
                                              │
                                              ▼
                                        SetEntry()
                                              │
                                              ▼
                                        CoherentState.TryAdd()
                                              │
                                              ▼
                              ConcurrentDictionary<string, CacheEntry>
                              ConcurrentDictionary<object, CacheEntry>

TryGetValue(key) ───▶ CoherentState.TryGetValue() ───▶ CacheEntry
                                                            │
                                                            ▼
                                                   PostProcessTryGetValue()
                                                            │
                                                            ▼
                                                   値またはnull
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| MemoryCache.cs | `src/libraries/Microsoft.Extensions.Caching.Memory/src/MemoryCache.cs` | ソース | キャッシュ実装 |
| CacheEntry.cs | `src/libraries/Microsoft.Extensions.Caching.Memory/src/CacheEntry.cs` | ソース | エントリ実装 |
| MemoryCacheOptions.cs | `src/libraries/Microsoft.Extensions.Caching.Memory/src/MemoryCacheOptions.cs` | ソース | オプション |
| MemoryCacheStatistics.cs | `src/libraries/Microsoft.Extensions.Caching.Memory/src/MemoryCacheStatistics.cs` | ソース | 統計情報 |
| EvictionReason.cs | `src/libraries/Microsoft.Extensions.Caching.Abstractions/src/EvictionReason.cs` | ソース | エビクション理由 |
| PostEvictionCallbackRegistration.cs | `src/libraries/Microsoft.Extensions.Caching.Memory/src/PostEvictionCallbackRegistration.cs` | ソース | コールバック登録 |
| CacheExtensions.cs | `src/libraries/Microsoft.Extensions.Caching.Memory/src/CacheExtensions.cs` | ソース | 拡張メソッド |
