# バッチ設計書 25-repairPad

## 概要

本ドキュメントは、古いパッドを指定リビジョンまで新しいパッドに再構築する修復ツール「repairPad.ts」の設計仕様を定義する。

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

repairPad.tsは、破損したパッドのデータを特定のリビジョンまで新しいパッドとして再構築することで修復を行う修復ツールである。

**業務上の目的・背景**：パッドのリビジョン履歴が破損した場合、特定のリビジョン以降のデータが読み取れなくなることがある。本バッチは、破損が発生する前の「良好な」リビジョンまでのデータを抽出し、新しいパッドとして再構築することで、可能な限りのデータ復旧を実現する。元のパッドは削除せず保持されるため、安全な復旧作業が可能。

**バッチの実行タイミング**：手動実行のみ。パッドのデータ破損が検出された場合（checkPad.tsで確認）に、運用者が実行する。

**主要な処理内容**：
1. コマンドライン引数からパッドID、目標リビジョン番号、新パッドID（任意）を取得
2. データベース接続を初期化
3. 新パッドIDの検証と既存チェック
4. 元パッドからチャット履歴をコピー
5. 指定リビジョンまでのリビジョン履歴を順次適用して新パッドを構築
6. 保存済みリビジョン情報をコピー
7. 新パッドをデータベースに保存

**前後の処理との関連**：本バッチを実行する前に、checkPad.tsで破損状況を確認し、extractPadData.tsでバックアップを取得することを推奨する。修復後のパッドはcheckPad.tsで整合性を確認する。

**影響範囲**：新しいパッドが作成される。元のパッドは変更されずに保持される。著者情報が新パッドに関連付けられる。

## バッチ種別

データ修復 / リカバリツール

## 実行スケジュール

| 項目 | 内容 |
|-----|------|
| 実行頻度 | 随時（手動） |
| 実行時刻 | 任意 |
| 実行曜日 | - |
| 実行日 | - |
| トリガー | 手動実行 |

## 実行条件

### 前提条件

| 条件 | 説明 |
|-----|------|
| Node.js環境 | Node.js v14以上が必要 |
| データベース接続 | Etherpadデータベースにアクセス可能であること |
| パッドID指定 | コマンドライン引数でパッドIDが指定されていること |
| リビジョン番号指定 | 復旧対象のリビジョン番号が指定されていること |
| 元パッド存在 | 修復対象のパッドがデータベースに存在すること |

### 実行可否判定

- コマンドライン引数が4つまたは5つでない場合はエラー終了
- 新パッドIDが無効な形式の場合はエラー終了
- 新パッドIDと同名のパッドが既に存在する場合はエラー終了
- 指定リビジョンが見つからない場合はエラー終了

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | デフォルト値 | 説明 |
|-------------|-----|-----|-------------|------|
| PADID | string | Yes | - | 修復対象のパッドID |
| REV | number | Yes | - | 復旧対象のリビジョン番号 |
| NEWPADID | string | No | {PADID}-rebuilt | 新しいパッドのID |

### 入力データソース

| データソース | 形式 | 説明 |
|-------------|------|------|
| pad:{padId} | DB | 元パッドのメタデータ |
| pad:{padId}:revs:{rev} | DB | 元パッドのリビジョン |
| pad:{padId}:chat:{n} | DB | 元パッドのチャット履歴 |

## 出力仕様

### 出力データ

| 出力先 | 形式 | 説明 |
|-------|------|------|
| pad:{newPadId} | DB | 新パッドのメタデータ |
| pad:{newPadId}:revs:{rev} | DB | 新パッドのリビジョン |
| pad:{newPadId}:chat:{n} | DB | 新パッドのチャット履歴 |
| 標準出力 | テキスト | 処理進捗と結果のログ |

### 出力ファイル仕様

本バッチはファイル出力を行わない。

## 処理フロー

### 処理シーケンス

```
1. コマンドライン引数検証
   └─ 引数が4つまたは5つでない場合はエラー
2. パラメータ取得
   └─ パッドID、リビジョン番号、新パッドIDを取得
3. データベース初期化
   └─ db.init()呼び出し
4. 新パッドID検証
   └─ 有効なパッドID形式か確認
5. 既存パッドチェック
   └─ 新パッドIDが既に存在しないか確認
6. 元パッド取得
   └─ PadManager.getPad()で取得
7. 新Padオブジェクト作成
   └─ new Pad(newPadId)
8. チャット履歴コピー
   └─ 0からchatHeadまでの全チャットをコピー
9. AttributePoolコピー
   └─ numToAttribをコピー
10. リビジョン再構築
    └─ 0から指定リビジョンまで順次適用
11. 保存済みリビジョンコピー
    └─ 指定リビジョン以下のsavedRevisionsをコピー
12. 新パッド保存
    └─ db.set()とsaveToDatabase()で保存
13. DBシャットダウン
    └─ db.shutdown()呼び出し
```

### フローチャート

```mermaid
flowchart TD
    A[バッチ開始] --> B{引数チェック}
    B -->|引数不正| C[エラー終了]
    B -->|引数OK| D[DB初期化]
    D --> E{新パッドID有効?}
    E -->|No| F[エラー: Invalid padId]
    E -->|Yes| G{新パッド既存?}
    G -->|Yes| H[エラー: Already exists]
    G -->|No| I[元パッド取得]
    I --> J[新Padオブジェクト作成]
    J --> K[チャット履歴コピー]
    K --> L[AttributePoolコピー]
    L --> M[リビジョン再構築ループ]
    M --> N{リビジョン存在?}
    N -->|No| O[エラー: Revision not found]
    N -->|Yes| P[チェンジセット適用]
    P --> Q{全リビジョン完了?}
    Q -->|No| M
    Q -->|Yes| R[保存済みリビジョンコピー]
    R --> S[新パッド保存]
    S --> T[DBシャットダウン]
    T --> U[正常終了 exit 0]
```

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

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

| 処理 | 対象テーブル | 操作種別 | 概要 |
|-----|-------------|---------|------|
| 元パッド読み込み | pad:{padId} | SELECT | 元パッドメタデータ取得 |
| チャットコピー | pad:{newPadId}:chat:* | INSERT | チャット履歴を新パッドにコピー |
| リビジョンコピー | pad:{newPadId}:revs:* | INSERT | リビジョンを新パッドにコピー |
| 著者登録 | globalAuthor:* | UPDATE | 著者に新パッドを関連付け |
| 新パッド保存 | pad:{newPadId} | INSERT | 新パッドメタデータ保存 |

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

#### pad:{newPadId}

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| INSERT | atext, pool, head, chatHead, savedRevisions | 再構築されたデータ | 新規作成 |

#### pad:{newPadId}:revs:{rev}

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| INSERT | changeset, meta | 元パッドから取得した値 | リビジョン番号0から指定番号まで |

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| - | 引数エラー | コマンドライン引数が不足 | 正しい引数でコマンドを再実行 |
| - | 無効なパッドID | 新パッドIDの形式が不正 | 有効なパッドID形式を指定 |
| - | パッド既存 | 新パッドIDが既に存在 | 別のパッドIDを指定 |
| - | リビジョン未存在 | 指定リビジョンが見つからない | 存在するリビジョン番号を指定 |

### リトライ仕様

| 項目 | 内容 |
|-----|------|
| リトライ回数 | なし |
| リトライ間隔 | - |
| リトライ対象エラー | - |

### 障害時対応

処理中に中断された場合、不完全な新パッドが作成される可能性がある。この場合、新パッドをdeletePad.tsで削除し、再度実行する。

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

| 項目 | 内容 |
|-----|------|
| トランザクション範囲 | バッチ全体（暗黙的） |
| コミットタイミング | db.shutdown()時 |
| ロールバック条件 | エラー発生時は部分的にデータが残る可能性あり |

## パフォーマンス要件

| 項目 | 内容 |
|-----|------|
| 想定処理件数 | 1パッド |
| 目標処理時間 | リビジョン数に依存 |
| メモリ使用量上限 | パッドサイズに依存 |

## 排他制御

データベース書き込みを行うため、Etherpad稼働中に実行する場合は競合に注意が必要。可能であればEtherpad停止中に実行することを推奨。

## ログ出力

| ログ種別 | 出力タイミング | 出力内容 |
|---------|--------------|---------|
| 進捗ログ | チャットコピー時 | "Created: Chat Revision: pad:{newPadId}:chat:{n}" |
| 進捗ログ | リビジョン作成時 | "Created: Revision: pad:{newPadId}:revs:{rev}" |
| 進捗ログ | 保存済みリビジョン追加時 | "Added: Saved Revision: {revNum}" |
| 終了ログ | パッド作成完了時 | "Created: Source Pad: pad:{newPadId}" |
| 終了ログ | 処理完了時 | "finished" |

## 監視・アラート

| 監視項目 | 閾値 | アラート先 |
|---------|-----|----------|
| - | - | - |

本バッチは手動実行のツールであり、監視対象外とする。

## 備考

- 元のパッドは削除されないため、必要に応じて手動で削除する
- 新パッドIDを指定しない場合、"{元パッドID}-rebuilt"という名前で作成される
- チャット履歴は全件コピーされる（リビジョン指定に関係なく）
- AttributePoolの属性マッピングは完全にコピーされる
- 保存済みリビジョンは指定リビジョン以下のもののみがコピーされる
- リビジョンの適用にはChangesetモジュールが使用される
