# 機能設計書 27-Server Timing

## 概要

本ドキュメントは、Ruby on RailsのServer Timing機能の設計仕様を定義する。Server Timingは、サーバーサイドのパフォーマンス計測情報をHTTPレスポンスヘッダーに付与し、ブラウザの開発者ツールで確認できるようにする機能である。

### 本機能の処理概要

**業務上の目的・背景**：Webアプリケーションのパフォーマンス最適化において、サーバーサイドの処理時間を把握することは重要である。Server Timingミドルウェアは、ActiveSupport::Notificationsでインストルメントされた各処理の実行時間を収集し、W3C Server-Timing仕様に準拠したHTTPヘッダーとして出力することで、ブラウザの開発者ツールからパフォーマンスメトリクスを確認可能にする。

**機能の利用シーン**：開発環境でのパフォーマンスチューニング、本番環境でのパフォーマンスモニタリング、ボトルネック箇所の特定など。ブラウザの開発者ツール（Network タブ）でサーバー側の処理時間を可視化できる。

**主要な処理内容**：
1. ActiveSupport::Notificationsへのサブスクライブ
2. リクエスト処理中のイベント収集
3. Server-Timingヘッダーの生成
4. HTTPレスポンスへのヘッダー付与

**関連システム・外部連携**：ActiveSupport::Notifications（イベント収集）、ブラウザ開発者ツール（結果表示）と連携する。

**権限による制御**：特になし。開発環境でデフォルト有効、本番環境では設定により有効化可能。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| - | ブラウザ開発者ツール | 主機能 | Server-Timingヘッダーの表示 |

## 機能種別

パフォーマンス計測 / ミドルウェア

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| event | ActiveSupport::Notifications::Event | Yes | インストルメントイベント | - |
| event.name | String | Yes | イベント名 | "!"で始まらないこと |
| event.duration | Float | Yes | 処理時間（ミリ秒） | - |

### 入力データソース

- ActiveSupport::Notifications からのイベント
- アプリケーション内の instrument 呼び出し

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| Server-Timing | String | Server-Timing HTTPヘッダー値 |

### 出力先

- HTTPレスポンスヘッダー（Server-Timing）

## 処理フロー

### 処理シーケンス

```
1. ミドルウェア初期化
   └─ Subscriberをインスタンス化
   └─ ActiveSupport::Notifications にサブスクライブ
2. リクエスト処理開始
   └─ collect_events でイベント収集開始
3. アプリケーション処理
   └─ 各処理のNotificationsイベントを収集
4. リクエスト処理終了
   └─ イベントを名前でグループ化
   └─ 各イベントの合計時間を計算
5. Server-Timingヘッダー生成
   └─ "event_name;dur=X.XX" 形式で結合
   └─ レスポンスヘッダーに設定
```

### フローチャート

```mermaid
flowchart TD
    A[call メソッド] --> B[collect_events 開始]
    B --> C[@app.call 実行]
    C --> D[イベント収集]
    D --> E[レスポンス取得]
    E --> F[イベントをグループ化]
    F --> G[Server-Timing ヘッダー生成]
    G --> H{既存ヘッダーあり?}
    H -->|Yes| I[既存ヘッダーに追加]
    H -->|No| J[新規ヘッダー設定]
    I --> K[レスポンス返却]
    J --> K
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-27-01 | イベントフィルタリング | "!"で始まるイベントは除外 | 内部イベント等 |
| BR-27-02 | 時間集計 | 同名イベントの時間を合計 | 複数回呼ばれたイベント |
| BR-27-03 | ヘッダー形式 | "name;dur=X.XX" 形式 | W3C Server-Timing仕様準拠 |
| BR-27-04 | 既存ヘッダー保持 | 既存のServer-Timingヘッダーがあれば先頭に追加 | ヘッダーが存在する場合 |

### 計算ロジック

Server-Timing ヘッダー値の生成：
```ruby
# イベント名と合計時間の組み合わせを生成
header_info = events.group_by(&:name).map do |event_name, events_collection|
  "%s;dur=%.2f" % [event_name, events_collection.sum(&:duration)]
end
headers["Server-Timing"] = header_info.join(", ")
```

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

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

| 操作 | 対象テーブル | 操作種別 | 概要 |
|-----|-------------|---------|------|
| - | - | - | データベース操作なし |

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

本機能はデータベースを直接操作しない。

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| - | - | 本機能は例外を発生させない | - |

### リトライ仕様

リトライ機構は提供されない。イベント収集に失敗した場合は空のヘッダーが返される。

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

本機能はデータベーストランザクションを使用しない。

## パフォーマンス要件

- イベント収集のオーバーヘッドは最小限
- ActiveSupport::IsolatedExecutionStateを使用してスレッドセーフに動作
- 開発環境ではデフォルト有効

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

- Server-Timingヘッダーにはパフォーマンス情報が含まれる
- 本番環境では必要に応じて無効化を検討
- サイドチャネル攻撃のリスクを認識すること

## 備考

- W3C Server-Timing仕様に準拠
- Chrome、Firefox、Safari等のモダンブラウザでサポート
- unsubscribeメソッドでサブスクリプション解除可能

---

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

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

### 推奨読解順序

#### Step 1: ミドルウェアの基本構造を理解する

ServerTimingミドルウェアのエントリーポイントを確認。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | server_timing.rb | `actionpack/lib/action_dispatch/middleware/server_timing.rb` | callメソッド、Subscriberクラス |

**読解のコツ**: ServerTimingはRackミドルウェアとして実装され、内部のSubscriberクラスがActiveSupport::Notificationsのイベントを収集する。

**主要処理フロー**:
- **52-56行目**: initialize でSubscriberを取得しサブスクライブ
- **58-76行目**: call メソッドでイベント収集とヘッダー生成

#### Step 2: Subscriberクラスを理解する

イベント収集の仕組みを確認。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | server_timing.rb | `actionpack/lib/action_dispatch/middleware/server_timing.rb` | Subscriber クラス |

**主要処理フロー**:
- **9-15行目**: Subscriber のシングルトンインスタンス、KEYの定義
- **17-21行目**: call メソッドでイベントを収集
- **23-30行目**: collect_events でイベント収集開始・終了
- **32-38行目**: ensure_subscribed でNotificationsにサブスクライブ

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

```
Rack ミドルウェアスタック
    │
    └─ ActionDispatch::ServerTiming#call (58行目)
           │
           ├─ @subscriber.collect_events (60行目)
           │      │
           │      ├─ ActiveSupport::IsolatedExecutionState[KEY] = []
           │      │
           │      └─ yield
           │             │
           │             └─ @app.call(env) (61行目)
           │                    │
           │                    └─ 各イベントで Subscriber#call が呼ばれる
           │
           ├─ events.group_by(&:name) (66行目)
           │
           └─ headers[SERVER_TIMING] = header_info.join(", ") (73行目)
```

### データフロー図

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

アプリケーション ───▶ instrument呼び出し ───▶ イベント発火
      │                    │
      │                    ▼
      │              ┌─────────────────┐
      │              │ ActiveSupport::  │
      │              │ Notifications   │───▶ イベント
      │              └─────────────────┘
      │                    │
      │                    ▼
      │              ┌─────────────────┐
      │              │ Subscriber      │───▶ イベントリスト
      │              │ (収集)          │
      │              └─────────────────┘
      │                    │
      │                    ▼
      └──────────────▶ ヘッダー生成 ───▶ Server-Timing ヘッダー
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| server_timing.rb | `actionpack/lib/action_dispatch/middleware/server_timing.rb` | ソース | ミドルウェア本体 |
| constants.rb | `actionpack/lib/action_dispatch/constants.rb` | ソース | SERVER_TIMING定数定義 |
| notifications.rb | `activesupport/lib/active_support/notifications.rb` | ソース | イベント通知基盤 |
| isolated_execution_state.rb | `activesupport/lib/active_support/isolated_execution_state.rb` | ソース | スレッドローカル状態管理 |
