# 機能設計書 58-メッセージ同期

## 概要

本ドキュメントは、Actual BudgetにおけるCRDTベースのメッセージ同期機能の設計を記述する。この機能は、複数クライアント間でのデータ同期において、競合を自動解決しながら一貫性を保つメッセージング基盤を提供する。

### 本機能の処理概要

Actual Budgetの同期システムにおいて、CRDT（Conflict-free Replicated Data Type）の原則に基づくメッセージ同期機能を提供する。Protocol Buffers形式でシリアライズされたメッセージをMerkle Trieで管理し、効率的な差分検出と競合のない同期を実現する。

**業務上の目的・背景**：複数のデバイスやユーザーが同時に予算データを編集する場合、データの競合が発生する可能性がある。CRDTベースの同期メカニズムを採用することで、オフライン編集からのオンライン復帰時も含め、自動的に競合を解決し、すべてのクライアントで一貫したデータ状態を維持する。

**機能の利用シーン**：
- 複数デバイス間でのリアルタイム同期
- オフライン編集後のオンライン同期
- 新規デバイスへのデータ移行

**主要な処理内容**：
1. MessageEnvelopeによるメッセージカプセル化
2. Protocol Buffersによるバイナリシリアライズ
3. Merkle Trieによる差分検出
4. タイムスタンプベースの順序付け
5. 暗号化メッセージのサポート

**関連システム・外部連携**：同期サーバー、SQLiteデータベース

**権限による制御**：予算ファイルへのアクセス権限に基づく

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| - | なし | - | バックエンド機能のため直接関連する画面なし |

## 機能種別

データ同期 / CRDT

## 入力仕様

### 入力パラメータ（メッセージ）

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| timestamp | string | Yes | HULCタイムスタンプ | フォーマット検証 |
| isencrypted | boolean | Yes | 暗号化フラグ | - |
| content | Uint8Array | Yes | メッセージ内容（暗号化または平文） | - |

### 入力データソース

- クライアントからの同期リクエスト（Protocol Buffer形式）
- SQLiteデータベース

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| trie | TrieNode | 更新後のMerkle Trie |
| newMessages | MessageEnvelope[] | 新規メッセージ配列 |

### 出力先

- HTTPレスポンス（Protocol Buffer形式）
- SQLiteデータベース

## 処理フロー

### 処理シーケンス

```
1. 同期リクエスト受信
   └─ SyncRequest.deserializeBinary()

2. 受信メッセージの処理
   ├─ 各メッセージをDBに保存
   └─ Merkle Trieへの挿入

3. 差分メッセージ取得
   └─ since以降のメッセージをDB検索

4. Merkle Trie更新
   ├─ タイムスタンプのハッシュ計算
   └─ Trieノードへの挿入

5. 応答構築
   └─ SyncResponse.serializeBinary()
```

### フローチャート

```mermaid
flowchart TD
    A[SyncRequest受信] --> B[メッセージリスト取得]
    B --> C{メッセージあり?}
    C -->|Yes| D[各メッセージをDBに保存]
    D --> E[Merkle Trieに挿入]
    E --> F[Trie剪定]
    C -->|No| F
    F --> G[since以降のメッセージ取得]
    G --> H[SyncResponse構築]
    H --> I[レスポンス送信]
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-58-1 | 重複排除 | 同一タイムスタンプのメッセージは INSERT OR IGNORE | メッセージ保存時 |
| BR-58-2 | ハッシュ計算 | MurmurHash v3でタイムスタンプをハッシュ化 | Trie挿入時 |
| BR-58-3 | Base3変換 | 分単位のタイムスタンプをBase3でキー化 | Trie挿入時 |
| BR-58-4 | Trie剪定 | 最新の2ブランチのみ保持 | 同期完了時 |
| BR-58-5 | XORハッシュ | ノードのハッシュはXOR演算で結合 | Trie操作時 |

### 計算ロジック

- **タイムスタンプキー**: `Math.floor(timestamp.millis / 1000 / 60).toString(3)`
- **ハッシュ計算**: `murmurhash.v3(timestamp.toString())`
- **ノードハッシュ**: `parentHash XOR childHash`

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

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

| 操作 | 対象テーブル | 操作種別 | 概要 |
|-----|-------------|---------|------|
| メッセージ保存 | messages_binary | INSERT OR IGNORE | 同期メッセージの保存 |
| Merkle取得 | messages_merkles | SELECT | 現在のMerkle Trie取得 |
| Merkle更新 | messages_merkles | UPSERT | Merkle Trie更新 |
| 差分取得 | messages_binary | SELECT | since以降のメッセージ取得 |

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

#### messages_binary

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| INSERT | timestamp | HULCタイムスタンプ文字列 | 一意制約 |
| INSERT | is_encrypted | 0 or 1 | 暗号化状態 |
| INSERT | content | バイナリデータ | Protocol Buffer |

#### messages_merkles

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| SELECT | id | 1 | 固定ID |
| UPSERT | merkle | JSONシリアライズされたTrie | - |

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| - | タイムスタンプエラー | 不正なタイムスタンプ形式 | メッセージを破棄 |
| - | DBエラー | データベース書き込み失敗 | トランザクションロールバック |

### リトライ仕様

DB操作はトランザクション内で実行され、失敗時は自動ロールバック。

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

- メッセージ挿入とMerkle Trie更新はSQLiteトランザクション内で実行
- db.transaction()でアトミック性を保証

## パフォーマンス要件

- メッセージ処理：1000メッセージ/秒以上
- Merkle Trie操作：O(log n)の時間計算量

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

- E2E暗号化メッセージのサポート
- 暗号化メッセージはサーバーで復号不可

## 備考

- Protocol Buffersでバイナリ効率化
- Merkle Trieで効率的な差分検出

---

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

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

### 推奨読解順序

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | sync_pb.ts | `packages/crdt/src/proto/sync_pb.ts` | Protocol Buffer型定義 |

**読解のコツ**: Protocol Buffersの宣言ファイル形式を理解しておくと、メッセージ構造が把握しやすい。

**主要処理フロー**:
- **52-90行目**: Message クラス（dataset, row, column, value）
- **92-131行目**: MessageEnvelope クラス（timestamp, isencrypted, content）
- **133-180行目**: SyncRequest クラス
- **182-217行目**: SyncResponse クラス

#### Step 2: Merkle Trie操作を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | merkle.ts | `packages/crdt/src/crdt/merkle.ts` | Merkle Trie実装 |

**主要処理フロー**:
- **15-20行目**: TrieNode型定義
- **24-26行目**: emptyTrie関数
- **36-43行目**: keyToTimestamp変換
- **48-54行目**: insert関数（タイムスタンプ挿入）
- **78-139行目**: diff関数（差分検出）
- **141-164行目**: prune関数（剪定）

#### Step 3: 同期処理を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | sync-simple.js | `packages/sync-server/src/sync-simple.js` | 同期ロジック |

**主要処理フロー**:
- **10-22行目**: getGroupDb関数（グループDB取得）
- **24-58行目**: addMessages関数（メッセージ追加）
- **60-70行目**: getMerkle関数（Merkle取得）
- **72-95行目**: sync関数（メイン同期処理）

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

```
sync(messages, since, groupId)
    │
    ├─ getGroupDb(groupId)
    │      └─ openDatabase()
    │
    ├─ db.all() [since以降のメッセージ取得]
    │
    ├─ addMessages(db, messages)
    │      │
    │      ├─ getMerkle(db)
    │      │      └─ db.all('SELECT * FROM messages_merkles')
    │      │
    │      ├─ [各メッセージに対して]
    │      │      │
    │      │      ├─ db.mutate('INSERT OR IGNORE...')
    │      │      │
    │      │      └─ merkle.insert(trie, timestamp)
    │      │             │
    │      │             ├─ timestamp.hash() [MurmurHash]
    │      │             └─ insertKey() [再帰挿入]
    │      │
    │      ├─ merkle.prune(trie)
    │      │
    │      └─ db.mutate('INSERT...ON CONFLICT DO UPDATE')
    │
    ├─ db.close()
    │
    └─ return { trie, newMessages }
```

### データフロー図

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

SyncRequest ───────▶ deserializeBinary()
      │
      ▼
MessageEnvelope[] ───────▶ addMessages()
      │                          │
      ▼                          ▼
messages_binary DB         Merkle Trie更新
      │                          │
      ▼                          ▼
SELECT (since以降) ───────▶ newMessages
                                │
                                ▼
                          SyncResponse ───────▶ クライアント
                          (merkle + messages)
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| sync-simple.js | `packages/sync-server/src/sync-simple.js` | ソース | 同期メイン処理 |
| merkle.ts | `packages/crdt/src/crdt/merkle.ts` | ソース | Merkle Trie実装 |
| timestamp.ts | `packages/crdt/src/crdt/timestamp.ts` | ソース | タイムスタンプ管理 |
| sync_pb.ts | `packages/crdt/src/proto/sync_pb.ts` | ソース | Protocol Buffer型定義 |
| index.ts | `packages/crdt/src/crdt/index.ts` | ソース | CRDTモジュールエクスポート |
