# バッチ設計書 60-PagesDomainSslRenewalCronWorker

## 概要

本ドキュメントは、GitLabにおけるPagesカスタムドメインのSSL証明書自動更新を実行するバッチ処理「PagesDomainSslRenewalWorker」の設計を定義する。

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

**業務上の目的・背景**：GitLab Pagesでは、Let's Encryptを使用してカスタムドメインのSSL証明書を自動的に取得・更新する機能を提供している。SSL証明書には有効期限があり、期限切れ前に自動的に更新する必要がある。本バッチは、SSL証明書の更新が必要なドメインに対して、Let's Encryptを使用した証明書取得・更新プロセスを実行し、HTTPSによる安全な通信を継続的に提供する。

**バッチの実行タイミング**：PagesDomainSslRenewalCronWorker（Cronスケジューラ）が定期的にPagesDomainSslRenewalWorkerをトリガー。また、ACMEチャレンジ処理中は1分遅延で再キューされる。

**主要な処理内容**：
1. ドメインの存在と有効性を確認
2. Let's Encrypt機能の有効化確認
3. ACMEオーダーの状態に応じた処理（新規作成、証明書リクエスト、証明書保存）
4. エラー発生時は通知サービスで管理者に通知

**前後の処理との関連**：ObtainLetsEncryptCertificateServiceがメインの証明書取得ロジックを実装。CreateAcmeOrderServiceでACMEオーダーを作成し、Let's Encrypt APIと連携。

**影響範囲**：pages_domainsテーブルのSSL証明書関連カラム、pages_domain_acme_ordersテーブルが更新される。証明書更新失敗時はauto_ssl_failedフラグが設定される。

## バッチ種別

証明書管理 / SSL更新処理

## 実行スケジュール

| 項目 | 内容 |
|-----|------|
| 実行頻度 | イベント駆動（Cron + 遅延キュー） |
| 実行時刻 | Cron設定に依存、または1分遅延 |
| 実行曜日 | 毎日 |
| 実行日 | - |
| トリガー | cron / PagesDomainSslRenewalWorker.perform_in |

## 実行条件

### 前提条件

| 条件 | 説明 |
|-----|------|
| ドメインの存在 | PagesDomain.find_by_id(domain_id)で取得可能 |
| ドメインが有効 | domain.enabled?がtrue |
| Let's Encrypt有効 | Gitlab::LetsEncrypt.enabled?がtrue |

### 実行可否判定

ドメインが存在しない、無効、またはLet's Encryptが無効の場合は即時終了。

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | デフォルト値 | 説明 |
|-------------|-----|-----|-------------|------|
| domain_id | Integer | Yes | - | 対象PagesドメインのID |

### 入力データソース

| データソース | 形式 | 説明 |
|-------------|------|------|
| pages_domains | DB | Pagesカスタムドメイン情報 |
| pages_domain_acme_orders | DB | ACMEオーダー情報 |

## 出力仕様

### 出力データ

| 出力先 | 形式 | 説明 |
|-------|------|------|
| pages_domains | DB | SSL証明書情報の更新 |
| pages_domain_acme_orders | DB | ACMEオーダーの作成/削除 |
| PagesDomainSslRenewalWorkerキュー | Sidekiq | 遅延再キュー（処理継続時） |

### 出力ファイル仕様

ファイル出力なし

## 処理フロー

### 処理シーケンス

```
1. ドメインの存在と有効性を確認
   └─ PagesDomain.find_by_id, domain.enabled?
2. Let's Encrypt機能の有効化確認
   └─ Gitlab::LetsEncrypt.enabled?
3. ObtainLetsEncryptCertificateService.executeを実行
   ├─ 期限切れACMEオーダーを削除
   ├─ 既存ACMEオーダーがない場合：
   │   └─ CreateAcmeOrderServiceで新規作成
   │   └─ 1分後に再キュー（CHALLENGE_PROCESSING_DELAY）
   ├─ ACMEオーダーのステータス確認
   │   ├─ ready: 証明書リクエスト実行、1分後に再キュー
   │   ├─ valid: 証明書保存、オーダー削除
   │   └─ invalid: エラー保存、通知送信
4. エラー発生時
   └─ auto_ssl_failed設定、通知サービス実行
```

### フローチャート

```mermaid
flowchart TD
    A[バッチ開始] --> B[ドメイン取得]
    B --> C{ドメイン存在&有効?}
    C -->|No| D[バッチ終了]
    C -->|Yes| E{LetsEncrypt有効?}
    E -->|No| D
    E -->|Yes| F[ObtainLetsEncryptCertificateService.execute]
    F --> G[期限切れACMEオーダー削除]
    G --> H{既存ACMEオーダーあり?}
    H -->|No| I[CreateAcmeOrderService実行]
    I --> J[1分後に再キュー]
    J --> D
    H -->|Yes| K[ACMEオーダーステータス確認]
    K --> L{ステータス}
    L -->|ready| M[証明書リクエスト]
    M --> N[1分後に再キュー]
    N --> D
    L -->|valid| O[証明書保存]
    O --> P[ACMEオーダー削除]
    P --> D
    L -->|invalid| Q[エラー保存]
    Q --> R[通知サービス実行]
    R --> D
```

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

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

| 処理 | 対象テーブル | 操作種別 | 概要 |
|-----|-------------|---------|------|
| ドメイン取得 | pages_domains | SELECT | domain_idで取得 |
| オーダー取得 | pages_domain_acme_orders | SELECT | domain_idで関連取得 |
| オーダー削除 | pages_domain_acme_orders | DELETE | 期限切れ/完了オーダー |
| 証明書更新 | pages_domains | UPDATE | gitlab_provided_key/certificate |
| エラーフラグ | pages_domains | UPDATE | auto_ssl_failed |

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

#### pages_domains

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| SELECT | id, enabled | id = domain_id | ドメイン取得 |
| UPDATE | gitlab_provided_key, gitlab_provided_certificate | 新しい証明書情報 | 更新成功時 |
| UPDATE | auto_ssl_failed | true | エラー発生時 |

#### pages_domain_acme_orders

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| SELECT | url, private_key | pages_domain_id | オーダー取得 |
| DELETE | - | expired / 完了時 | オーダー削除 |
| INSERT | - | 新規オーダー作成 | CreateAcmeOrderService経由 |

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| - | Acme::Client::Error | Let's Encrypt API エラー | エラー保存、通知送信 |
| - | invalid status | ACMEチャレンジ失敗 | エラー保存、通知送信 |
| - | DB接続エラー | データベース接続失敗 | Sidekiqによるリトライ |

### リトライ仕様

| 項目 | 内容 |
|-----|------|
| リトライ回数 | 3回（sidekiq_options retry: 3） |
| リトライ間隔 | Sidekiqデフォルト |
| リトライ対象エラー | すべての例外 |

### 障害時対応

証明書取得失敗時はauto_ssl_failedフラグが設定され、NotificationServiceで管理者に通知。手動での再試行または設定確認が必要。

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

| 項目 | 内容 |
|-----|------|
| トランザクション範囲 | 個別操作単位 |
| コミットタイミング | 各操作完了時 |
| ロールバック条件 | 例外発生時 |

## パフォーマンス要件

| 項目 | 内容 |
|-----|------|
| 想定処理件数 | 1ドメイン/ジョブ |
| 目標処理時間 | Let's Encrypt APIレスポンス依存（通常10-15秒） |
| メモリ使用量上限 | 低（単一ドメイン処理） |

## 排他制御

特になし。各ドメインは個別のジョブで処理される。ACMEオーダーの状態管理により処理の重複を防止。

## ログ出力

| ログ種別 | 出力タイミング | 出力内容 |
|---------|--------------|---------|
| エラーログ | 証明書取得失敗時 | message, acme_error, project_id, pages_domain |

## 監視・アラート

| 監視項目 | 閾値 | アラート先 |
|---------|-----|----------|
| エラー件数 | Sidekiq失敗キュー | 運用チーム |
| auto_ssl_failed | true | プロジェクト管理者（NotificationService経由） |

## 備考

- data_consistency: :always を使用
- feature_category: :pages
- sidekiq_options retry: 3
- CronjobChildWorkerをinclude
- CHALLENGE_PROCESSING_DELAY = 1分（ACMEチャレンジ処理待ち）
- CERTIFICATE_PROCESSING_DELAY = 1分（証明書生成待ち）
- Let's Encrypt APIのステータス遷移: pending -> ready -> valid (または invalid)
- 証明書取得失敗時はNotificationService.new.pages_domain_auto_ssl_failedで通知
- ACMEオーダーの期限切れは自動削除される
