# バッチ設計書 80-GitlabServicePingWorker

## 概要

本ドキュメントは、GitLabのサービスPing（利用状況データ）を送信するバッチ `GitlabServicePingWorker` の設計仕様を記載する。

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

このバッチは、GitLabインスタンスの利用状況データを収集し、GitLab社に送信するワーカーである。収集されたデータは製品改善に活用される。

**業務上の目的・背景**：GitLabはオープンソースプロジェクトであり、ユーザーインスタンスからの利用状況データを収集することで、製品の使用パターンを理解し、機能の優先順位付けやバグ修正に活用している。Service Pingは匿名化されたデータを定期的に収集・送信し、GitLabコミュニティ全体の利益に貢献する。管理者はこの機能を無効化することも可能である。データは非SQLメトリクス、SQLクエリメトリクス、およびペイロード全体の3種類に分けて収集・保存される。

**バッチの実行タイミング**：動的にスケジュールされる（Sidekiq初期化時に設定）。

**主要な処理内容**：
1. usage_ping_generation_enabled? の確認
2. 非SQLデータの収集と保存（日次、排他ロック）
3. SQLクエリデータの収集と保存（日次、排他ロック）
4. 完全なペイロードの構築と送信（GitLab.com以外、日次）
5. RawUsageDataテーブルへのデータ保存

**前後の処理との関連**：`ServicePing::BuildPayload` でペイロードを構築し、`ServicePing::SubmitService` でGitLab社へ送信する。

**影響範囲**：`raw_usage_data` テーブル、`service_ping_non_sql_service_pings` テーブル、`service_ping_queries_service_pings` テーブル

## バッチ種別

データ収集 / 外部送信

## 実行スケジュール

| 項目 | 内容 |
|-----|------|
| 実行頻度 | 日次（動的スケジュール） |
| 実行時刻 | Sidekiq初期化時に動的設定 |
| 実行曜日 | 全曜日 |
| 実行日 | 毎日 |
| トリガー | cron（動的設定） |

## 実行条件

### 前提条件

| 条件 | 説明 |
|-----|------|
| 設定有効 | usage_ping_generation_enabled? が true |
| データベース接続 | 読み書き可能な接続 |

### 実行可否判定

- `Gitlab::CurrentSettings.usage_ping_generation_enabled?` が `true` の場合のみ処理を実行
- GitLab.comではCronトリガー時は送信をスキップ（手動実行時のみ送信）

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | デフォルト値 | 説明 |
|-------------|-----|-----|-------------|------|
| options | Hash | No | {} | オプション設定 |
| options['triggered_from_cron'] | Boolean | No | true | Cronからの実行かどうか |

### 入力データソース

| データソース | 形式 | 説明 |
|-------------|------|------|
| 各種テーブル | DB | 利用状況メトリクスの収集元 |
| Redis | Cache | 一部メトリクスの収集元 |
| システム情報 | System | Git/Ruby/OS等のバージョン情報 |

## 出力仕様

### 出力データ

| 出力先 | 形式 | 説明 |
|-------|------|------|
| raw_usage_data | DB | 完全なペイロードの保存 |
| service_ping_non_sql_service_pings | DB | 非SQLメトリクス |
| service_ping_queries_service_pings | DB | SQLクエリメトリクス |
| GitLab社API | HTTPS | ペイロードの送信 |

### 出力ファイル仕様

ファイル出力なし

## 処理フロー

### 処理シーケンス

```
1. 設定確認
   └─ usage_ping_generation_enabled? の確認
2. 非SQLデータ収集（日次ロック）
   └─ NON_SQL_LEASE_KEY で排他ロック
   └─ ServicePingReport.for(output: :non_sql_metrics_values)
   └─ NonSqlServicePing.upsert
3. SQLクエリデータ収集（日次ロック）
   └─ QUERIES_LEASE_KEY で排他ロック
   └─ ServicePingReport.for(output: :metrics_queries)
   └─ QueriesServicePing.upsert
4. GitLab.com判定
   └─ GitLab.comかつCronトリガーならスキップ
5. ペイロード送信（日次ロック）
   └─ LEASE_KEY で排他ロック
   └─ BuildPayload.new.execute
   └─ RawUsageData.upsert
   └─ SubmitService.new.execute
```

### フローチャート

```mermaid
flowchart TD
    A[バッチ開始] --> B{設定有効?}
    B -->|No| C[処理終了]
    B -->|Yes| D[非SQLデータ収集]
    D --> E[NonSqlServicePing保存]
    E --> F[SQLクエリデータ収集]
    F --> G[QueriesServicePing保存]
    G --> H{GitLab.com & Cron?}
    H -->|Yes| C
    H -->|No| I[ペイロード構築]
    I --> J[RawUsageData保存]
    J --> K[GitLab社へ送信]
    K --> C
```

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

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

| 処理 | 対象テーブル | 操作種別 | 概要 |
|-----|-------------|---------|------|
| save_non_sql_data | service_ping_non_sql_service_pings | UPSERT | 非SQLメトリクス保存 |
| save_queries_data | service_ping_queries_service_pings | UPSERT | SQLクエリメトリクス保存 |
| usage_data | raw_usage_data | UPSERT | 完全ペイロード保存 |

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

#### raw_usage_data

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| UPSERT | recorded_at, payload, organization_id | unique_by: [:organization_id, :recorded_at] | 日次データ |

#### service_ping_non_sql_service_pings

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| UPSERT | recorded_at, payload, metadata, organization_id | unique_by: [:organization_id, :recorded_at] | 非SQLメトリクス |

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| - | StandardError | データ収集エラー | track_and_raise_for_dev_exception、処理継続 |
| - | 送信エラー | API通信失敗時 | リトライ（8時間間隔） |

### リトライ仕様

| 項目 | 内容 |
|-----|------|
| リトライ回数 | 3回 |
| リトライ間隔 | (count + 1) * 8時間 |
| dead | false（dead queueに入れない） |

### 障害時対応

- エラー発生時はdev環境では例外をraise、本番では追跡のみ
- リトライ間隔が長いため、日次処理として適切に分散

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

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

## パフォーマンス要件

| 項目 | 内容 |
|-----|------|
| 想定処理件数 | 1回/日（各データタイプ） |
| 目標処理時間 | 数分〜数十分（インスタンス規模に依存） |
| メモリ使用量上限 | ペイロードサイズに依存 |

## 排他制御

- `Gitlab::ExclusiveLeaseHelpers` を使用した排他ロック
- LEASE_KEY: `gitlab_service_ping_worker:ping`（完全ペイロード）
- NON_SQL_LEASE_KEY: `gitlab_service_ping_worker:non_sql_ping`
- QUERIES_LEASE_KEY: `gitlab_service_ping_worker:queries_ping`
- LEASE_TIMEOUT: 86400秒（24時間）
- 処理開始前に0〜60秒のランダムスリープ（Thundering herd対策）

## ログ出力

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

## 監視・アラート

| 監視項目 | 閾値 | アラート先 |
|---------|-----|----------|
| ジョブ失敗率 | 継続的な失敗 | Sidekiq監視システム |
| データ送信失敗 | 複数日連続 | 管理者通知 |

## 備考

- このワーカーは `idempotent!` として宣言されていない
- feature_category は `service_ping`
- data_consistency は `sticky` に設定
- worker_resource_boundary は `cpu` に設定
- sidekiq_options: retry: 3, dead: false
- リトライ間隔: (count + 1) * 8時間
- GitLab.comではCronトリガー時は送信をスキップ（https://gitlab.com/gitlab-org/gitlab/-/issues/292929）
- Organizations::Organization.first を使用してorganization_idを取得
- ランダムスリープ（0〜60秒）で複数インスタンスの同時リクエストを分散
- `prepend_mod` でEE拡張が可能
