# 機能設計書 49-Semaphore

## 概要

本ドキュメントは、Symfony Semaphoreコンポーネントの機能設計を記述する。Semaphoreはセマフォによる並行制御機能を提供し、同時アクセス数を制限するためのカウンティングセマフォを実現するコンポーネントである。

### 本機能の処理概要

Semaphoreコンポーネントは、共有リソースへの同時アクセス数を制限するためのカウンティングセマフォ機能を提供する。Lockコンポーネントが排他ロック（1プロセスのみアクセス可能）であるのに対し、Semaphoreは指定した同時アクセス数（limit）までの並行アクセスを許可する。

**業務上の目的・背景**：アプリケーションにおいて、特定のリソースやサービスへの同時アクセス数を制限する必要がある場合がある。例えば、外部API呼び出しの同時接続数制限、データベース接続プールの管理、ファイル処理の並行数制限等。Lockコンポーネントの排他制御とは異なり、N個のプロセスが同時にアクセスできるセマフォ機構が求められる。

**機能の利用シーン**：外部APIへの同時リクエスト数制限、データベース接続の同時使用数制限、並行バッチ処理の同時実行数制御、リソース集約的な処理の並行度制限等。

**主要な処理内容**：
1. SemaphoreFactory::createSemaphore() - リソース名、制限数、重みからセマフォ生成
2. Semaphore::acquire() - セマフォの取得（利用可能スロットの確保）
3. Semaphore::refresh() - セマフォのTTL延長
4. Semaphore::release() - セマフォの解放（スロットの返却）
5. Semaphore::isAcquired() - セマフォ保持状態の確認
6. 自動リリース機構 - デストラクタによる自動解放

**関連システム・外部連携**：Redisストア等のバックエンドと連携する。

**権限による制御**：Semaphoreコンポーネント自体には権限制御機構はない。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| - | - | - | Semaphoreコンポーネントには関連画面はない |

## 機能種別

排他制御 / 並行制御

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| resource | string | Yes | セマフォ対象リソース名 | - |
| limit | int | Yes | 同時アクセス数上限 | 正の整数 |
| weight | int | No | 取得するスロット数（デフォルト: 1） | 正の整数 |
| ttlInSecond | float\|null | No | セマフォの最大持続時間（秒、デフォルト: 300.0） | - |
| autoRelease | bool | No | デストラクタでの自動解放（デフォルト: true） | - |

### 入力データソース

- アプリケーションコードからのSemaphoreFactory経由のセマフォ生成
- DIコンテナ経由のストア設定

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| bool (acquire) | bool | セマフォ取得の成否 |
| isAcquired | bool | セマフォ保持状態 |
| remainingLifetime | float\|null | 残り有効期間（秒） |
| isExpired | bool | セマフォ期限切れ状態 |

### 出力先

- バックエンドストア（セマフォ状態の永続化）
- アプリケーションコード（取得結果）

## 処理フロー

### 処理シーケンス

```
1. SemaphoreFactory::createSemaphore(resource, limit, weight, ttl, autoRelease)
   └─ new Key(resource, limit, weight)
   └─ new Semaphore(key, store, ttl, autoRelease)
2. Semaphore::acquire()
   └─ Key::resetLifetime()
   └─ store::save(key, ttlInSecond) でスロット確保
   └─ Key::reduceLifetime(ttlInSecond)
   └─ dirty = true
3. Semaphore::refresh(ttlInSecond)
   └─ store::putOffExpiration(key, ttlInSecond) で有効期限延長
   └─ Key::reduceLifetime(ttlInSecond)
4. Semaphore::release()
   └─ store::delete(key) でスロット返却
   └─ dirty = false
```

### フローチャート

```mermaid
flowchart TD
    A[createSemaphore resource, limit, weight] --> B[Semaphore::acquire]
    B --> C[store::save key, ttl]
    C --> D{成功?}
    D -->|Yes| E[dirty=true]
    D -->|No SemaphoreAcquiringException| F[return false]
    D -->|No その他例外| G[RuntimeException]
    E --> H[return true]
    H --> I[処理実行]
    I --> J{refresh必要?}
    J -->|Yes| K[store::putOffExpiration]
    J -->|No| L[Semaphore::release]
    K --> I
    L --> M[store::delete]
    M --> N[dirty=false]
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-49-01 | TTLデフォルト | デフォルトTTLは300.0秒（5分） | createSemaphore()のttl未指定時 |
| BR-49-02 | 自動リリース | autoRelease=trueの場合、デストラクタでセマフォ自動解放 | Semaphoreオブジェクト破棄時 |
| BR-49-03 | 重み付きスロット | weightパラメータにより、1回のacquireで複数スロットを消費可能 | Key生成時にweight指定 |
| BR-49-04 | シリアライズ不可 | Semaphoreオブジェクトはシリアライズ/デシリアライズ不可 | __serialize/__unserialize時にBadMethodCallException |

### 計算ロジック

特になし。スロット管理はストア実装に委譲される。

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

Semaphoreコンポーネント自体はデータベース操作を行わない。ストア実装に依存する。

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| - | SemaphoreAcquiringException | 利用可能スロットがない場合 | リトライまたは別タイミングで取得 |
| - | SemaphoreExpiredException | refresh時にセマフォが期限切れ | TTLを延長して再取得 |
| - | SemaphoreReleasingException | 解放中のエラー | ストアの接続状態を確認 |
| - | RuntimeException | 予期しないエラー | ストアの接続状態を確認 |
| - | InvalidArgumentException | refresh時にTTLが未設定 | TTLを指定する |
| - | BadMethodCallException | シリアライズを試みた場合 | Semaphoreをシリアライズしない |

### リトライ仕様

Semaphoreコンポーネントにはブロッキング取得機能はない。リトライが必要な場合はアプリケーション層で実装する。

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

該当なし。

## パフォーマンス要件

- セマフォの取得/解放はバックエンドストアの応答速度に依存
- カウンティングセマフォの実装はアトミック操作が必要

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

- バックエンドストアへのアクセス認証を適切に設定すること
- TTLの設定により、プロセスクラッシュ時のデッドロック（スロットリーク）を防止

## 備考

- Lockコンポーネントとの違い: Lockは排他（1プロセス）、Semaphoreは並行（N プロセス）
- Keyクラスにlimitとweightの概念が追加されている点がLock::Keyとの主な違い

---

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

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

### 推奨読解順序

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | Key.php | `src/Symfony/Component/Semaphore/Key.php` | セマフォキーのデータ構造（resource、limit、weight） |
| 1-2 | SemaphoreInterface.php | `src/Symfony/Component/Semaphore/SemaphoreInterface.php` | セマフォの基本インターフェース |

**読解のコツ**: Semaphore::KeyはLock::Keyと異なり、limit（同時アクセス上限）とweight（スロット消費数）のプロパティを持つ。

#### Step 2: ファクトリを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | SemaphoreFactory.php | `src/Symfony/Component/Semaphore/SemaphoreFactory.php` | セマフォ生成ファクトリ |

**主要処理フロー**:
- **37-39行目**: createSemaphore() - new Key(resource, limit, weight) + new Semaphore()
- **46-54行目**: createSemaphoreFromKey() - Loggerの設定を含む

#### Step 3: セマフォ本体を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | Semaphore.php | `src/Symfony/Component/Semaphore/Semaphore.php` | セマフォの主要実装 |

**主要処理フロー**:
- **64-84行目**: acquire() - store::save()でスロット確保、reduceLifetime()で有効期限設定
- **86-110行目**: refresh() - store::putOffExpiration()でTTL延長
- **112-115行目**: isAcquired() - store::exists()による確認
- **117-129行目**: release() - store::delete()でスロット返却
- **55-62行目**: デストラクタ - autoRelease=trueで自動解放

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

```
SemaphoreFactory::createSemaphore(resource, limit, weight, ttl)
    |
    ├─ new Key(resource, limit, weight)
    └─ new Semaphore(key, store, ttl, autoRelease)
           |
           ├─ acquire()
           |      ├─ Key::resetLifetime()
           |      ├─ store::save(key, ttl)
           |      └─ Key::reduceLifetime(ttl)
           |
           ├─ refresh(ttl)
           |      ├─ Key::resetLifetime()
           |      ├─ store::putOffExpiration(key, ttl)
           |      └─ Key::reduceLifetime(ttl)
           |
           ├─ isAcquired()
           |      └─ store::exists(key)
           |
           └─ release()
                  └─ store::delete(key)
```

### データフロー図

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

resource (string)    SemaphoreFactory                Semaphore
limit (int)          └─ Key + Semaphore 生成
weight (int)              |
                     Semaphore::acquire()            bool (取得成否)
                     └─ Store::save(key, ttl)
                          |
                     Semaphore::release()            void
                     └─ Store::delete(key)
                          |
                     バックエンドストア
                     └─ Redis等（アトミックカウンタ）
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| Semaphore.php | `src/Symfony/Component/Semaphore/Semaphore.php` | ソース | セマフォ主要実装 |
| SemaphoreFactory.php | `src/Symfony/Component/Semaphore/SemaphoreFactory.php` | ソース | セマフォ生成ファクトリ |
| SemaphoreInterface.php | `src/Symfony/Component/Semaphore/SemaphoreInterface.php` | ソース | セマフォインターフェース |
| Key.php | `src/Symfony/Component/Semaphore/Key.php` | ソース | セマフォキーデータ構造 |
| PersistingStoreInterface.php | `src/Symfony/Component/Semaphore/PersistingStoreInterface.php` | ソース | ストアインターフェース |
| Store/ | `src/Symfony/Component/Semaphore/Store/` | ソース | ストア実装 |
| Serializer/ | `src/Symfony/Component/Semaphore/Serializer/` | ソース | シリアライゼーション対応 |
