# バッチ設計書 30-DeployTokens::ExpiringWorker

## 概要

本ドキュメントは、期限切れ間近のデプロイトークンについて通知を送信するバッチ処理「DeployTokens::ExpiringWorker」の設計仕様を記載する。

### 本バッチの処理概要

本バッチは、有効期限が近づいているプロジェクトデプロイトークンのプロジェクトオーナーとメンテナーに対して、事前通知メールを送信する処理を行う。複数の通知間隔（7日、30日、60日前）に対応している。

**業務上の目的・背景**：デプロイトークンは、GitLabのコンテナレジストリやパッケージレジストリへのアクセス、CIパイプラインでのデプロイ操作に使用される重要な認証情報である。トークンの有効期限切れは、デプロイメントの失敗やコンテナイメージのプル失敗を引き起こす。本バッチは、プロジェクトの管理者（オーナーとメンテナー）に事前通知を行うことで、計画的なトークン更新を促し、運用上の問題を未然に防止する。

**バッチの実行タイミング**：Cronジョブとして定期的に実行される。MAX_RUNTIME（3分）の制限があり、時間超過時は2分後に再実行される。

**主要な処理内容**：
1. 通知間隔（7日、30日、60日）ごとにデプロイトークンを処理
2. プロジェクトデプロイトークンを抽出（アクティブなもののみ）
3. 関連プロジェクトのオーナーとメンテナーを特定
4. 各ユーザーにNotificationServiceで通知メール送信
5. 通知タイムスタンプを一括更新
6. 処理時間超過時は自動的に再キュー

**前後の処理との関連**：PersonalAccessTokens::ExpiringWorkerと同様のパターンで実装されており、デプロイトークン専用の通知を担当する。

**影響範囲**：deploy_tokensテーブルの通知タイムスタンプ更新、メール送信。

## バッチ種別

通知配信 / セキュリティ運用

## 実行スケジュール

| 項目 | 内容 |
|-----|------|
| 実行頻度 | 定期実行（Cron） |
| 実行時刻 | システム設定による |
| 実行曜日 | 毎日 |
| 実行日 | - |
| トリガー | cron |

## 実行条件

### 前提条件

| 条件 | 説明 |
|-----|------|
| Feature Flag | project_deploy_token_expiring_notifications がプロジェクトで有効であること |
| データベース接続 | deploy_tokensテーブルへのアクセスが可能であること |
| メール設定 | SMTPが設定されていること |

### 実行可否判定

Feature Flag `project_deploy_token_expiring_notifications` がプロジェクトレベルで有効な場合のみ通知を送信。

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | デフォルト値 | 説明 |
|-------------|-----|-----|-------------|------|
| args | 任意 | No | - | 再キュー時に引き継がれる引数 |

### 入力データソース

| データソース | 形式 | 説明 |
|-------------|------|------|
| deploy_tokens | DB | デプロイトークン情報テーブル |
| projects | DB | プロジェクト情報テーブル |
| project_members | DB | プロジェクトメンバー情報 |

## 出力仕様

### 出力データ

| 出力先 | 形式 | 説明 |
|-------|------|------|
| deploy_tokens | DB | 通知タイムスタンプ更新 |
| メール | 通知 | オーナー/メンテナーへの期限切れ通知メール |

### 出力ファイル仕様

ファイル出力なし

## 処理フロー

### 処理シーケンス

```
1. RuntimeLimiter初期化
   └─ MAX_RUNTIME = 3分
2. 通知間隔ループ（7日、30日、60日）
3. デプロイトークン処理（process_deploy_tokens）
   └─ scope_for_notification_intervalでトークン抽出
   └─ project_token.activeのみ対象
   └─ with_project_owners_and_maintainersで関連ユーザー取得
   └─ Keyset Paginationでバッチ処理（100件ずつ）
4. 各トークンの通知処理
   └─ Feature Flagチェック（project_deploy_token_expiring_notifications）
   └─ プロジェクト存在確認
   └─ オーナー/メンテナー一覧取得
   └─ 各ユーザーにNotificationService.deploy_token_about_to_expire送信
5. 通知タイムスタンプ更新
   └─ DeployToken.update_notification_timestamps
   └─ 更新失敗時はエラートラッキング
6. 時間超過時の再キュー
   └─ perform_in(REQUEUE_DELAY, *args) - 2分後に再実行
```

### フローチャート

```mermaid
flowchart TD
    A[バッチ開始] --> B[RuntimeLimiter初期化]
    B --> C[通知間隔ループ]
    C --> D[process_deploy_tokens]
    D --> E[Keyset Paginatorでバッチ取得]
    E --> F{トークンあり?}
    F -->|なし| G{時間超過?}
    F -->|あり| H{Feature Flag有効?}
    H -->|いいえ| I[スキップ]
    H -->|はい| J{プロジェクト存在?}
    J -->|いいえ| I
    J -->|はい| K[オーナー/メンテナー取得]
    K --> L{ユーザーあり?}
    L -->|いいえ| I
    L -->|はい| M[通知メール送信]
    M --> N[成功トークンIDを記録]
    I --> O[次のトークンへ]
    N --> O
    O --> F
    G -->|はい| P[再キュー設定]
    G -->|いいえ| Q{次の間隔あり?}
    Q -->|はい| C
    Q -->|いいえ| R[処理完了]
    P --> S[perform_in 2分後]
    S --> T[バッチ終了]
    R --> T
```

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

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

| 処理 | 対象テーブル | 操作種別 | 概要 |
|-----|-------------|---------|------|
| トークン取得 | deploy_tokens | SELECT | 期限間近のトークン抽出 |
| プロジェクト取得 | projects | SELECT | トークン関連プロジェクト |
| メンバー取得 | project_members | SELECT | オーナー/メンテナー情報 |
| タイムスタンプ更新 | deploy_tokens | UPDATE | 通知タイムスタンプ設定 |

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

#### deploy_tokens

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| SELECT | id, name, expires_at | scope_for_notification_interval, project_token, active | Keyset Pagination |
| UPDATE | seven_days_notification_sent_at | Time.current | 7日通知時 |
| UPDATE | thirty_days_notification_sent_at | Time.current | 30日通知時 |
| UPDATE | sixty_days_notification_sent_at | Time.current | 60日通知時 |

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| - | メール送信エラー | SMTP障害等 | ErrorTracking記録、該当トークンをスキップ |
| - | タイムスタンプ更新エラー | DB更新失敗 | ErrorTracking記録、継続 |

### リトライ仕様

| 項目 | 内容 |
|-----|------|
| リトライ回数 | 自動再キュー（時間超過時） |
| リトライ間隔 | REQUEUE_DELAY = 2分 |
| リトライ対象エラー | 時間超過のみ自動再キュー |

### 障害時対応

個別の通知エラーはErrorTrackingに記録して次のトークンへ継続。タイムスタンプ更新失敗時もログ記録して継続。

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

| 項目 | 内容 |
|-----|------|
| トランザクション範囲 | バッチ更新単位 |
| コミットタイミング | update_notification_timestamps実行時 |
| ロールバック条件 | 更新失敗時（エラー記録して継続） |

## パフォーマンス要件

| 項目 | 内容 |
|-----|------|
| 想定処理件数 | BATCH_SIZE: 100件ずつ |
| 目標処理時間 | MAX_RUNTIME: 3分（超過時は再キュー） |
| メモリ使用量上限 | Keyset Paginationにより低メモリ使用 |

## 排他制御

- idempotent!が設定されており、重複実行は安全
- 通知タイムスタンプで重複通知を防止

## ログ出力

| ログ種別 | 出力タイミング | 出力内容 |
|---------|--------------|---------|
| エラーログ | 通知失敗時 | 例外情報、deploy_token_id、project_id、user_id |
| エラーログ | 更新失敗時 | 例外情報、token_ids、interval |

## 監視・アラート

| 監視項目 | 閾値 | アラート先 |
|---------|-----|----------|
| 処理失敗 | 継続的な失敗 | システム管理者 |
| メール送信エラー | 異常な発生率 | メールシステム管理者 |

## 備考

- feature_category: continuous_delivery
- data_consistency: sticky
- idempotent: true
- 通知間隔: NOTIFICATION_INTERVALS = { seven_days: 0..7, thirty_days: 8..30, sixty_days: 31..60 }
- MAX_RUNTIME: 3分
- REQUEUE_DELAY: 2分
- BATCH_SIZE: 100件
- Feature Flag: project_deploy_token_expiring_notifications（プロジェクトレベル）
- Keyset Pagination使用: ordered_for_keyset_pagination
- プロジェクトトークンのみ対象（グループトークンは対象外）
