# バッチ設計書 28-PersonalAccessTokens::ExpiringWorker

## 概要

本ドキュメントは、期限切れ間近のパーソナルアクセストークンについて通知を送信するバッチ処理「PersonalAccessTokens::ExpiringWorker」の設計仕様を記載する。

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

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

**業務上の目的・背景**：パーソナルアクセストークンは、GitLab APIやGit操作の認証に使用される重要なセキュリティ資産である。トークンの有効期限切れは、CIパイプラインやインテグレーションの障害を引き起こす可能性がある。本バッチは、ユーザーに事前通知を行うことで、計画的なトークン更新を促し、予期しないサービス中断を防止する。

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

**主要な処理内容**：
1. 通知間隔（7日、30日、60日）ごとにトークンを処理
2. 一般ユーザーのトークン処理
   - 期限間近のトークンを持つユーザーを抽出
   - NotificationServiceで通知メール送信
   - 通知済みフラグを更新
3. プロジェクトボットのトークン処理
   - リソースアクセストークンを抽出
   - Webhookを実行（resource_access_token_hooks）
   - 関連するオーナー/メンテナーに通知
4. 処理時間超過時は自動的に再キュー

**前後の処理との関連**：ExpiredNotificationWorkerは期限切れ後の通知を担当し、本ワーカーは期限切れ前の事前通知を担当する。

**影響範囲**：personal_access_tokensテーブルの通知フラグ更新、メール送信、Webhook実行。

## バッチ種別

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

## 実行スケジュール

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

## 実行条件

### 前提条件

| 条件 | 説明 |
|-----|------|
| データベース接続 | personal_access_tokensテーブルへのアクセスが可能であること |
| メール設定 | SMTPが設定されていること |

### 実行可否判定

特別な実行可否判定ロジックはなく、Cronスケジュールに従って実行される。時間超過時は自動再キューされる。

## 入力仕様

### 入力パラメータ

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

### 入力データソース

| データソース | 形式 | 説明 |
|-------------|------|------|
| personal_access_tokens | DB | トークン情報テーブル |
| users | DB | ユーザー情報テーブル |
| projects | DB | プロジェクト情報（ボットトークン用） |

## 出力仕様

### 出力データ

| 出力先 | 形式 | 説明 |
|-------|------|------|
| personal_access_tokens | DB | 通知フラグ更新 |
| メール | 通知 | ユーザーへの期限切れ通知メール |
| Webhook | HTTP | resource_access_token_hooks |
| Sidekiqログ | ログ | 処理統計 |

### 出力ファイル仕様

ファイル出力なし

## 処理フロー

### 処理シーケンス

```
1. RuntimeLimiter初期化
   └─ MAX_RUNTIME = 3分
2. 通知間隔ループ（7日、30日、60日）
3. ユーザートークン処理（process_user_tokens）
   └─ scope_for_notification_intervalでトークン抽出
   └─ バッチサイズ: 100件ずつ
   └─ NotificationService.access_token_about_to_expire送信
   └─ {interval}_notification_sent_at更新
   └─ 時間超過時は早期リターン
4. ボットトークン処理（process_project_bot_tokens）
   └─ プロジェクトボットのトークン抽出
   └─ リソース（プロジェクト）の存在確認
   └─ Webhook実行（execute_web_hooks）
   └─ NotificationService.bot_resource_access_token_about_to_expire送信
   └─ 通知フラグ更新
5. 時間超過時の再キュー
   └─ perform_in(REQUEUE_DELAY, *args) - 2分後に再実行
```

### フローチャート

```mermaid
flowchart TD
    A[バッチ開始] --> B[RuntimeLimiter初期化]
    B --> C[通知間隔ループ]
    C --> D[process_user_tokens]
    D --> E{時間超過?}
    E -->|はい| M[再キュー設定]
    E -->|いいえ| F[process_project_bot_tokens]
    F --> G{時間超過?}
    G -->|はい| M
    G -->|いいえ| H{次の間隔あり?}
    H -->|はい| C
    H -->|いいえ| I[処理完了]
    M --> J[perform_in 2分後]
    J --> K[バッチ終了]
    I --> K
```

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

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

| 処理 | 対象テーブル | 操作種別 | 概要 |
|-----|-------------|---------|------|
| トークン取得 | personal_access_tokens | SELECT | 期限間近のトークン抽出 |
| ユーザー取得 | users | SELECT | トークン所有者情報取得 |
| 通知フラグ更新 | personal_access_tokens | UPDATE | 通知済みフラグ設定 |
| プロジェクト取得 | projects | SELECT | ボットトークンのリソース情報 |

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

#### personal_access_tokens

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| SELECT | id, name, user_id, expires_at | scope_for_notification_interval | BATCH_SIZE: 100 |
| 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日通知時 |
| UPDATE | expire_notification_delivered | true | 7日通知時（レガシー互換） |

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| - | メール送信エラー | SMTP障害 | 例外ログ記録、処理継続 |
| - | Webhook実行エラー | エンドポイント障害 | 例外ログ記録、該当ボットをスキップ |
| - | リソース不存在 | ボットのプロジェクトが削除済み | project_bot_ids_without_resourceに記録 |

### リトライ仕様

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

### 障害時対応

個別のメール送信やWebhook実行エラーは記録して次のトークンへ継続。致命的なエラーはSidekiqの標準リトライに委ねる。

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

| 項目 | 内容 |
|-----|------|
| トランザクション範囲 | バッチ更新単位 |
| コミットタイミング | update_all実行時 |
| ロールバック条件 | 更新失敗時 |

## パフォーマンス要件

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

## 排他制御

特別な排他制御はなし。idempotentではない（通知重複の可能性あり）。通知フラグで重複通知を防止。

## ログ出力

| ログ種別 | 出力タイミング | 出力内容 |
|---------|--------------|---------|
| 統計ログ | バッチ終了時 | total_notification_delivered_for_resource_access_tokens |
| 統計ログ | バッチ終了時 | total_resource_bot_without_membership |
| 統計ログ | バッチ終了時 | total_failed_notifications_for_resource_bots |
| エラーログ | エラー発生時 | 例外情報、user_id |

## 監視・アラート

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

## 備考

- feature_category: system_access
- data_consistency: always
- idempotent: false
- 通知間隔: NOTIFICATION_INTERVALS = { seven_days: 0..7, thirty_days: 8..30, sixty_days: 31..60 }
- MAX_TOKENS: 1ユーザーあたり最大100トークンまでメール記載
- Webhook: resource_access_token_hooksイベント（expiringデータ）
- expire_notification_deliveredは廃止予定（後方互換のため維持）
