# バッチ設計書 10-Ci_PipelineArtifacts_ExpireArtifactsWorker

## 概要

本ドキュメントは、期限切れのパイプラインアーティファクトを削除するバッチ処理「Ci::PipelineArtifacts::ExpireArtifactsWorker」の設計仕様を記載する。

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

Ci::PipelineArtifacts::ExpireArtifactsWorkerは、有効期限が切れたパイプラインアーティファクト（パイプラインレベルの成果物）を検出し、削除するバッチ処理である。ジョブアーティファクト（ExpireBuildArtifactsWorker）とは異なり、パイプライン全体に関連する成果物を管理する。

**業務上の目的・背景**：CI/CDパイプラインでは、パイプラインレベルで生成される成果物（カバレッジレポート、テストレポート、セキュリティスキャン結果等）がある。これらのパイプラインアーティファクトは、ジョブアーティファクトとは別に管理され、独自の有効期限が設定される。大規模なGitLabインスタンスでは、これらのアーティファクトも大量のストレージを消費するため、定期的な削除が必要である。本バッチは、有効期限切れのパイプラインアーティファクトを検出し、効率的に削除することで、ストレージ容量を適正に管理する。

**バッチの実行タイミング**：23分間隔で実行（cron: `*/23 * * * *`）。ジョブアーティファクト削除（7分間隔）とは異なるタイミングで実行し、DB負荷を分散する。

**主要な処理内容**：
1. 排他ロック（ExclusiveLease）を取得し、同時実行を防止
2. 有効期限が切れたパイプラインアーティファクトを検索
3. バッチサイズ100件ずつ、5分または1000回のループ上限まで処理
4. 各アーティファクトを個別に削除（destroy!）
5. 削除件数をPrometheusメトリクスとログに記録

**前後の処理との関連**：本ワーカーはExpireBuildArtifactsWorker（ジョブアーティファクト）とは独立して動作する。パイプラインアーティファクトはci_pipeline_artifactsテーブルで管理され、ジョブアーティファクト（ci_job_artifacts）とは別系統である。

**影響範囲**：全プロジェクトのパイプラインアーティファクトに影響。アーティファクト削除によりストレージが解放され、関連するレポートやダッシュボードからのアクセスが不可になる。

## バッチ種別

データクレンジング / ストレージ管理

## 実行スケジュール

| 項目 | 内容 |
|-----|------|
| 実行頻度 | 23分間隔 |
| 実行時刻 | 毎時0分、23分、46分 |
| 実行曜日 | 毎日 |
| 実行日 | 毎日 |
| トリガー | cron (`*/23 * * * *`) |

## 実行条件

### 前提条件

| 条件 | 説明 |
|-----|------|
| 排他ロック取得 | ExclusiveLeaseを取得できること |
| 期限切れアーティファクト存在 | 有効期限が切れたパイプラインアーティファクトが存在すること |

### 実行可否判定

排他ロック（ExclusiveLease）を取得できた場合のみ処理を実行する。TTLは10分。`deduplicate :until_executed, including_scheduled: true`により重複実行は防止される。

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | デフォルト値 | 説明 |
|-------------|-----|-----|-------------|------|
| なし | - | - | - | cronジョブとして引数なしで実行 |

### 入力データソース

| データソース | 形式 | 説明 |
|-------------|------|------|
| ci_pipeline_artifacts | DB | パイプラインアーティファクトテーブル |

## 出力仕様

### 出力データ

| 出力先 | 形式 | 説明 |
|-------|------|------|
| ci_pipeline_artifacts | DB | レコード削除 |
| オブジェクトストレージ | ファイル削除 | アーティファクトファイル削除 |
| Prometheus | メトリクス | destroyed_pipeline_artifacts_count_total |

### 出力ファイル仕様

ファイル出力なし（ファイル削除のみ）。

## 処理フロー

### 処理シーケンス

```
1. 排他ロック取得
   └─ ExclusiveLeaseHelpers.in_lockでロック取得（TTL: 10分）
2. ループ処理開始
   └─ 5分のタイムアウトと1000回のリミットを設定
3. 期限切れアーティファクト取得
   └─ expired_before(@start_at).artifact_unlocked.limit(100)
4. 空チェック
   └─ artifactsが空ならbreak
5. バッチ削除実行
   └─ artifacts.each(&:destroy!)
6. メトリクス更新
   └─ destroyed_artifacts_counter.increment({}, size)
7. 削除件数カウント
   └─ @removed_artifacts_count += size
8. ログ出力
   └─ destroyed_pipeline_artifacts_countをメタデータに記録
```

### フローチャート

```mermaid
flowchart TD
    A[バッチ開始] --> B{排他ロック取得}
    B -->|成功| C[期限切れアーティファクト検索]
    B -->|失敗| I[バッチ終了]
    C --> D{アーティファクト存在?}
    D -->|なし| G{タイムアウト or ループ上限?}
    D -->|あり| E[destroy!で削除]
    E --> F[メトリクス更新]
    F --> G
    G -->|はい| H[削除件数をログ出力]
    G -->|いいえ| C
    H --> I
```

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

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

| 処理 | 対象テーブル | 操作種別 | 概要 |
|-----|-------------|---------|------|
| 期限切れアーティファクト取得 | ci_pipeline_artifacts | SELECT | expire_at < now、artifact_unlocked |
| アーティファクト削除 | ci_pipeline_artifacts | DELETE | レコード削除 |

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

#### ci_pipeline_artifacts

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| SELECT | 全カラム | expire_at < @start_at, locked = 0 | バッチサイズ100件 |
| DELETE | - | 取得したレコードを削除 | destroy!で実行 |

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| - | ExclusiveLease取得失敗 | 別プロセスがロック保持中 | スキップ |
| - | destroy!失敗 | レコード削除エラー | 例外発生、ループ継続 |
| - | ファイル削除失敗 | オブジェクトストレージエラー | 例外発生 |

### リトライ仕様

| 項目 | 内容 |
|-----|------|
| リトライ回数 | 1回（ExclusiveLease取得） |
| リトライ間隔 | - |
| リトライ対象エラー | ロック取得失敗 |

### 障害時対応

1. ロック取得失敗の場合：次回実行時に自動復旧
2. destroy!失敗の場合：次回実行時にリトライ

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

| 項目 | 内容 |
|-----|------|
| トランザクション範囲 | レコード単位 |
| コミットタイミング | 各destroy!完了時 |
| ロールバック条件 | destroy!処理中のエラー発生時 |

## パフォーマンス要件

| 項目 | 内容 |
|-----|------|
| 想定処理件数 | バッチサイズ100件 × 最大1000回 = 最大100,000件 |
| 目標処理時間 | 5分以内（LOOP_TIMEOUT） |
| メモリ使用量上限 | バッチサイズ100件による制御 |

## 排他制御

- ExclusiveLease（TTL: 10分）による排他制御
- 同時に1プロセスのみ実行可能
- ロックキー: `expired_pipeline_artifacts:destroy:lock`
- `deduplicate :until_executed, including_scheduled: true`で重複実行防止

## ログ出力

| ログ種別 | 出力タイミング | 出力内容 |
|---------|--------------|---------|
| 開始ログ | バッチ開始時 | Sidekiq標準ログ |
| メタデータ | バッチ終了時 | destroyed_pipeline_artifacts_count |
| エラーログ | エラー発生時 | 例外情報 |

## 監視・アラート

| 監視項目 | 閾値 | アラート先 |
|---------|-----|----------|
| 処理時間 | 10分超過 | 運用チーム |
| 削除件数 | 異常増減 | 運用チーム |
| destroyed_pipeline_artifacts_count_total | 監視 | Prometheus |

## 備考

- `idempotent!`が設定されており、冪等性が保証されている
- `data_consistency :always`が設定されており、プライマリDBを使用
- `feature_category :job_artifacts`でジョブアーティファクト機能として分類
- `deduplicate :until_executed, including_scheduled: true`で重複実行防止
- BATCH_SIZE = 100, LOOP_LIMIT = 1000, LOOP_TIMEOUT = 5.minutes
- LOCK_TIMEOUT = 10.minutes（cron間隔23分より短い）
- artifact_unlockedスコープ：ロックされたアーティファクトは保護される
- Prometheusメトリクス `destroyed_pipeline_artifacts_count_total`で削除数を監視
- ジョブアーティファクト（ci_job_artifacts）とは別テーブルで管理
