# バッチ設計書 7-Internal::ProcessStaleContactsJob

## 概要

本ドキュメントは、Chatwootアプリケーションにおける`Internal::ProcessStaleContactsJob`バッチの設計仕様を記載したものである。

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

このバッチは、古いコンタクト削除ジョブをアカウント単位でスケジュールするジョブである。Chatwoot Cloud環境専用のハウスキーピング処理として、5日周期で分散処理を行う。

**業務上の目的・背景**：Chatwoot Cloudでは、識別情報（メール、電話番号、識別子）がなく会話もない古いコンタクトが蓄積し、データベースのストレージとパフォーマンスに悪影響を与える。このバッチは、大量のアカウントに対する削除処理を一度に実行するのではなく、5日周期で分散させることで、システムへの負荷を軽減しながらデータクリーンアップを行う。

**バッチの実行タイミング**：日次で04:30 UTC（IST 10:00 AM）に実行される。Chatwoot Cloud環境でのみ動作する。

**主要な処理内容**：
1. Chatwoot Cloud環境であることを確認
2. 日付ベースでアカウントを5グループに分散（ID % 5で判定）
3. 対象グループのアカウントに対してRemoveStaleContactsJobをキューイング
4. 1〜10分のランダム遅延を設定してキュー圧力を軽減

**前後の処理との関連**：本バッチは親ジョブとして機能し、Internal::RemoveStaleContactsJobを子ジョブとしてトリガーする。子ジョブが実際のコンタクト削除処理を行う。

**影響範囲**：直接的なデータ変更は行わない。子ジョブをキューイングするのみ。

## バッチ種別

データクリーンアップ / ジョブスケジューリング

## 実行スケジュール

| 項目 | 内容 |
|-----|------|
| 実行頻度 | 日次 |
| 実行時刻 | 04:30 UTC（IST 10:00 AM） |
| 実行曜日 | 毎日 |
| 実行日 | - |
| トリガー | cron（sidekiq-cron） |

## 実行条件

### 前提条件

| 条件 | 説明 |
|-----|------|
| Chatwoot Cloud環境 | `ChatwootApp.chatwoot_cloud?`がtrueの場合のみ実行 |

### 実行可否判定

`ChatwootApp.chatwoot_cloud?`がfalseの場合は、`perform`メソッド冒頭で`return`により即座に終了する。

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | デフォルト値 | 説明 |
|-------------|-----|-----|-------------|------|
| なし | - | - | - | 本バッチは引数を取らない |

### 入力データソース

| データソース | 形式 | 説明 |
|-------------|------|------|
| accounts | DB | 対象アカウントの取得（ID % 5 = 日付剰余） |

## 出力仕様

### 出力データ

| 出力先 | 形式 | 説明 |
|-------|------|------|
| Sidekiqキュー | ジョブ | RemoveStaleContactsJobをキューイング（遅延付き） |
| ログ | テキスト | 処理対象アカウントの情報 |

### 出力ファイル仕様

ファイル出力なし

## 処理フロー

### 処理シーケンス

```
1. 実行環境チェック
   └─ ChatwootApp.chatwoot_cloud?がfalseならreturn
2. 日付ベースのグループ決定
   └─ day_of_month = Date.current.day
   └─ remainder = day_of_month % 5（DISTRIBUTION_GROUPS）
3. 対象アカウントの取得
   └─ Account.where("id % 5 = ?", remainder)
   └─ batch_size: 20（MAX_ACCOUNTS_PER_BATCH）で分割取得
4. ログ出力
   └─ 処理対象グループと総アカウント数を出力
5. 子ジョブのキューイング
   └─ 各アカウントに対してRemoveStaleContactsJobをキューイング
   └─ 1〜10分のランダム遅延を設定
   └─ キューイング情報をログ出力
```

### フローチャート

```mermaid
flowchart TD
    A[バッチ開始] --> B{Chatwoot Cloud?}
    B -->|No| C[処理終了]
    B -->|Yes| D[日付からグループ番号計算]
    D --> E[対象アカウント検索]
    E --> F[各アカウントをループ処理]
    F --> G[ランダム遅延を設定]
    G --> H[RemoveStaleContactsJobをキューイング]
    H --> I{次のアカウントあり?}
    I -->|Yes| F
    I -->|No| J[バッチ終了]
```

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

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

| 処理 | 対象テーブル | 操作種別 | 概要 |
|-----|-------------|---------|------|
| アカウント件数取得 | accounts | SELECT COUNT | 総アカウント数の取得（ログ用） |
| 対象アカウント取得 | accounts | SELECT | ID % 5 = 剰余のアカウント取得 |

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

#### accounts

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| SELECT | id | id % 5 = remainder | batch_size: 20で分割取得 |
| SELECT COUNT | - | 全レコード | ログ出力用 |

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| - | StandardError | 予期しないエラー | Sidekiqリトライ機構に委譲 |

### リトライ仕様

| 項目 | 内容 |
|-----|------|
| リトライ回数 | Sidekiqのデフォルト設定に依存 |
| リトライ間隔 | Sidekiqのデフォルト設定に依存 |
| リトライ対象エラー | ジョブ実行時の例外全般 |

### 障害時対応

本ジョブが失敗した場合、翌日の定期実行で再度処理される。5日周期で全アカウントがカバーされるため、1日の失敗は大きな影響にならない。

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

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

## パフォーマンス要件

| 項目 | 内容 |
|-----|------|
| 想定処理件数 | 全アカウントの約20%/日 |
| 目標処理時間 | 数秒（キューイングのみ） |
| メモリ使用量上限 | 最小限（find_eachで分割処理） |

## 排他制御

同時実行の制御は特になし。日次1回のみの実行であり、通常は競合しない。

## ログ出力

| ログ種別 | 出力タイミング | 出力内容 |
|---------|--------------|---------|
| 情報ログ | 処理開始時 | `ProcessStaleContactsJob: Processing accounts with ID % 5 = {remainder} (out of {total} total accounts)` |
| 情報ログ | 子ジョブキューイング時 | `Enqueuing RemoveStaleContactsJob for account {id}` |

## 監視・アラート

| 監視項目 | 閾値 | アラート先 |
|---------|-----|----------|
| ジョブ失敗 | 連続失敗 | Sidekiq監視 |
| キュー滞留 | 大量の子ジョブ滞留 | 運用監視 |

## 備考

- Chatwoot Cloud専用のジョブであり、セルフホスト環境では実行されない
- DISTRIBUTION_GROUPS = 5: アカウントを5グループに分散
- MAX_ACCOUNTS_PER_BATCH = 20: find_eachのバッチサイズ
- 子ジョブには1〜10分のランダム遅延を設定し、キュー圧力を軽減
- housekeepingキューを使用し、通常のジョブとは別のキューで処理される
- 各アカウントは約5日に1回処理される（ID % 5による分散）
