# 機能設計書 48-Lock

## 概要

本ドキュメントは、Symfony Lockコンポーネントの機能設計を記述する。Lockは共有リソースへの排他的アクセスを提供するロック機構を提供し、Flock、Redis、Memcached、PDO、PostgreSQL、MongoDB等の多様なストアに対応するコンポーネントである。

### 本機能の処理概要

Lockコンポーネントは、分散システムにおける共有リソースの排他制御を実現する。LockFactoryを通じてロックインスタンスを生成し、acquire/releaseのライフサイクルでリソースの排他的アクセスを管理する。共有ロック（読み取りロック）にも対応する。

**業務上の目的・背景**：マイクロサービスアーキテクチャや分散システムでは、同一リソースへの同時アクセスを制御する必要がある。クーポンの二重適用防止、在庫の過剰引き当て防止、バッチ処理の重複実行防止等の排他制御要件に対応する。

**機能の利用シーン**：分散環境でのクリティカルセクション保護、バッチジョブの重複実行防止、APIレートリミッターのトークン管理、ファイル書き込みの排他制御、データベースマイグレーションの同期制御等。

**主要な処理内容**：
1. LockFactory::createLock() - リソース名からロックインスタンスを生成
2. Lock::acquire() - ロックの取得（ブロッキング/非ブロッキング）
3. Lock::acquireRead() - 共有ロック（読み取りロック）の取得
4. Lock::refresh() - ロックのTTL延長
5. Lock::release() - ロックの解放
6. Lock::isAcquired() - ロック状態の確認
7. 自動リリース機構 - デストラクタによる自動ロック解放

**関連システム・外部連携**：Redis、Memcached、PostgreSQL、MongoDB、PDO等のバックエンドストアと連携する。RateLimiterコンポーネントのトークン管理でも使用される。

**権限による制御**：Lockコンポーネント自体には権限制御機構はない。ロックのアクセス制御はバックエンドストアの設定に依存する。

## 関連画面

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

## 機能種別

排他制御 / 同期制御

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| resource | string | Yes | ロック対象リソース名 | - |
| ttl | float\|null | No | ロックの最大持続時間（秒、デフォルト: 300.0） | - |
| autoRelease | bool | No | デストラクタでの自動解放（デフォルト: true） | - |
| blocking | bool | No | ブロッキング取得（デフォルト: false） | - |

### 入力データソース

- アプリケーションコードからのLockFactory経由のロック生成
- DIコンテナ経由のストア設定

## 出力仕様

### 出力データ

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

### 出力先

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

## 処理フロー

### 処理シーケンス

```
1. LockFactory::createLock(resource, ttl, autoRelease)
   └─ new Key(resource) でロックキー生成
   └─ new Lock(key, store, ttl, autoRelease)
2. Lock::acquire(blocking)
   └─ Key::resetLifetime() でライフタイムリセット
   └─ blocking=true かつ BlockingStoreInterface
      └─ store::waitAndSave(key)
   └─ blocking=true かつ 非BlockingStore
      └─ store::save(key) を100msリトライ（ジッター±10ms付き）
   └─ blocking=false
      └─ store::save(key)
   └─ TTL設定時はrefresh()呼び出し
   └─ 期限切れチェック
3. Lock::refresh(ttl)
   └─ store::putOffExpiration(key, ttl) で有効期限延長
4. Lock::release()
   └─ store::delete(key) でロック解放
   └─ store::exists(key) で解放確認
```

### フローチャート

```mermaid
flowchart TD
    A[createLock resource, ttl] --> B[Lock::acquire blocking]
    B --> C{blocking?}
    C -->|Yes| D{BlockingStore?}
    D -->|Yes| E[waitAndSave]
    D -->|No| F[save + 100msリトライループ]
    C -->|No| G[save]
    E --> H{成功?}
    F --> H
    G --> H
    H -->|Yes| I[dirty=true]
    H -->|No| J{blocking?}
    J -->|Yes| K[LockConflictedException]
    J -->|No| L[return false]
    I --> M{TTL設定?}
    M -->|Yes| N[refresh]
    M -->|No| O{期限切れ?}
    N --> O
    O -->|Yes| P[release + LockExpiredException]
    O -->|No| Q[return true]
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-48-01 | TTLデフォルト | デフォルトTTLは300.0秒（5分） | createLock()のttl未指定時 |
| BR-48-02 | 自動リリース | autoRelease=trueの場合、デストラクタでロック自動解放 | Lockオブジェクト破棄時 |
| BR-48-03 | ブロッキングリトライ | 非BlockingStoreでblocking=true時、100ms±10msのジッター付きリトライ | acquire(true)時 |
| BR-48-04 | 共有ロックフォールバック | SharedLockStoreInterface非対応ストアの場合、排他ロックにフォールバック | acquireRead()時 |
| BR-48-05 | シリアライズ不可 | Lockオブジェクトはシリアライズ/デシリアライズ不可 | __serialize/__unserialize時にBadMethodCallException |

### 計算ロジック

ブロッキングリトライ間隔: `usleep((100 + random_int(-10, 10)) * 1000)` = 90ms〜110ms（**Lock.php 78行目**）

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

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

| 操作 | 対象テーブル | 操作種別 | 概要 |
|-----|-------------|---------|------|
| save | lock_keys（PDO/Doctrine） | INSERT/UPDATE | ロック状態の保存 |
| putOffExpiration | lock_keys | UPDATE | ロック有効期限の延長 |
| delete | lock_keys | DELETE | ロック状態の削除 |
| exists | lock_keys | SELECT | ロック状態の確認 |

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| - | LockConflictedException | 他プロセスがロックを保持中 | リトライまたは別のタイミングで取得 |
| - | LockExpiredException | ロック保存後に即座に期限切れ | TTLを延長する |
| - | LockAcquiringException | ロック取得中の予期しないエラー | バックエンドストアの接続を確認 |
| - | LockReleasingException | ロック解放中のエラー | バックエンドストアの状態を確認 |
| - | InvalidArgumentException | refresh時にTTLが未設定 | TTLを指定する |
| - | BadMethodCallException | シリアライズを試みた場合 | Lockオブジェクトをシリアライズしない |

### リトライ仕様

blocking=trueの場合、非BlockingStoreでは100ms±10msの間隔で自動リトライする。BlockingStoreではwaitAndSave()でストア側がリトライを管理する。

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

Lockコンポーネント自体はトランザクション管理を行わない。PDO/Doctrine DBALストア使用時は、ストア内部でトランザクションが使用される場合がある。

## パフォーマンス要件

- ロック取得/解放はバックエンドストアの応答速度に依存
- FlockStoreはローカルファイルシステムベースで最も低レイテンシ
- RedisStoreは分散環境で一般的に使用される

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

- ロックリソース名に機密情報を含めないこと
- バックエンドストアへのアクセス認証を適切に設定すること
- TTLの設定により、プロセスクラッシュ時のデッドロックを防止

## 備考

- StoreFactoryクラスでDSN文字列からストアインスタンスを生成可能
- CombinedStoreでコンセンサスベースの分散ロック（Redlockアルゴリズム類似）を実現可能
- Serializerディレクトリでロック状態のシリアライゼーションをサポート

---

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

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

### 推奨読解順序

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | Key.php | `src/Symfony/Component/Lock/Key.php` | ロックキーのデータ構造（resource、state、expiringTime） |
| 1-2 | LockInterface.php | `src/Symfony/Component/Lock/LockInterface.php` | ロックの基本インターフェース |
| 1-3 | SharedLockInterface.php | `src/Symfony/Component/Lock/SharedLockInterface.php` | 共有ロック拡張インターフェース |
| 1-4 | PersistingStoreInterface.php | `src/Symfony/Component/Lock/PersistingStoreInterface.php` | ストアの基本インターフェース |

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | LockFactory.php | `src/Symfony/Component/Lock/LockFactory.php` | ロック生成ファクトリ |

**主要処理フロー**:
- **39-42行目**: createLock() - new Key(resource)とnew Lock()の生成
- **51-59行目**: createLockFromKey() - Keyオブジェクトからロック生成、ロガー設定

#### Step 3: ロック本体を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | Lock.php | `src/Symfony/Component/Lock/Lock.php` | ロックの主要実装 |

**主要処理フロー**:
- **67-118行目**: acquire() - ブロッキング/非ブロッキング取得、リトライロジック
- **120-176行目**: acquireRead() - 共有ロック取得、SharedLockStoreInterface非対応時のフォールバック
- **178-207行目**: refresh() - putOffExpirationによるTTL延長
- **209-212行目**: isAcquired() - store::exists()による確認
- **214-235行目**: release() - store::delete()による解放と存在確認

#### Step 4: ストア実装を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | FlockStore.php | `src/Symfony/Component/Lock/Store/FlockStore.php` | ファイルロックストア |
| 4-2 | RedisStore.php | `src/Symfony/Component/Lock/Store/RedisStore.php` | Redisストア |
| 4-3 | StoreFactory.php | `src/Symfony/Component/Lock/Store/StoreFactory.php` | DSNからストア生成 |

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

```
LockFactory::createLock(resource, ttl, autoRelease)
    |
    ├─ new Key(resource)
    └─ new Lock(key, store, ttl, autoRelease)
           |
           ├─ acquire(blocking)
           |      ├─ BlockingStoreInterface::waitAndSave(key)
           |      ├─ PersistingStoreInterface::save(key) + retry
           |      └─ refresh(ttl)
           |             └─ store::putOffExpiration(key, ttl)
           |
           ├─ acquireRead(blocking)
           |      ├─ SharedLockStoreInterface::saveRead(key)
           |      └─ フォールバック → acquire(blocking)
           |
           ├─ isAcquired()
           |      └─ store::exists(key)
           |
           └─ release()
                  └─ store::delete(key)
```

### データフロー図

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

resource (string)    LockFactory                     Lock
ttl (float)          └─ Key + Lock 生成              (SharedLockInterface)
                          |
blocking (bool)      Lock::acquire()                 bool (取得成否)
                     ├─ Store::save/waitAndSave
                     └─ Store::putOffExpiration
                          |
                     Lock::release()                 void
                     └─ Store::delete()
                          |
                     バックエンドストア
                     ├─ Flock (ファイル)
                     ├─ Redis
                     ├─ Memcached
                     ├─ PostgreSQL
                     └─ PDO/Doctrine
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| Lock.php | `src/Symfony/Component/Lock/Lock.php` | ソース | ロック主要実装 |
| LockFactory.php | `src/Symfony/Component/Lock/LockFactory.php` | ソース | ロック生成ファクトリ |
| Key.php | `src/Symfony/Component/Lock/Key.php` | ソース | ロックキーデータ構造 |
| NoLock.php | `src/Symfony/Component/Lock/NoLock.php` | ソース | 無操作ロック（テスト用） |
| FlockStore.php | `src/Symfony/Component/Lock/Store/FlockStore.php` | ソース | ファイルロックストア |
| RedisStore.php | `src/Symfony/Component/Lock/Store/RedisStore.php` | ソース | Redisストア |
| MemcachedStore.php | `src/Symfony/Component/Lock/Store/MemcachedStore.php` | ソース | Memcachedストア |
| PostgreSqlStore.php | `src/Symfony/Component/Lock/Store/PostgreSqlStore.php` | ソース | PostgreSQLストア |
| PdoStore.php | `src/Symfony/Component/Lock/Store/PdoStore.php` | ソース | PDOストア |
| CombinedStore.php | `src/Symfony/Component/Lock/Store/CombinedStore.php` | ソース | コンセンサスストア |
| StoreFactory.php | `src/Symfony/Component/Lock/Store/StoreFactory.php` | ソース | DSNからストア生成 |
