# バッチ設計書 2-StuckCiJobsWorker

## 概要

本ドキュメントは、停滞したCIジョブを検出して削除するバッチ処理「StuckCiJobsWorker」の設計仕様を記載する。

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

StuckCiJobsWorkerは、GitLabのCI/CDシステムにおいて、異常な状態で停滞しているジョブを検出し、適切にクリーンアップするための中央管理バッチ処理である。ランナーとの通信断や予期せぬエラーにより、完了せず放置されたジョブをシステムから除去する。

**業務上の目的・背景**：CI/CDパイプラインにおいて、ランナーの障害、ネットワーク問題、予期せぬクラッシュなどにより、ジョブが完了も失敗もせずに停滞することがある。これらの「ゾンビジョブ」は、パイプラインの完了を妨げ、リソースを占有し、ユーザーに誤った状態を表示し続ける。本バッチは、このような停滞ジョブを定期的に検出・削除することで、CI/CDシステムの健全性を維持する。

**バッチの実行タイミング**：毎時0分（60分間隔）で実行（cron: `0 * * * *`）。1時間に1回のペースでシステム全体の停滞ジョブをスキャン・処理する。

**主要な処理内容**：
1. 3つの子ワーカー（DropRunning、DropScheduled、DropCanceling）をスタガー実行でスケジュール
2. 各ワーカーは15分間隔で順次実行されるよう設定
3. 自身は排他ロック取得後、DropPendingServiceを実行してペンディング状態のビルドを処理
4. 各サービスは1時間以上更新のないジョブを「停滞」と判定し削除

**前後の処理との関連**：本ワーカーは3つの子ワーカー（Ci::StuckBuilds::DropRunningWorker、Ci::StuckBuilds::DropScheduledWorker、Ci::StuckBuilds::DropCancelingWorker）をトリガーする親ワーカーとして機能する。各子ワーカーは独立して実行され、特定の状態のジョブを処理する。

**影響範囲**：全プロジェクトのCI/CDジョブに影響。停滞ジョブの削除により、パイプラインの状態が更新され、関連する通知やステータス表示に影響する。

## バッチ種別

データクレンジング / システムメンテナンス

## 実行スケジュール

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

## 実行条件

### 前提条件

| 条件 | 説明 |
|-----|------|
| 排他ロック取得 | ExclusiveLeaseGuardによるロック取得が可能であること |

### 実行可否判定

排他ロック（ExclusiveLeaseGuard）を取得できた場合のみ`DropPendingService`を実行する。ロックのTTLは30分。子ワーカーのスケジューリングは常に実行される。

## 入力仕様

### 入力パラメータ

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

### 入力データソース

| データソース | 形式 | 説明 |
|-------------|------|------|
| ci_builds | DB | CIビルド（ジョブ）テーブル |

## 出力仕様

### 出力データ

| 出力先 | 形式 | 説明 |
|-------|------|------|
| ci_builds | DB | ステータス更新（dropped） |
| Sidekiqキュー | ジョブ | 子ワーカーへのエンキュー |

### 出力ファイル仕様

ファイル出力なし。

## 処理フロー

### 処理シーケンス

```
1. スタガー実行スケジュール
   └─ DropRunningWorker、DropScheduledWorker、DropCancelingWorkerを15分間隔でスケジュール
2. 排他ロック取得
   └─ ExclusiveLeaseGuard.try_obtain_leaseでロック取得（TTL: 30分）
3. ペンディングビルド処理
   └─ Ci::StuckBuilds::DropPendingService.new.executeを実行
4. ロック解放
   └─ 処理完了後自動解放
```

### フローチャート

```mermaid
flowchart TD
    A[バッチ開始] --> B[子ワーカーをスタガースケジュール]
    B --> C[DropRunningWorker: 15分後]
    B --> D[DropScheduledWorker: 30分後]
    B --> E[DropCancelingWorker: 45分後]
    B --> F{排他ロック取得}
    F -->|成功| G[DropPendingService実行]
    F -->|失敗| H[スキップ]
    G --> I[バッチ終了]
    H --> I
```

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

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

| 処理 | 対象テーブル | 操作種別 | 概要 |
|-----|-------------|---------|------|
| ペンディングビルド取得 | ci_builds | SELECT | pending状態で1日以上古いビルドを取得 |
| ビルドステータス更新 | ci_builds | UPDATE | statusをfailedに更新、failure_reasonを設定 |

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

#### ci_builds

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| SELECT | status, created_at, updated_at | pending状態、1時間or1日以上未更新 | バッチサイズ100件 |
| UPDATE | status, failure_reason | failed, stuck_or_timeout_failure | drop!メソッドで更新 |

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| - | ExclusiveLease取得失敗 | 別プロセスがロック保持中 | スキップ |
| - | StandardError | ビルドdrop処理中のエラー | doom!で代替処理、エラートラッキング |
| - | OptimisticLockingエラー | 同時更新の競合 | 3回リトライ |

### リトライ仕様

| 項目 | 内容 |
|-----|------|
| リトライ回数 | 3回（OptimisticLocking） |
| リトライ間隔 | 即時 |
| リトライ対象エラー | OptimisticLockingエラー |

### 障害時対応

1. ロック取得失敗の場合：次回実行時に自動復旧
2. ビルドdrop失敗の場合：doom!メソッドで強制終了状態にマーク
3. エラー発生の場合：Gitlab::ErrorTrackingでSentryに報告

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

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

## パフォーマンス要件

| 項目 | 内容 |
|-----|------|
| 想定処理件数 | バッチサイズ100件ずつ処理 |
| 目標処理時間 | 30分以内（ロックTTL） |
| メモリ使用量上限 | バッチサイズ100件による制御 |

## 排他制御

- ExclusiveLeaseGuard（TTL: 30分）による排他制御
- 同時に1プロセスのみ実行可能
- 子ワーカーも各自ExclusiveLeaseGuardを使用

## ログ出力

| ログ種別 | 出力タイミング | 出力内容 |
|---------|--------------|---------|
| 開始ログ | サービス開始時 | "Cleaning pending timed-out builds" |
| 進捗ログ | ビルドdrop時 | build_id, runner_id, status, failure_reason |
| 終了ログ | バッチ終了時 | Sidekiq標準ログ |
| エラーログ | エラー発生時 | 例外情報、build詳細 |

## 監視・アラート

| 監視項目 | 閾値 | アラート先 |
|---------|-----|----------|
| 処理時間 | 30分超過 | 運用チーム |
| エラー件数 | 多発時 | 運用チーム |
| 削除ジョブ数 | 異常増加時 | 運用チーム |

## 備考

- `data_consistency :always`が設定されており、プライマリDBを使用
- インスタンス全体のクリーンアップクエリのため、CronWorkerContextは無効化
- 子ワーカーへの処理委譲により、負荷を時間分散
- MINUTES_BETWEEN_RUNS = 60 で1時間ごとの実行を前提とした設計
