# バッチ設計書 20-Database::CiNamespaceMirrorsConsistencyCheckWorker

## 概要

本ドキュメントは、GitLabにおけるCI名前空間ミラーの整合性チェックを行うCronワーカー `Database::CiNamespaceMirrorsConsistencyCheckWorker` の設計仕様を定義する。

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

本バッチは、`namespaces` テーブルと `ci_namespace_mirrors` テーブル間のデータ整合性を検証し、不整合を検出した場合は自動修復を行う。CIデータベース分離アーキテクチャにおけるデータミラーリングの信頼性を維持する役割を担う。

**業務上の目的・背景**：GitLabのCellアーキテクチャでは、メインデータベースとCIデータベースを分離している。`ci_namespace_mirrors` テーブルは、メインDBの `namespaces` テーブルの必要な情報をCIデータベースにミラーリングしたものである。このミラーリングにより、CIパイプライン処理時にメインDBへのクロスデータベースクエリを回避できる。本バッチは、このミラーリングの整合性を定期的にチェックし、不整合があれば同期イベントを発行して修復する。

**バッチの実行タイミング**：Cronジョブとして定期的に実行される。

**主要な処理内容**：
1. `ConsistencyCheckService` で `namespaces` と `ci_namespace_mirrors` を比較
2. 不整合（mismatches）を検出
3. 不整合がある場合、`ConsistencyFixService` で修復
4. 結果をメタデータとしてログ出力

**前後の処理との関連**：`Namespaces::SyncEvent` を発行し、ミラーリング同期処理をトリガーする。`Database::CiProjectMirrorsConsistencyCheckWorker` と類似の役割を持つ（対象テーブルが異なる）。

**影響範囲**：`namespaces` テーブル、`ci_namespace_mirrors` テーブル、`namespaces_sync_events` テーブル。

## バッチ種別

データ整合性チェック / 自動修復

## 実行スケジュール

| 項目 | 内容 |
|-----|------|
| 実行頻度 | 定期実行（Cronジョブ） |
| 実行時刻 | 継続的 |
| 実行曜日 | 毎日 |
| 実行日 | 毎日 |
| トリガー | cron |

## 実行条件

### 前提条件

| 条件 | 説明 |
|-----|------|
| データ存在 | `namespaces` テーブルにレコードが存在すること |

### 実行可否判定

`namespaces` テーブルが空の場合は空結果を返す。

## 入力仕様

### 入力パラメータ

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

### 入力データソース

| データソース | 形式 | 説明 |
|-------------|------|------|
| namespaces | DB | ソーステーブル（メインDB） |
| ci_namespace_mirrors | DB | ターゲットテーブル（CIDB） |
| Redis SharedState | Key-Value | カーソル位置の保存 |

## 出力仕様

### 出力データ

| 出力先 | 形式 | 説明 |
|-------|------|------|
| namespaces_sync_events | DB | 同期イベントレコード |
| ci_namespace_mirrors | DB | 不整合レコードの削除（ソースにない場合） |
| ログ | メタデータ | matches, mismatches, batches等 |
| Redis SharedState | Key-Value | 次回開始カーソル位置 |

### 出力ファイル仕様

ファイル出力なし。

## 処理フロー

### 処理シーケンス

```
1. ConsistencyCheckService 初期化
   └─ source_model: Namespace
   └─ target_model: Ci::NamespaceMirror
   └─ 比較カラム: id/traversal_ids ⇔ namespace_id/traversal_ids
2. 整合性チェック実行
   └─ Redisからカーソル位置取得（または乱数で初期化）
   └─ 最大25バッチ（1000件/バッチ）または30秒まで処理
3. 不整合検出時の修復
   └─ ソースとターゲット両方に存在: SyncEvent作成
   └─ ターゲットのみ存在: ターゲットレコード削除
4. カーソル位置をRedisに保存
5. 結果をメタデータログ出力
```

### フローチャート

```mermaid
flowchart TD
    A[バッチ開始] --> B[ConsistencyCheckService初期化]
    B --> C[Redisからカーソル取得]
    C --> D{カーソル存在?}
    D -->|No| E[ランダムstart_id生成]
    D -->|Yes| F[カーソル位置から開始]
    E --> G[バッチ単位で整合性チェック]
    F --> G
    G --> H{不整合あり?}
    H -->|No| I[カーソル更新]
    H -->|Yes| J[ConsistencyFixService実行]
    J --> K{ソースに存在?}
    K -->|Yes| L[SyncEvent作成]
    K -->|No| M[ターゲットレコード削除]
    L --> N[enqueue_worker呼び出し]
    M --> N
    N --> I
    I --> O[メタデータログ出力]
    O --> P[バッチ終了]
```

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

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

| 処理 | 対象テーブル | 操作種別 | 概要 |
|-----|-------------|---------|------|
| 整合性チェック | namespaces | SELECT | ソースデータ取得 |
| 整合性チェック | ci_namespace_mirrors | SELECT | ターゲットデータ取得 |
| 修復（同期） | namespaces_sync_events | INSERT | 同期イベント作成 |
| 修復（削除） | ci_namespace_mirrors | DELETE | 孤立レコード削除 |

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

#### namespaces

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| SELECT | id, traversal_ids | id >= start_id | バッチ処理 |

#### ci_namespace_mirrors

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| SELECT | namespace_id, traversal_ids | namespace_id >= start_id | バッチ処理 |
| DELETE | - | ソースに存在しないレコード | destroy! |

#### namespaces_sync_events

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| INSERT | namespace_id | 不整合のnamespace_id | 修復時 |

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| - | DB接続エラー | データベース接続失敗 | リトライなし（retry: false） |
| - | Redis接続エラー | カーソル保存失敗 | 次回はランダム位置から再開 |

### リトライ仕様

| 項目 | 内容 |
|-----|------|
| リトライ回数 | 0（retry: false） |
| リトライ間隔 | N/A |
| リトライ対象エラー | なし |

### 障害時対応

処理失敗時は次回実行でランダム位置から再開。部分的な整合性チェックでも問題なく、複数回の実行で全体をカバーする設計。

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

| 項目 | 内容 |
|-----|------|
| トランザクション範囲 | 修復処理単位 |
| コミットタイミング | 各修復操作後 |
| ロールバック条件 | INSERT/DELETE失敗時 |

## パフォーマンス要件

| 項目 | 内容 |
|-----|------|
| 想定処理件数 | 最大25バッチ × 1000件 = 25,000件 |
| 目標処理時間 | 最大30秒 |
| メモリ使用量上限 | バッチサイズ分 |

## 排他制御

- 本ワーカーはidempotent（冪等）として設計
- 同時実行されても安全（異なるカーソル位置から処理）

## ログ出力

| ログ種別 | 出力タイミング | 出力内容 |
|---------|--------------|---------|
| 開始ログ | Sidekiqワーカー開始時 | ジョブID、ワーカー名 |
| 終了ログ | バッチ終了時 | matches, mismatches, batches, start_id, next_start_id, mismatches_details |
| エラーログ | エラー発生時 | 例外情報 |

## 監視・アラート

| 監視項目 | 閾値 | アラート先 |
|---------|-----|----------|
| mismatches | 0より大きい場合（継続的） | 運用チーム |
| 処理時間 | 30秒超過 | 運用チーム |

## 備考

- data_consistency は `:sticky` に設定
- feature_category は `:cell` に設定
- version 1 として管理
- sidekiq_options retry: false（リトライ無効）
- CURSOR_REDIS_KEY_TTL = 7日（カーソル保持期間）
- カーソルが失われた場合はランダム位置から再開（全テーブルを均等にカバー）
- 比較カラム: namespaces(id, traversal_ids) ⇔ ci_namespace_mirrors(namespace_id, traversal_ids)
