# 機能設計書 50-ストレージ

## 概要

本ドキュメントは、VS Codeのストレージ機能に関する機能設計書である。キー・バリュー形式でのデータ永続化を担当するプラットフォームサービスを定義する。

### 本機能の処理概要

ストレージ機能は、VS Codeのワークベンチ状態やユーザーデータを複数のスコープ（アプリケーション、プロファイル、ワークスペース）で永続化するプラットフォームサービスである。

**業務上の目的・背景**：エディタの状態（開いているファイル、レイアウト等）をセッション間で保持し、ユーザー体験の継続性を確保する。プロファイルやワークスペースごとの設定分離も実現する。

**機能の利用シーン**：
- ワークベンチ状態の保存・復元
- 拡張機能のデータ永続化
- 最近開いたファイル/フォルダの記録
- UI状態（サイドバー開閉等）の保持

**主要な処理内容**：
1. キー・バリューの保存（store）
2. キー・バリューの取得（get, getBoolean, getNumber, getObject）
3. キーの削除（remove）
4. ストレージの切り替え（switch）
5. ストレージの永続化（flush）
6. 変更イベントの発火

**関連システム・外部連携**：
- ワークベンチ全体（状態保存）
- 拡張機能（Memento API）
- プロファイル管理（プロファイル切替）
- 設定同期（同期対象データ管理）

**権限による制御**：StorageScopeとStorageTargetにより保存先とデータ分類を制御。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 1 | ワークベンチ | 補助機能 | ワークベンチ状態の永続化 |
| 19 | プロファイルエディタ | 主機能 | プロファイル別ストレージ管理 |

## 機能種別

プラットフォームサービス / データ永続化

## 入力仕様

### 入力パラメータ（主要メソッド）

#### store

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| key | string | Yes | ストレージキー | - |
| value | StorageValue | Yes | 保存する値 | undefined/nullで削除 |
| scope | StorageScope | Yes | ストレージスコープ | - |
| target | StorageTarget | Yes | ターゲット（USER/MACHINE） | - |

#### get

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| key | string | Yes | ストレージキー | - |
| scope | StorageScope | Yes | ストレージスコープ | - |
| fallbackValue | string | No | デフォルト値 | - |

### StorageScope列挙

| 値 | 説明 |
|-----|------|
| APPLICATION | 全ワークスペース・全プロファイル共通（-1） |
| PROFILE | 同一プロファイル内で共通（0） |
| WORKSPACE | 現在のワークスペースのみ（1） |

### StorageTarget列挙

| 値 | 説明 |
|-----|------|
| USER | ユーザー固有、マシン間で同期可能 |
| MACHINE | マシン固有、同期しない |

### IStorageEntry

| フィールド | 型 | 説明 |
|-----------|-----|------|
| key | string | ストレージキー |
| value | StorageValue | 値 |
| scope | StorageScope | スコープ |
| target | StorageTarget | ターゲット |

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| string | primitive | 文字列値 |
| boolean | primitive | 真偽値（getBoolean） |
| number | primitive | 数値（getNumber） |
| object | generic | オブジェクト（getObject） |
| IStorageValueChangeEvent | interface | 値変更イベント |
| IStorageTargetChangeEvent | interface | ターゲット変更イベント |

### 出力先

- 呼び出し元（get系メソッドの戻り値）
- イベントリスナー（onDidChangeValue, onDidChangeTarget）
- ストレージファイル（SQLite等）

## 処理フロー

### 処理シーケンス

```
1. ストレージの初期化
   └─ initialize() でストレージDB接続
   └─ 各スコープのストレージをロード
   └─ 定期フラッシュスケジューラを開始

2. 値の保存（store）
   └─ undefined/null の場合は remove() を呼び出し
   └─ キー・ターゲットマップを更新
   └─ 実際の値を保存
   └─ onDidChangeValue イベント発火

3. 値の取得（get）
   └─ スコープに応じたストレージを取得
   └─ キーで値を検索
   └─ 見つからない場合は fallbackValue を返却

4. 状態の永続化（flush）
   └─ onWillSaveState イベント発火
   └─ 各スコープのストレージをフラッシュ
   └─ shutdown理由の場合は即時フラッシュ

5. ストレージ切り替え（switch）
   └─ onWillSaveState イベント発火
   └─ プロファイルまたはワークスペースを切り替え
   └─ 変更されたキーに対しイベント発火
```

### フローチャート

```mermaid
flowchart TD
    A[store呼び出し] --> B{value == undefined/null?}
    B -->|Yes| C[remove呼び出し]
    B -->|No| D[キー・ターゲット更新]
    C --> E[キー・ターゲット削除]
    D --> F[値を保存]
    E --> G[値を削除]
    F --> H[イベント発火]
    G --> H
    H --> I[完了]
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-50-01 | null削除 | undefined/nullを設定すると削除扱い | store時 |
| BR-50-02 | 定期フラッシュ | 60秒ごとにアイドル時フラッシュ | 初期化後 |
| BR-50-03 | シャットダウン時即時フラッシュ | shutdown理由の場合は遅延なしでフラッシュ | flush(SHUTDOWN)時 |
| BR-50-04 | ターゲット追跡 | キーごとにUSER/MACHINEを記録 | store時 |
| BR-50-05 | スコープ分離 | APPLICATION, PROFILE, WORKSPACEは別ストレージ | 全操作 |

### WillSaveStateReason

| 値 | 説明 |
|-----|------|
| NONE | 通常のフラッシュ |
| SHUTDOWN | シャットダウン前のフラッシュ |

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

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

| 操作 | 対象 | 操作種別 | 概要 |
|-----|------|---------|------|
| get | ストレージDB | SELECT | 値の読み込み |
| store | ストレージDB | INSERT/UPDATE | 値の保存 |
| remove | ストレージDB | DELETE | 値の削除 |
| flush | ストレージDB | FLUSH | 変更の永続化 |

### ストレージファイル

| スコープ | パス | 説明 |
|---------|------|------|
| Application | ~/.config/Code/User/globalStorage/state.vscdb | アプリケーション共通 |
| Profile | ~/.config/Code/User/profiles/{id}/globalStorage/state.vscdb | プロファイル別 |
| Workspace | {workspace}/.vscode/.vscdb | ワークスペース別 |

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| DB_ERROR | システムエラー | DB接続失敗 | インメモリストレージにフォールバック |
| FLUSH_ERROR | システムエラー | フラッシュ失敗 | リトライ、ログ出力 |
| PARSE_ERROR | 業務エラー | JSON.parseエラー | fallbackValue使用 |

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

ストレージの書き込みはバッチ処理される。storeAll()で複数エントリを一括保存可能。イベントはすべての書き込み完了後に発火。

## パフォーマンス要件

- 定期的なアイドル時フラッシュ（60秒間隔）
- PauseableEmitterによるイベントバッチ処理
- キー・ターゲットマップのキャッシュ
- インメモリストレージのサポート（テスト用）

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

- ストレージはローカルに保存
- 機密情報はシークレットサービスを使用すべき
- StorageTarget.USERのみが同期対象

## 備考

- IS_NEW_KEY で新規ストレージかを判定可能
- 拡張機能はworkspaceState / globalStateでアクセス
- プロファイルはuseDefaultFlags.globalStateでデフォルトストレージを使用可能

---

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

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

### 推奨読解順序

#### Step 1: サービスインターフェースを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | storage.ts | `src/vs/platform/storage/common/storage.ts` | IStorageService（57-221行目） |

**主要処理フロー**:
1. **19行目**: `IStorageService = createDecorator<IStorageService>('storageService')` でサービスID定義
2. **57-221行目**: `IStorageService` インターフェース
   - **69-72行目**: `onDidChangeValue` - 値変更イベント（スコープ別オーバーロード）
   - **77行目**: `onDidChangeTarget` - ターゲット変更イベント
   - **92行目**: `onWillSaveState` - 保存前イベント
   - **101-102行目**: `get()` - 文字列値取得
   - **112-113行目**: `getBoolean()` - 真偽値取得
   - **124-125行目**: `getNumber()` - 数値取得
   - **135-136行目**: `getObject()` - オブジェクト取得
   - **149行目**: `store()` - 値保存
   - **158行目**: `storeAll()` - 一括保存
   - **167行目**: `remove()` - 値削除
   - **183行目**: `keys()` - キー一覧取得
   - **188行目**: `log()` - デバッグログ
   - **205行目**: `isNew()` - 新規ストレージ判定
   - **210行目**: `optimize()` - DB最適化
   - **220行目**: `flush()` - 永続化

#### Step 2: スコープとターゲットを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | storage.ts | `src/vs/platform/storage/common/storage.ts` | StorageScope（223-239行目）、StorageTarget（241-252行目） |

**主要な列挙型**:
- **223-239行目**: `StorageScope` - APPLICATION(-1), PROFILE(0), WORKSPACE(1)
- **241-252行目**: `StorageTarget` - USER, MACHINE

#### Step 3: イベント型を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | storage.ts | `src/vs/platform/storage/common/storage.ts` | 変更イベント型（254-292行目） |

**主要な型**:
- **21-32行目**: `WillSaveStateReason` - 保存理由（NONE, SHUTDOWN）
- **34-36行目**: `IWillSaveStateEvent` - 保存前イベント
- **38-43行目**: `IStorageEntry` - ストレージエントリ
- **45-55行目**: スコープ別変更イベント型
- **254-282行目**: `IStorageValueChangeEvent` - 値変更イベント
- **284-292行目**: `IStorageTargetChangeEvent` - ターゲット変更イベント

#### Step 4: 抽象基底クラスを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | storage.ts | `src/vs/platform/storage/common/storage.ts` | AbstractStorageService（315-701行目） |

**主要処理フロー**:
- **315-338行目**: コンストラクタ、フラッシュスケジューラ
- **366-391行目**: `initialize()` - 初期化処理
- **426-431行目**: `get()` - 値取得実装
- **458-475行目**: `store()` - 値保存実装
- **477-488行目**: `remove()` - 値削除実装
- **584-586行目**: `isNew()` - 新規判定実装
- **588-618行目**: `flush()` - フラッシュ実装
- **644-654行目**: `switch()` - ストレージ切り替え

#### Step 5: インメモリ実装を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 5-1 | storage.ts | `src/vs/platform/storage/common/storage.ts` | InMemoryStorageService（707-760行目） |

**主要処理フロー**:
- **709-711行目**: 3つのスコープ用ストレージ作成
- **721-730行目**: `getStorage()` - スコープ別ストレージ取得

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

```
IStorageService
    │
    ├─ get() / getBoolean() / getNumber() / getObject() ──▶ 値取得
    │      └─ getStorage(scope).get(key)
    │
    ├─ store() ──▶ 値保存
    │      ├─ updateKeyTarget() ──▶ ターゲットマップ更新
    │      └─ getStorage(scope).set(key, value)
    │
    ├─ storeAll() ──▶ 一括保存
    │      └─ withPausedEmitters() で store() を複数回
    │
    ├─ remove() ──▶ 値削除
    │      ├─ updateKeyTarget(undefined) ──▶ ターゲット削除
    │      └─ getStorage(scope).delete(key)
    │
    ├─ keys() ──▶ キー一覧取得
    │      └─ getKeyTargets(scope) からフィルタリング
    │
    ├─ flush() ──▶ 永続化
    │      ├─ onWillSaveState イベント発火
    │      └─ 各ストレージをフラッシュ
    │
    ├─ switch() ──▶ ストレージ切り替え
    │      ├─ switchToProfile()
    │      └─ switchToWorkspace()
    │
    └─ onDidChangeValue / onDidChangeTarget ──▶ 変更通知
```

### データフロー図

```
呼び出し元
    │ store(key, value, scope, target)
    ▼
┌─────────────────────────────────┐
│ AbstractStorageService          │
│  ├─ updateKeyTarget()           │
│  │    └─ keyTargets[key] = target│
│  └─ getStorage(scope).set()     │
└─────────────────────────────────┘
    │
    ▼
┌─────────────────────────────────┐
│ IStorage (スコープ別)           │
│  ├─ applicationStorage          │
│  ├─ profileStorage              │
│  └─ workspaceStorage            │
└─────────────────────────────────┘
    │
    ▼ (flush時)
┌─────────────────────────────────┐
│ ストレージファイル (.vscdb)     │
└─────────────────────────────────┘
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| storage.ts | `src/vs/platform/storage/common/storage.ts` | ソース | サービスインターフェース・抽象クラス |
| storageService.ts | `src/vs/platform/storage/common/storageService.ts` | ソース | 追加ユーティリティ |
| storageIpc.ts | `src/vs/platform/storage/common/storageIpc.ts` | ソース | IPC通信 |
| storageMainService.ts | `src/vs/platform/storage/electron-main/storageMainService.ts` | ソース | メインプロセス実装 |
| storageMain.ts | `src/vs/platform/storage/electron-main/storageMain.ts` | ソース | メインストレージ |
| storage.ts | `src/vs/base/parts/storage/common/storage.ts` | ソース | 低レベルストレージ |
| storage.ts | `src/vs/base/parts/storage/node/storage.ts` | ソース | Node.jsストレージ実装 |
| storageService.ts | `src/vs/workbench/services/storage/browser/storageService.ts` | ソース | ブラウザ向け実装 |
