# 機能設計書 146-Git LFS

## 概要

本ドキュメントは、GitLabのGit LFS（Large File Storage）機能に関する設計書である。Git LFSは大容量ファイルをGitリポジトリで効率的に管理するための機能を提供する。

### 本機能の処理概要

Git LFSは、Gitリポジトリ内の大容量ファイル（バイナリファイル、画像、動画など）を効率的に管理するための拡張機能である。実際のファイル内容は別ストレージに保存し、Gitリポジトリにはポインタファイルのみを格納することで、リポジトリサイズの肥大化を防ぐ。

**業務上の目的・背景**：ソフトウェア開発において、バイナリファイル、テストデータ、メディアファイルなどの大容量ファイルをバージョン管理する必要がある。通常のGitでは大容量ファイルの履歴がリポジトリを肥大化させるが、Git LFSを使用することで、クローン・フェッチの高速化とストレージの効率化を実現できる。

**機能の利用シーン**：
- 大容量バイナリファイル（画像、動画、音声）のバージョン管理
- ゲームアセットやCADファイルの管理
- 機械学習モデルやデータセットの管理
- ビルド成果物やリリースパッケージの管理
- ファイルロックによる競合防止

**主要な処理内容**：
1. LFSオブジェクトのアップロード・ダウンロード
2. LFSオブジェクトとプロジェクトの関連付け
3. ファイルロック・アンロック機能
4. リモートへのLFSオブジェクトプッシュ
5. LFSオブジェクトの変換（ポインタ⇔実体）

**関連システム・外部連携**：
- オブジェクトストレージ（S3互換、GCS等）
- Git LFSクライアント
- リモートLFSサーバー（ミラーリング時）

**権限による制御**：
- ファイルロック：push_code権限が必要
- LFSオブジェクトアクセス：リポジトリへの適切な権限

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| - | リポジトリ設定画面 | 補助機能 | LFS設定の有効化/無効化 |

## 機能種別

ファイル操作 / バージョン管理 / ストレージ管理

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| oid | String | Yes | LFSオブジェクトID（SHA256） | 64文字の16進数 |
| size | Integer | Yes | ファイルサイズ（バイト） | 正の整数 |
| path | String | Yes（ロック時） | ファイルパス | 有効なパス |

### 入力データソース

- Git LFSクライアントからのAPI呼び出し
- CI/CDパイプラインからのアクセス
- プッシュミラーリング設定

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| oid | String | LFSオブジェクトID |
| size | Integer | ファイルサイズ |
| authenticated | Boolean | 認証済みフラグ |
| actions | Object | upload/download/verifyアクション情報 |

### 出力先

- LFS Batch API レスポンス（JSON）
- オブジェクトストレージ

## 処理フロー

### 処理シーケンス

```
1. LFSオブジェクトアップロード（PushService）
   └─ LFSオブジェクトをバッチ処理で取得
   └─ リモートLFSサーバーにBatch APIで問い合わせ
   └─ uploadアクションがある場合はアップロード
   └─ verifyアクションがある場合は検証

2. ファイルロック（LockFileService）
   └─ push_code権限の確認
   └─ ロック作成（lfs_file_locks）
   └─ プロジェクトのロック変更エポック更新

3. ファイルアンロック（UnlockFileService）
   └─ ロックの存在確認
   └─ ロック削除
   └─ プロジェクトのロック変更エポック更新

4. アップロードファイナライズ（FinalizeUploadService）
   └─ アップロード完了の確認
   └─ LFSオブジェクトの作成/関連付け
```

### フローチャート

```mermaid
flowchart TD
    A[LFSリクエスト受信] --> B{操作種別}

    B -->|Push| C[Batch API - upload]
    B -->|Lock| D[権限確認]
    B -->|Unlock| E[ロック存在確認]

    C --> F[オブジェクト存在確認]
    F -->|存在しない| G[アップロード実行]
    F -->|存在する| H[スキップ]

    G --> I{verify必要?}
    I -->|Yes| J[検証実行]
    I -->|No| K[完了]
    J --> K

    D -->|権限あり| L[ロック作成]
    D -->|権限なし| M[403エラー]

    L --> N{重複?}
    N -->|Yes| O[409コンフリクト]
    N -->|No| P[201作成成功]

    E -->|存在| Q[ロック削除]
    E -->|存在しない| R[404エラー]
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-146-01 | OID形式 | oidは64文字の16進数（SHA256） | 常時 |
| BR-146-02 | ロック権限 | ファイルロックにはpush_code権限が必要 | LockFileService |
| BR-146-03 | ロック一意性 | 同一パスに対するロックは1つのみ | LockFileService |
| BR-146-04 | バッチサイズ | 1バッチあたり最大100オブジェクト | PushService |
| BR-146-05 | フォークアクセス | フォークネットワーク内のLFSオブジェクトはアクセス可能 | LfsObject#project_allowed_access? |

### 計算ロジック

**OID計算**:
```ruby
oid = Digest::SHA256.hexdigest(file_content)
# 例: "a3f7b8c9d0e1f2..."（64文字）
```

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

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

| 操作 | 対象テーブル | 操作種別 | 概要 |
|-----|-------------|---------|------|
| オブジェクト作成 | lfs_objects | INSERT | LFSオブジェクトの作成 |
| 関連付け | lfs_objects_projects | INSERT | オブジェクトとプロジェクトの関連付け |
| ロック作成 | lfs_file_locks | INSERT | ファイルロックの作成 |
| ロック削除 | lfs_file_locks | DELETE | ファイルロックの削除 |
| ロック検索 | lfs_file_locks | SELECT | ロック状態の確認 |

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

#### lfs_objects

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| INSERT | oid, size, file, file_store | アップロード時 | LFSオブジェクト作成 |
| SELECT | id, oid, size | oid = :oid AND size = :size | オブジェクト検索 |

#### lfs_file_locks

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| INSERT | project_id, user_id, path | ロック作成時 | ファイルロック |
| DELETE | id | ロック削除時 | アンロック |
| SELECT | * | project_id = :project_id AND path = :path | ロック検索 |

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| 403 | Forbidden | push_code権限がない | "You have no permissions" |
| 409 | Conflict | 既にロック済み | "already locked" + 既存ロック情報 |
| 500 | Internal Server Error | 予期しないエラー | エラーメッセージ |

### リトライ仕様

- PushServiceはエラー時にGitlab::ErrorTracking.log_exceptionでログ記録
- クライアント側でのリトライを想定

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

- ロック作成はActiveRecordトランザクション内で実行
- RecordNotUnique例外で競合を検出

## パフォーマンス要件

- バッチサイズ: 100オブジェクト/リクエスト（Git LFSクライアント標準）
- 大容量ファイルはオブジェクトストレージに直接保存

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

- push_code権限によるロック操作制限
- OIDによるコンテンツアドレッシング（改ざん検出）
- 認証情報のセキュアな受け渡し

## 備考

- Git LFSクライアントのバッチサイズ（100）に合わせた設計
- repository_type による分類（nil: 通常リポジトリ, :project: プロジェクト）
- フォークネットワーク内でのLFSオブジェクト共有

---

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

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

### 推奨読解順序

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

LFSオブジェクトのデータ構造を把握する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | lfs_object.rb | `app/models/lfs_object.rb` | LFSオブジェクトモデル |

**主要処理フロー**:
1. **12-13行目**: has_many関連（lfs_objects_projects, projects）
2. **15行目**: scope :for_oids - OIDによる検索
3. **17行目**: バリデーション（oid形式）
4. **23-25行目**: for_oid_and_size - OID+サイズで検索
5. **36-44行目**: project_allowed_access? - フォークアクセス判定

**読解のコツ**:
- oidは64文字のSHA256ハッシュ
- フォークネットワーク内のプロジェクトはLFSオブジェクトを共有

#### Step 2: プッシュサービスを理解する

リモートへのLFSオブジェクトプッシュ処理を理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | push_service.rb | `app/services/lfs/push_service.rb` | LFSプッシュサービス |

**主要処理フロー**:
1. **11行目**: BATCH_SIZE = 100（バッチサイズ定数）
2. **13-22行目**: execute - バッチ処理でプッシュ
3. **38-48行目**: push_objects! - Batch API呼び出し
4. **50-61行目**: upload_object! - アップロード実行
5. **63-68行目**: verify_object! - 検証実行

#### Step 3: ファイルロックサービスを理解する

ファイルロック機能の実装を理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | lock_file_service.rb | `app/services/lfs/lock_file_service.rb` | ファイルロックサービス |
| 3-2 | unlock_file_service.rb | `app/services/lfs/unlock_file_service.rb` | ファイルアンロックサービス |

**主要処理フロー（LockFileService）**:
1. **5-8行目**: execute - 権限チェックとロック作成
2. **10-12行目**: RecordNotUnique例外ハンドリング
3. **27-33行目**: create_lock! - ロック作成とエポック更新

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

```
Lfs::PushService
    │
    ├─ lfs_objects_relation
    │      └─ project.lfs_objects_for_repository_types
    │
    ├─ push_objects!(objects)
    │      ├─ lfs_client.batch!('upload', objects)
    │      ├─ upload_object!(object, spec)
    │      │      └─ lfs_client.upload!(object, upload)
    │      └─ verify_object!(object, spec)
    │             └─ lfs_client.verify!(object, verify)
    │
    └─ lfs_client
           └─ Gitlab::Lfs::Client.new(url, credentials)

Lfs::LockFileService
    │
    ├─ can?(current_user, :push_code, project)
    │
    └─ create_lock!
           ├─ project.lfs_file_locks.create!
           └─ project.refresh_lfs_file_locks_changed_epoch
```

### データフロー図

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

LFS Push             ───▶  Lfs::PushService          ───▶  リモートLFSサーバー
                           ├─ Batch API
                           ├─ Upload
                           └─ Verify

Lock Request         ───▶  Lfs::LockFileService      ───▶  lfs_file_locks INSERT
                           ├─ 権限チェック
                           └─ ロック作成

Unlock Request       ───▶  Lfs::UnlockFileService    ───▶  lfs_file_locks DELETE
                           ├─ ロック検索
                           └─ ロック削除
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| lfs_object.rb | `app/models/lfs_object.rb` | モデル | LFSオブジェクトモデル |
| lfs_objects_project.rb | `app/models/lfs_objects_project.rb` | モデル | オブジェクト-プロジェクト関連 |
| lfs_file_lock.rb | `app/models/lfs_file_lock.rb` | モデル | ファイルロックモデル |
| push_service.rb | `app/services/lfs/push_service.rb` | サービス | LFSプッシュサービス |
| lock_file_service.rb | `app/services/lfs/lock_file_service.rb` | サービス | ファイルロックサービス |
| unlock_file_service.rb | `app/services/lfs/unlock_file_service.rb` | サービス | ファイルアンロックサービス |
| finalize_upload_service.rb | `app/services/lfs/finalize_upload_service.rb` | サービス | アップロードファイナライズ |
| file_transformer.rb | `app/services/lfs/file_transformer.rb` | サービス | ファイル変換 |
| locks_finder_service.rb | `app/services/lfs/locks_finder_service.rb` | サービス | ロック検索 |
| client.rb | `lib/gitlab/lfs/client.rb` | ライブラリ | LFSクライアント |
| lfs_object_uploader.rb | `app/uploaders/lfs_object_uploader.rb` | アップローダー | LFSオブジェクトアップロード |
