# 機能設計書 41-リトライ機能

## 概要

本ドキュメントは、データベース操作のリトライ機能の設計について記載する。この機能は、ネットワークエラーや一時的なサーバーエラーが発生した際に、指数バックオフ戦略を用いて自動的に操作をリトライする仕組みを提供する。

### 本機能の処理概要

**業務上の目的・背景**：クラウドベースのデータベースサービス（Supabase）を使用するWebアプリケーションでは、ネットワーク障害やサービス一時停止などの一時的なエラーが発生することがある。これらのエラーはリトライによって解決可能なケースが多いため、自動リトライ機能を実装することでシステムの可用性と信頼性を向上させる必要がある。

**機能の利用シーン**：データベースへのCRUD操作実行時にネットワークエラー（fetch failed、connection refused等）やHTTP 5xxエラー（500、502、503、504）が発生した場合に、自動的にリトライを行う。これにより、一時的な障害による操作失敗を最小限に抑え、ユーザー体験を向上させる。

**主要な処理内容**：
1. 操作実行と結果判定（成功/失敗の判定）
2. エラーがリトライ可能かどうかの判定（retryablePatterns照合）
3. 指数バックオフによる待機時間の計算
4. 最大試行回数に達するまでのリトライ実行
5. 最終結果のResultAsync型での返却

**関連システム・外部連携**：Supabaseデータベースサービスとの連携において使用される。neverthrowライブラリのResultAsync型を使用した関数型エラーハンドリングを採用。

**権限による制御**：本機能は低レベルのユーティリティであり、権限による制御は行わない。呼び出し元の機能で適切な権限チェックを行う。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| - | - | - | 本機能は画面に直接関連しない低レベルユーティリティ |

## 機能種別

データ連携 / エラーハンドリング / リトライ処理

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| fn | `() => ResultAsync<T, E>` | Yes | リトライ対象の非同期関数 | ResultAsync型を返す関数であること |
| options | RetryOptions | No | リトライ設定オプション | - |
| options.maxAttempts | number | No | 最大試行回数（デフォルト: 3） | 正の整数 |
| options.baseDelayMs | number | No | 基本待機時間（ミリ秒、デフォルト: 1000） | 正の整数 |
| options.maxDelayMs | number | No | 最大待機時間（ミリ秒、デフォルト: 8000） | 正の整数 |
| options.backoffMultiplier | number | No | バックオフ倍率（デフォルト: 2） | 正の数値 |

### 入力データソース

プログラム内部からの関数呼び出し

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| result | `ResultAsync<T, E>` | 成功時は`ok(T)`、失敗時は`err(E)`を含むResultAsync |

### 出力先

呼び出し元への戻り値として返却

## 処理フロー

### 処理シーケンス

```
1. retry関数呼び出し
   └─ オプションパラメータのデフォルト値適用
2. attemptRetry実行（attemptNumber = 1）
   └─ 対象関数fnを実行
3. 結果判定
   ├─ 成功（isOk()）→ 結果を返却して終了
   └─ 失敗（isErr()）→ 次のステップへ
4. リトライ可否判定
   ├─ 最大試行回数到達 → エラーをスロー
   ├─ リトライ不可エラー → エラーをスロー
   └─ リトライ可能 → 次のステップへ
5. 指数バックオフ計算
   └─ delay = min(baseDelayMs * multiplier^(attempt-1), maxDelayMs)
6. 待機後、ステップ2へ戻る（attemptNumber + 1）
7. 最終結果をResultAsyncでラップして返却
```

### フローチャート

```mermaid
flowchart TD
    A[retry関数呼び出し] --> B[オプションデフォルト値適用]
    B --> C[attemptRetry実行]
    C --> D[対象関数fn実行]
    D --> E{結果判定}
    E -->|成功| F[結果を返却]
    E -->|失敗| G{最大試行回数到達?}
    G -->|Yes| H[エラーをスロー]
    G -->|No| I{リトライ可能エラー?}
    I -->|No| H
    I -->|Yes| J[指数バックオフ計算]
    J --> K[待機]
    K --> C
    F --> L[ResultAsyncでラップ]
    H --> L
    L --> M[終了]
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-41-01 | リトライ可能エラー判定 | エラーメッセージまたはエラー名に特定パターンが含まれる場合のみリトライ | エラー発生時 |
| BR-41-02 | 最大試行回数制限 | デフォルト3回まで試行し、超過時はエラーを返却 | リトライ可能エラー発生時 |
| BR-41-03 | 指数バックオフ | 待機時間を試行回数に応じて指数関数的に増加 | リトライ実行前 |
| BR-41-04 | 最大待機時間制限 | 待機時間がmaxDelayMsを超えないよう制限 | 待機時間計算時 |

### 計算ロジック

**指数バックオフ待機時間計算**：
```
delay = Math.min(baseDelayMs * backoffMultiplier^(attemptNumber - 1), maxDelayMs)
```

**リトライ可能パターン一覧**：
- ネットワークエラー: `fetch failed`, `network error`, `connection`, `socket hang up`
- DNS/接続エラー: `enotfound`, `econnreset`, `etimedout`, `timeout`
- HTTP 5xxエラー: `500`, `502`, `503`, `504`
- ゲートウェイエラー: `internal server error`, `bad gateway`, `gateway error`, `service unavailable`, `gateway timeout`

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

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

| 操作 | 対象テーブル | 操作種別 | 概要 |
|-----|-------------|---------|------|
| - | - | - | 本機能はデータベースを直接操作しない |

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

本機能はデータベースを直接操作しないユーティリティ関数である。

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| - | 認証エラー | Unauthorized等のエラー | リトライせず即座にエラーを返却 |
| - | バリデーションエラー | Invalid input等のエラー | リトライせず即座にエラーを返却 |
| - | 最大試行回数超過 | maxAttempts回失敗 | 最後のエラーを返却 |
| - | nullエラー | エラーがnull | リトライせず即座にエラーを返却 |

### リトライ仕様

- 最大試行回数: 3回（デフォルト）
- 基本待機時間: 1000ms（デフォルト）
- 最大待機時間: 8000ms（デフォルト）
- バックオフ倍率: 2（デフォルト）
- 待機時間例: 1回目失敗後1000ms、2回目失敗後2000ms、3回目失敗後4000ms

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

本機能はトランザクションを管理しない。トランザクション管理は呼び出し元の責務である。

## パフォーマンス要件

- リトライなしの場合: 対象関数の実行時間のみ
- 最大リトライ時: 対象関数の実行時間 + 最大待機時間（1000 + 2000 + 4000 = 7000ms）
- カスタム設定により待機時間の調整が可能

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

- 本機能はセキュリティに直接関与しない低レベルユーティリティ
- リトライ対象の関数で適切な認証・認可を行うこと
- 認証エラー等のリトライ不可エラーは即座に返却され、不必要なリトライを防止

## 備考

- neverthrowライブラリのResult型を使用した関数型エラーハンドリングを採用
- TypeScriptのジェネリクスにより任意の型に対応
- テスト済みのエラーパターン: ネットワークエラー、タイムアウト、DNS解決エラー、HTTP 5xx、Cloudflareエラー

---

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

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

### 推奨読解順序

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

まず、リトライ機能で使用される型定義と設定値を理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | retry.ts | `frontend/internal-packages/db/src/utils/retry.ts` | RetryOptions型とRETRY_CONFIGの定義 |

**読解のコツ**: TypeScriptの`as const`アサーションにより、RETRY_CONFIGがリテラル型として定義されている点に注目。これにより型安全性が向上している。

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

retry関数のシグネチャと処理の起点を確認する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | retry.ts | `frontend/internal-packages/db/src/utils/retry.ts` | retry関数の定義とオプションのデフォルト値適用 |

**主要処理フロー**:
1. **61-69行目**: retry関数の定義とオプションパラメータのデフォルト値適用
2. **71-89行目**: attemptRetry内部関数による再帰的リトライ処理
3. **91-95行目**: ResultAsync.fromPromiseによる結果のラップ

#### Step 3: リトライ判定ロジックを理解する

isRetryableError関数によるエラー種別判定を理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | retry.ts | `frontend/internal-packages/db/src/utils/retry.ts` | isRetryableError関数とretryablePatternsの定義 |

**主要処理フロー**:
- **16-46行目**: isRetryableError関数の定義
- **23-41行目**: リトライ可能パターンの配列定義
- **43-45行目**: someメソッドによるパターンマッチング

#### Step 4: エクスポートと利用方法を理解する

モジュールのエクスポートと他モジュールからの利用方法を確認する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | index.ts | `frontend/internal-packages/db/src/index.ts` | retry関数のエクスポート |

**主要処理フロー**:
- **18行目**: `export { retry } from './utils/retry'`によるretry関数の公開

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

```
呼び出し元（Supabase操作）
    │
    └─ retry(fn, options?)
           │
           ├─ attemptRetry(attemptNumber)
           │      │
           │      ├─ fn()  ← リトライ対象関数の実行
           │      │
           │      ├─ isRetryableError(error)
           │      │      └─ retryablePatterns.some(...)
           │      │
           │      └─ setTimeout(delay)  ← 指数バックオフ待機
           │
           └─ ResultAsync.fromPromise(...)
```

### データフロー図

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

fn: () => ResultAsync ─▶ attemptRetry(1) ─▶ fn() ─────▶ ResultAsync<T, E>
                              │
options: RetryOptions ───────┤
                              │
                         isRetryableError(error)
                              │
                         指数バックオフ計算
                              │
                         setTimeout(delay)
                              │
                         attemptRetry(n+1) ─▶ ...
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| retry.ts | `frontend/internal-packages/db/src/utils/retry.ts` | ソース | リトライ機能のメイン実装 |
| retry.test.ts | `frontend/internal-packages/db/src/utils/retry.test.ts` | テスト | リトライ機能のユニットテスト |
| index.ts | `frontend/internal-packages/db/src/index.ts` | ソース | パッケージエントリーポイント（エクスポート） |
