# バッチ設計書 75-Environments::AutoStopCronWorker

## 概要

本ドキュメントは、期限切れ環境を自動停止するバッチ `Environments::AutoStopCronWorker` の設計仕様を記載する。

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

このバッチは、自動停止時刻（auto_stop_at）が過ぎた環境（Environment）を検出し、停止処理をトリガーするワーカーである。

**業務上の目的・背景**：GitLabのCI/CD機能では、動的に環境（レビュー環境など）を作成できる。これらの環境は一時的なものであり、一定期間後に自動的に停止することでリソースを節約する必要がある。例えば、マージリクエストに関連付けられたレビュー環境は、マージ後に不要となるため自動停止が重要である。このバッチは、設定された自動停止時刻を過ぎた環境を定期的に検出し、停止処理を実行することで、クラウドリソースの効率的な利用とコスト削減に貢献する。

**バッチの実行タイミング**：毎時24分（`24 * * * *`）にCronジョブとして実行される。

**主要な処理内容**：
1. 排他ロック（50分TTL）を取得
2. 自動停止対象の環境を100件ずつバッチ取得
3. 各環境に対して `AutoStopWorker` をエンキュー
4. 最大45分または1000回のループで処理

**前後の処理との関連**：`Environments::AutoStopService` が実際の処理ロジックを実装し、`Environments::AutoStopWorker` が個々の環境の停止処理を実行する。

**影響範囲**：`environments` テーブルの状態、関連するKubernetesリソースやサーバー

## バッチ種別

リソース管理 / 自動停止処理

## 実行スケジュール

| 項目 | 内容 |
|-----|------|
| 実行頻度 | 毎時 |
| 実行時刻 | 毎時24分 |
| 実行曜日 | 全曜日 |
| 実行日 | 毎日 |
| トリガー | cron（`24 * * * *`） |

## 実行条件

### 前提条件

| 条件 | 説明 |
|-----|------|
| 排他ロック取得 | 同時実行を防ぐための排他ロック |
| データベース接続 | 読み書き可能な接続 |
| Sidekiq | ジョブのエンキューが可能であること |

### 実行可否判定

- 排他ロック取得成功時のみ実行
- 自動停止対象の環境が存在しない場合はループ終了

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | デフォルト値 | 説明 |
|-------------|-----|-----|-------------|------|
| なし | - | - | - | このワーカーはパラメータを受け取らない |

### 入力データソース

| データソース | 形式 | 説明 |
|-------------|------|------|
| environments | DB | 自動停止対象の環境レコード |

## 出力仕様

### 出力データ

| 出力先 | 形式 | 説明 |
|-------|------|------|
| Sidekiq Queue | Job | Environments::AutoStopWorker ジョブ |

### 出力ファイル仕様

ファイル出力なし

## 処理フロー

### 処理シーケンス

```
1. 排他ロック取得
   └─ キー: "environments:auto_stop:lock"
   └─ TTL: 50分、リトライ: 1回
2. ループ処理開始
   └─ タイムアウト: 45分
   └─ 最大ループ回数: 1000回
3. バッチ取得（100件）
   └─ Environment.auto_stoppable(100)
   └─ available状態かつauto_stop_at < 現在時刻
4. AutoStopWorkerエンキュー
   └─ bulk_perform_async_with_contexts
   └─ 環境IDとプロジェクトコンテキストを渡す
5. ループ継続判定
   └─ 対象がなくなるか、タイムアウト/回数上限まで継続
```

### フローチャート

```mermaid
flowchart TD
    A[バッチ開始] --> B{排他ロック取得}
    B -->|失敗| C[処理終了]
    B -->|成功| D[ループ開始]
    D --> E[環境取得 100件]
    E --> F{環境あり?}
    F -->|No| G[ロック解放]
    F -->|Yes| H[AutoStopWorkerエンキュー]
    H --> I{タイムアウト or 上限?}
    I -->|No| D
    I -->|Yes| G
    G --> C
```

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

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

| 処理 | 対象テーブル | 操作種別 | 概要 |
|-----|-------------|---------|------|
| auto_stoppable | environments | SELECT | 自動停止対象環境の取得 |

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

#### environments

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| SELECT | id, project_id | state = available AND auto_stop_at < NOW() | LIMIT 100 |

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| - | ロック取得失敗 | 他プロセスがロック保持中 | 処理をスキップ |
| - | データベースエラー | DB接続失敗時 | Sidekiqによる自動リトライ |

### リトライ仕様

| 項目 | 内容 |
|-----|------|
| ロック取得リトライ | 1回 |
| ロックTTL | 50分 |
| Sidekiqリトライ | デフォルト設定 |

### 障害時対応

- ロック取得失敗時は次回の毎時実行で再試行
- 個々の環境停止失敗は `AutoStopWorker` 側でハンドリング

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

| 項目 | 内容 |
|-----|------|
| トランザクション範囲 | なし（読み取りとエンキューのみ） |
| コミットタイミング | 該当なし |
| ロールバック条件 | 該当なし |

## パフォーマンス要件

| 項目 | 内容 |
|-----|------|
| 想定処理件数 | バッチサイズ100件 × 最大1000ループ |
| 目標処理時間 | 45分以内 |
| メモリ使用量上限 | バッチサイズで制御 |

## 排他制御

- `Gitlab::ExclusiveLeaseHelpers` を使用した排他ロック
- ロックキー: `environments:auto_stop:lock`
- TTL: 50分
- リトライ: 1回
- `Gitlab::LoopHelpers` によるループ制御

## ログ出力

| ログ種別 | 出力タイミング | 出力内容 |
|---------|--------------|---------|
| 開始ログ | ジョブ開始時 | ジョブID、クラス名 |
| 終了ログ | ジョブ完了時 | 処理結果 |
| エラーログ | エラー発生時 | 例外情報 |

## 監視・アラート

| 監視項目 | 閾値 | アラート先 |
|---------|-----|----------|
| ジョブ失敗率 | 継続的な失敗 | Sidekiq監視システム |
| 処理時間 | 50分超過 | 管理者通知 |

## 備考

- このワーカーは `idempotent!` として宣言されていない
- feature_category は `continuous_delivery`
- data_consistency は `always` に設定
- worker_resource_boundary は `cpu` に設定（CPU集約型処理）
- 毎時24分の実行で、他のCronジョブとの競合を避けている
- `AutoStopWorker` が実際の環境停止処理を実行
- ループタイムアウト45分はロックTTL 50分より短く設定されている
