# 機能設計書 8-プロジェクト統計

## 概要

本ドキュメントは、GitLabにおけるプロジェクト統計機能の詳細設計を記述する。この機能は、プロジェクトのリポジトリサイズ、コミット数、Wiki サイズ、LFSオブジェクトサイズ等の各種統計情報を管理・更新する機能である。

### 本機能の処理概要

プロジェクト統計機能は、プロジェクトに関連する各種サイズ情報やカウンターを一元管理し、ストレージ使用量の監視やクォータ管理のための基盤を提供する。

**業務上の目的・背景**：プロジェクトのストレージ使用量を正確に把握し、課金計算、クォータ管理、リソース計画のための情報を提供する必要がある。プロジェクト統計により、管理者やユーザーはストレージ消費を監視し、適切なリソース配分を行える。

**機能の利用シーン**：
- プロジェクトのストレージ使用量を確認する時
- 名前空間全体のストレージ集計を行う時
- クォータ超過の警告を表示する時
- 課金計算のためのストレージ使用量を算出する時

**主要な処理内容**：
1. リポジトリサイズの計算
2. コミット数の計算
3. Wiki サイズの計算
4. LFS オブジェクトサイズの計算
5. スニペットサイズの計算
6. アップロードサイズの計算
7. Container Registry サイズの計算
8. ビルドアーティファクトサイズの計算
9. パッケージサイズの計算
10. 総ストレージサイズの計算

**関連システム・外部連携**：
- Gitaly（リポジトリサイズ取得）
- Container Registry API（コンテナサイズ取得）
- 名前空間集計ワーカー

**権限による制御**：
- 統計情報の参照は一般的にプロジェクトメンバーに許可
- 一部詳細情報は管理者のみ

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 27 | プロジェクト統計 | 主画面 | 統計情報表示 |

## 機能種別

データ集計 / バッチ処理 / イベント駆動

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| only | Array | No | 更新対象カラムの指定 | COLUMNS_TO_REFRESHに含まれるカラムのみ |

### 入力データソース

- リポジトリ（Gitaly）
- データベース（関連テーブル）
- Container Registry API
- 内部イベント（CI/CD完了等）

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| repository_size | Integer | リポジトリサイズ（バイト） |
| wiki_size | Integer | Wiki サイズ（バイト） |
| lfs_objects_size | Integer | LFS オブジェクトサイズ（バイト） |
| commit_count | Integer | コミット数 |
| snippets_size | Integer | スニペットサイズ（バイト） |
| uploads_size | Integer | アップロードサイズ（バイト） |
| container_registry_size | Integer | Container Registry サイズ（バイト） |
| build_artifacts_size | Integer | ビルドアーティファクトサイズ（バイト） |
| packages_size | Integer | パッケージサイズ（バイト） |
| storage_size | Integer | 総ストレージサイズ（バイト） |

### 出力先

- データベース（project_statisticsテーブル）
- 名前空間集計（非同期）

## 処理フロー

### 処理シーケンス

```
1. refresh!メソッド呼び出し
   └─ 読み取り専用DBチェック

2. 更新対象カラム決定
   └─ onlyパラメータまたは全カラム

3. 各カラムの値更新
   ├─ update_repository_size
   ├─ update_wiki_size
   ├─ update_lfs_objects_size
   ├─ update_commit_count
   ├─ update_snippets_size
   ├─ update_uploads_size
   └─ update_container_registry_size

4. データベース保存
   └─ save!

5. ストレージサイズ再計算
   └─ refresh_storage_size!

6. 名前空間集計スケジュール
   └─ ScheduleAggregationWorker
```

### フローチャート

```mermaid
flowchart TD
    A[開始: refresh! 呼び出し] --> B{読み取り専用DB?}
    B -->|Yes| C[処理終了]
    B -->|No| D{onlyパラメータ指定?}
    D -->|Yes| E[指定カラムのみ更新]
    D -->|No| F[全カラム更新]
    E --> G[各update_*メソッド実行]
    F --> G
    G --> H[save!]
    H --> I{NAMESPACE_RELATABLE?}
    I -->|Yes| J[名前空間集計スケジュール]
    I -->|No| K[完了]
    J --> K

    C --> L[終了]
    K --> L
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-001 | 読み取り専用回避 | 読み取り専用DBでは更新しない | 常時 |
| BR-002 | 部分更新 | onlyパラメータで指定したカラムのみ更新可能 | only指定時 |
| BR-003 | 名前空間連動 | NAMESPACE_RELATABLEカラム更新時は名前空間集計を実行 | 該当カラム更新時 |
| BR-004 | ストレージサイズ自動計算 | storage_sizeは各コンポーネントの合計 | 常時 |

### 計算ロジック

ストレージサイズ計算：
```ruby
STORAGE_SIZE_COMPONENTS = [
  :repository_size,
  :wiki_size,
  :lfs_objects_size,
  :build_artifacts_size,
  :packages_size,
  :snippets_size,
  :uploads_size
].freeze

# storage_size = 各コンポーネントの合計
storage_size = STORAGE_SIZE_COMPONENTS.sum { |c| self[c] || 0 }
```

リポジトリサイズ：
```ruby
# 最近のオブジェクトサイズをメガバイト単位で取得
self.repository_size = project.repository.recent_objects_size.megabytes
```

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

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

| 操作 | 対象テーブル | 操作種別 | 概要 |
|-----|-------------|---------|------|
| 統計更新 | project_statistics | UPDATE | 各種サイズ更新 |
| ストレージサイズ更新 | project_statistics | UPDATE | storage_size再計算 |
| カウンター増分 | project_statistics | UPDATE | 差分更新 |

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

#### project_statistics

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| UPDATE | repository_size | repository.recent_objects_size * 1MB | リポジトリサイズ |
| UPDATE | wiki_size | wiki.repository.size * 1MB | Wiki サイズ |
| UPDATE | lfs_objects_size | LfsObject.sum(:size) | LFSサイズ |
| UPDATE | commit_count | repository.commit_count | コミット数 |
| UPDATE | snippets_size | snippets.sum(:repository_size) | スニペットサイズ |
| UPDATE | uploads_size | uploads.sum(:size) | アップロードサイズ |
| UPDATE | container_registry_size | container_repositories_size | CRサイズ |
| UPDATE | storage_size | 各コンポーネント合計 | 総ストレージ |

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| E001 | 読み取り専用 | 読み取り専用DBへの書き込み試行 | 処理スキップ |
| E002 | 不正カラム | 非インクリメンタブルカラムへの増分 | ArgumentError |

### リトライ仕様

- 統計更新失敗時は次回バッチで再計算
- カウンター更新はRedis経由で耐障害性確保

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

- refresh!はトランザクションなし（個別save!）
- カウンター属性はRedis経由でバッファリング

## パフォーマンス要件

- 統計更新はバックグラウンドジョブで実行推奨
- 大規模リポジトリではサイズ計算に時間がかかる場合あり

## セキュリティ考慮事項

- 統計情報はプロジェクトアクセス権に基づいて表示
- 詳細なストレージ情報は管理者のみ

## 備考

- build_artifacts_sizeとpackages_sizeはカウンター属性として実装
- 名前空間レベルでの集計は非同期ワーカーで実行
- pending_deleteのプロジェクトは統計更新しない

---

## コードリーディングガイド

本機能を理解するために参照すべきファイルと、推奨する読み解き順序を以下に示す。

### 推奨読解順序

#### Step 1: データ構造を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | project_statistics.rb | `app/models/project_statistics.rb` | モデルの全体構造 |

**主要処理フロー**:
1. **23行目**: COLUMNS_TO_REFRESH定義（更新対象カラム一覧）
2. **29-37行目**: STORAGE_SIZE_COMPONENTS定義（ストレージ構成要素）
3. **12-13行目**: counter_attribute定義（ビルドアーティファクト、パッケージ）

#### Step 2: 更新処理を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | project_statistics.rb | `app/models/project_statistics.rb` | refresh!メソッド |

**主要処理フロー**:
- **47-60行目**: refresh!メソッドの実装
- **62-88行目**: 各update_*メソッドの実装
- **107-109行目**: refresh_storage_size!でストレージサイズ再計算

#### Step 3: インクリメント処理を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | project_statistics.rb | `app/models/project_statistics.rb` | increment_statistic |

**主要処理フロー**:
- **115-121行目**: increment_statisticクラスメソッド
- **131-135行目**: increment_statisticインスタンスメソッド

### プログラム呼び出し階層図

```
ProjectStatistics#refresh!
    │
    ├─ Gitlab::Database.read_only? チェック
    │
    ├─ columns_to_update決定
    │      └─ only指定 or COLUMNS_TO_REFRESH全体
    │
    ├─ 各カラム更新
    │      ├─ update_repository_size
    │      │      └─ project.repository.recent_objects_size
    │      ├─ update_wiki_size
    │      │      └─ project.wiki.repository.size
    │      ├─ update_lfs_objects_size
    │      │      └─ LfsObject.sum(:size)
    │      ├─ update_commit_count
    │      │      └─ project.repository.commit_count
    │      ├─ update_snippets_size
    │      │      └─ project.snippets.sum(:repository_size)
    │      ├─ update_uploads_size
    │      │      └─ project.uploads.sum(:size)
    │      └─ update_container_registry_size
    │             └─ project.container_repositories_size
    │
    ├─ save!
    │
    └─ schedule_namespace_aggregation_worker
           └─ Namespaces::ScheduleAggregationWorker

ProjectStatistics.increment_statistic
    │
    ├─ project.pending_delete? チェック
    │
    └─ project.statistics.increment_statistic
           │
           ├─ incrementable_attribute? チェック
           │
           └─ increment_counter (CounterAttribute)
```

### データフロー図

```
[入力]                      [処理]                           [出力]

バッチジョブ            ProjectStatistics#refresh!
   │                          │
   │                          ▼
   │          ┌───────────────────────────────┐
   │          │ 各update_*メソッド            │
   │          │                               │
   │          │ ├─ repository → Gitaly        │
   │          │ ├─ wiki → Gitaly              │
   │          │ ├─ lfs → DB                   │
   │          │ ├─ uploads → DB               │
   │          │ └─ container_registry → API   │
   │          └─────────────┬─────────────────┘
   │                        │
   │                        ▼
   │          ┌───────────────────────────────┐      ┌─────────────────────┐
   │          │ save!                         │ ───▶ │ project_statistics  │
   │          └─────────────┬─────────────────┘      └─────────────────────┘
   │                        │
   │                        ▼
CI/CD完了            ┌───────────────────────────────┐
   │                │ refresh_storage_size!         │
   │                │ (storage_size計算)            │
   │                └─────────────┬─────────────────┘
   │                              │
   ▼                              ▼
increment_statistic  ┌───────────────────────────────┐
   │                │ ScheduleAggregationWorker     │
   │                │ (名前空間レベル集計)          │
   └──────────────▶ └───────────────────────────────┘
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| project_statistics.rb | `app/models/project_statistics.rb` | ソース | 統計モデル |
| counter_attribute.rb | `app/models/concerns/counter_attribute.rb` | ソース | カウンター属性 |
| schedule_aggregation_worker.rb | `app/workers/namespaces/schedule_aggregation_worker.rb` | ソース | 名前空間集計 |
| project.rb | `app/models/project.rb` | ソース | プロジェクトモデル（statistics関連） |
