# バッチ設計書 69-Gitlab::Import::StuckImportJob

## 概要

本ドキュメントは、停滞したインポートジョブを検出して失敗状態にマークするバッチ処理モジュール（Gitlab::Import::StuckImportJob）の設計を記載します。

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

GitLabのプロジェクトインポート機能で、Sidekiqジョブが完了または消失しているにも関わらずインポートステータスが更新されていない（停滞した）インポートを検出し、失敗状態にマークする共通モジュールです。

**業務上の目的・背景**：プロジェクトインポートはSidekiqジョブとして非同期実行されます。しかし、Sidekiqジョブが異常終了した場合、ワーカーがクラッシュした場合、またはJIDが設定されないまま放置された場合、インポートステータスが「インポート中」のまま残ることがあります。本モジュールをincludeしたワーカーは、24時間以上経過した停滞インポートを検出して失敗状態にマークすることで、ユーザーへの正確な状態通知とシステムリソースの適切な管理を実現します。

**バッチの実行タイミング**：このモジュールをincludeするワーカーによって異なる（例：毎時15分など）

**主要な処理内容**：
1. JIDなしのエンキュー済みインポートを検索して失敗マーク
2. JIDありのエンキュー済みインポートを検索
3. Gitlab::SidekiqStatusで完了済みJIDを確認
4. 完了済みJIDのインポートを失敗状態にマーク
5. メトリクスを記録

**前後の処理との関連**：プロジェクトインポートを開始するワーカー（ProjectImportScheduleWorker等）の異常終了を検知する監視的な役割を担います。

**影響範囲**：project_mirror_data、import_stateなど、インポート状態を管理するテーブル

## バッチ種別

データ状態管理 / 停滞ジョブ検出（共通モジュール）

## 実行スケジュール

| 項目 | 内容 |
|-----|------|
| 実行頻度 | このモジュールをincludeするワーカーに依存 |
| 実行時刻 | - |
| 実行曜日 | - |
| 実行日 | - |
| トリガー | cron |

## 実行条件

### 前提条件

| 条件 | 説明 |
|-----|------|
| インポート機能が有効 | プロジェクトインポート機能が利用可能であること |
| データベース接続 | PostgreSQLへの接続が確立されていること |
| Redis接続 | Sidekiqステータス確認のためのRedis接続が可能であること |

### 実行可否判定

特別な実行可否判定ロジックはありません。cronスケジュールに従って自動実行されます。

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | デフォルト値 | 説明 |
|-------------|-----|-----|-------------|------|
| なし | - | - | - | パラメータなしで実行 |

### 入力データソース

| データソース | 形式 | 説明 |
|-------------|------|------|
| インポート状態テーブル | DB | includeするワーカーが定義するスコープに依存 |
| Redis (Sidekiq) | KVS | ジョブの実行状態 |

## 出力仕様

### 出力データ

| 出力先 | 形式 | 説明 |
|-------|------|------|
| インポート状態テーブル | DB | ステータスをfailedに更新 |
| ログ | ファイル/stdout | 停滞インポートの検出ログ |
| Gitlab::Metrics | メトリクス | track_metricsで出力（実装依存） |

### 出力ファイル仕様

ファイル出力はありません。

## 処理フロー

### 処理シーケンス

```
1. JIDなしインポートの処理
   └─ enqueued_import_states_without_jid で検索
   └─ 各インポートを失敗状態にマーク
   └─ 件数をカウント
2. JIDありインポートの処理
   └─ enqueued_import_states_with_jid で検索
   └─ pluck(:jid, :id) でJIDリストを抽出
   └─ Gitlab::SidekiqStatus.completed_jids で完了JIDを取得
   └─ 完了JIDに対応するインポート状態を再取得
   └─ 各インポートを失敗状態にマーク
   └─ 件数をカウント
3. メトリクス記録
   └─ track_metrics(with_jid_count, without_jid_count)
```

### フローチャート

```mermaid
flowchart TD
    A[バッチ開始] --> B[JIDなしインポート検索<br/>without_jid]
    B --> C{インポート存在?}
    C -->|あり| D[mark_as_failed<br/>失敗状態へ]
    D --> C
    C -->|なし| E[JIDありインポート検索<br/>with_jid]
    E --> F[JIDリスト抽出]
    F --> G[Sidekiq完了状態確認<br/>completed_jids]
    G --> H{完了JID存在?}
    H -->|なし| L[メトリクス記録<br/>track_metrics]
    H -->|あり| I[完了インポート再取得]
    I --> J[ログ出力]
    J --> K[mark_as_failed<br/>失敗状態へ]
    K --> L
    L --> M[バッチ終了]
```

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

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

| 処理 | 対象テーブル | 操作種別 | 概要 |
|-----|-------------|---------|------|
| インポート状態検索 | implementorが定義 | SELECT | エンキュー済みインポートを検索 |
| インポート状態更新 | implementorが定義 | UPDATE | ステータスをfailedに更新 |

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

このモジュールは共通モジュールのため、操作対象テーブルはincludeするワーカーが定義します。

#### 共通インターフェース

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| SELECT | jid, id, status | enqueued_import_statesで定義 | with_jid/without_jidスコープ使用 |
| UPDATE | status, last_error | 失敗状態 | ImportFailureServiceで更新 |

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| - | StuckImportJobError | タイムアウト検出時に意図的に発生 | ImportFailureServiceで記録 |
| - | Redis::ConnectionError | Redis接続エラー | Sidekiqリトライ |

### リトライ仕様

| 項目 | 内容 |
|-----|------|
| リトライ回数 | Sidekiqデフォルト（25回） |
| リトライ間隔 | 指数バックオフ |
| リトライ対象エラー | 一時的なエラー全般 |

### 障害時対応

処理が中断された場合、次回実行時に未処理の停滞インポートが再度対象となります。mark_as_failedは冪等な操作のため、重複実行しても問題ありません。

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

| 項目 | 内容 |
|-----|------|
| トランザクション範囲 | レコード単位 |
| コミットタイミング | ImportFailureServiceの完了時 |
| ロールバック条件 | 各レコードの更新失敗時 |

## パフォーマンス要件

| 項目 | 内容 |
|-----|------|
| 想定処理件数 | 通常は少数（停滞は異常ケース） |
| 目標処理時間 | 数分以内 |
| メモリ使用量上限 | JIDリストサイズに依存 |

## 排他制御

CronjobQueueがincludeされているため、同じ時刻に重複実行されることはありません。Sidekiqステータスの確認と状態遷移は冪等な操作です。

## ログ出力

| ログ種別 | 出力タイミング | 出力内容 |
|---------|--------------|---------|
| 情報ログ | 停滞インポート検出時 | message: 'Marked stuck import jobs as failed', job_ids |

## 監視・アラート

| 監視項目 | 閾値 | アラート先 |
|---------|-----|----------|
| 停滞インポート件数 | track_metricsで実装者が定義 | Gitlab::Metrics |

## 備考

- IMPORT_JOBS_EXPIRATION = 24.hours.seconds.to_i（86400秒）でタイムアウト時間を定義
- このモジュールはActiveSupport::Concernとして実装
- includeするワーカーは以下のメソッドを実装する必要があります：
  - `track_metrics(with_jid_count, without_jid_count)` - メトリクス記録
  - `enqueued_import_states` - エンキュー済みインポート状態のスコープ
- worker_resource_boundary: :cpu が設定されており、CPU負荷の高いワーカーとして分類
- Gitlab::Import::ImportFailureServiceを使用してエラー追跡と状態更新を実行
- StuckImportJobErrorを意図的にraiseし、rescueしてImportFailureServiceに渡すパターン
- Import::Framework::Loggerを使用した構造化ログ出力
