# 通知設計書 29-一時ファイルクリーンアップエラー

## 概要

本ドキュメントは、Juliaの一時ファイル/ディレクトリクリーンアップ処理が失敗した場合に出力される警告/エラー通知の設計を記述する。

### 本通知の処理概要

本通知は、一時ファイルや一時ディレクトリの削除処理が失敗した場合に出力される複数の関連通知を包括的に記述する。具体的には以下の4つの通知パターンがある。

1. **`temp_cleanup_purge_prelocked` での警告** (`@warn`): 一時パスの削除に失敗した場合
2. **`temp_cleanup_postprocess` での警告** (`@warn`): 子プロセスでの遅延削除に失敗した場合
3. **`mktemp` でのエラー** (`@error`): 一時ファイルのクリーンアップに失敗した場合
4. **`mktempdir` でのエラー** (`@error`): 一時ディレクトリのクリーンアップに失敗した場合

**業務上の目的・背景**：Juliaは一時ファイルとディレクトリの自動クリーンアップ機構を持つ。`mktemp` や `mktempdir` でコールバック付きの形式が使われた場合、コールバック完了後に自動削除が試みられる。また、プロセス終了時（`atexit`）にすべての一時パスの削除が試みられる。これらの削除処理は、ファイルがまだ使用中（Windowsでのロック）、権限不足、ファイルシステムエラーなどの理由で失敗する可能性がある。失敗した場合でもJuliaプロセスの実行を継続するため、エラーを記録して処理を続行する。

**通知の送信タイミング**：一時ファイル/ディレクトリの削除操作が例外をスローした際に即時発生する。

**通知の受信者**：Juliaユーザーおよび開発者。コンソール（stderr）に表示される。

**通知内容の概要**：削除に失敗した一時パスと例外情報を表示する。

**期待されるアクション**：ユーザーは一時ファイルが残存している可能性を認識し、必要に応じて手動で削除する。

## 通知種別

ログ（Warn / Error） -- Julia標準ロギングフレームワーク（`@warn` / `@error` マクロ）によるコンソール出力。`_group=:file` が設定される。

## 送信仕様

### 基本情報

| 項目 | 内容 |
|-----|------|
| 送信方式 | 同期（削除処理中に即時出力） |
| 優先度 | パターン1,2: 中（Warn）、パターン3,4: 高（Error） |
| リトライ | パターン3,4: 削除失敗後 `temp_cleanup_later` で後日削除をスケジュール |

### 送信先決定ロジック

Julia の標準ロギングフレームワークに従い、`stderr` に出力される。`_group=:file` が設定されるため、ログフィルタリングで一時ファイル関連の通知をまとめて管理できる。

## 通知テンプレート

### メール通知の場合

該当なし（コンソールログ出力）

### 本文テンプレート

**パターン1: temp_cleanup_purge_prelocked (Warn)**
```
┌ Warning: Failed to clean up temporary path "/tmp/jl_XXXXX"
│         {exception message}
└ @ Base file.jl:660
```

**パターン2: temp_cleanup_postprocess (Warn)**
```
┌ Warning: Failed to clean up temporary path "/tmp/jl_XXXXX"
│         {exception message}
└ @ Main none:0
```

**パターン3: mktemp (Error)**
```
┌ Error: mktemp cleanup
│   exception =
│    {exception object}
│    Stacktrace:
│     [1] ...
└ @ Base file.jl:914
```

**パターン4: mktempdir (Error)**
```
┌ Error: mktempdir cleanup
│   exception =
│    {exception object}
│    Stacktrace:
│     [1] ...
└ @ Base file.jl:945
```

### 添付ファイル

該当なし

## テンプレート変数

| 変数名 | 説明 | データ取得元 | 必須 |
|--------|------|-------------|-----|
| path | 削除に失敗した一時パス | `TEMP_CLEANUP` のキー、または `mktemp`/`mktempdir` の戻り値 | Yes（パターン1,2） |
| ex | 捕捉された例外 | `catch` ブロック内の例外変数 | Yes |
| exception（構造化） | 例外とバックトレースのタプル | `(ex, catch_backtrace())` | Yes（パターン3,4） |

## 送信トリガー・条件

### トリガー一覧

| トリガー種別 | トリガーイベント | 送信条件 | 説明 |
|------------|----------------|---------|------|
| 一時パス削除 | `temp_cleanup_purge_prelocked` 内の `rm` | `rm` が例外をスロー | パターン1 |
| 子プロセス削除 | `temp_cleanup_postprocess` の子プロセス内 | `rm` が例外をスロー | パターン2 |
| `mktemp` クリーンアップ | `mktemp(fn, parent)` の `finally` ブロック | `close(tmp_io)` または `rm(tmp_path)` が例外をスロー | パターン3 |
| `mktempdir` クリーンアップ | `mktempdir(fn, parent)` の `finally` ブロック | `rm(tmpdir, recursive=true)` が例外をスロー | パターン4 |

### 送信抑止条件

| 条件 | 説明 |
|-----|------|
| 削除対象が存在しない | `ispath(path)` が `false` の場合、削除不要のため通知なし（パターン1,3,4） |

## 処理フロー

### 送信フロー

```mermaid
flowchart TD
    A["一時ファイル操作完了"] --> B["finally ブロック / atexit"]
    B --> C{"ispath(path)?"}
    C -->|No| D["何もしない"]
    C -->|Yes| E["rm(path) 試行"]
    E --> F{"例外発生?"}
    F -->|No| G["正常完了"]
    F -->|Yes| H{"パターン?"}
    H -->|purge_prelocked| I["@warn + continue"]
    H -->|mktemp| J["@error + temp_cleanup_later"]
    H -->|mktempdir| K["@error + temp_cleanup_later"]
    I --> L["次のパス処理"]
    J --> M["処理終了"]
    K --> M
```

## データベース参照・更新仕様

### 参照テーブル一覧

| データ構造 | 参照先 | 参照目的 |
|----------|--------|---------|
| `TEMP_CLEANUP` | `Dict{String, Bool}` | 一時パスの管理。キー=パス、値=asap（即時削除フラグ） |
| `TEMP_CLEANUP_LOCK` | `ReentrantLock` | `TEMP_CLEANUP` へのスレッドセーフアクセス |

### 更新テーブル一覧

| データ構造 | 更新内容 | 更新条件 |
|----------|---------|---------|
| `TEMP_CLEANUP` | パスの削除（`filter!`）または追加（`temp_cleanup_later`） | パターン1: 削除成功で除去 / パターン3,4: 削除失敗で `asap=true` で再登録 |

## エラー処理

### エラーケース一覧

| エラー種別 | 発生条件 | 対処方法 |
|----------|---------|---------|
| ファイルロック（Windows） | ファイルがまだ使用中 | 警告/エラーを出力、後日削除をスケジュール |
| 権限不足 | 削除権限がない | 警告/エラーを出力 |
| InterruptException | パターン1のみ `rethrow()` | ユーザー割り込みは再スロー |

### リトライ仕様

| 項目 | 内容 |
|-----|------|
| リトライ方式 | パターン3,4: `temp_cleanup_later(path, asap=true)` で遅延削除をスケジュール |
| リトライタイミング | `atexit` ハンドラ内（`temp_cleanup_atexit` → `temp_cleanup_purge_all`） |

## 配信設定

### レート制限

| 項目 | 内容 |
|-----|------|
| 制限 | なし（失敗した一時パスごとに出力） |

### 配信時間帯

制限なし

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

一時ファイルのパスがログに含まれる。一時ファイルには機密データが含まれている可能性がある。パス自体は `/tmp/jl_XXXXX` 形式であり、ファイル内容は露出しないが、一時ファイルの存在と場所が明らかになる。`_group=:file` を使用しているため、ログフィルタリングで制御可能。

## 備考

- `prepare_for_deletion(path)` はディレクトリ内のすべてのファイルとディレクトリの書き込み権限を再帰的に設定する（Windowsでの削除のため）
- `temp_cleanup_postprocess` は子Juliaプロセスを起動して遅延削除を行う。このプロセスは `stdin` が閉じられた後に実行を開始する
- `GC.gc(true)` がフルGCを実行してファイナライザを発火させ、Windowsでのファイルロック解除を促す
- `temp_prefix` は `"jl_"` で、すべての一時ファイル名はこのプレフィックスで始まる

---

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

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

### 推奨読解順序

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | file.jl | `base/file.jl` | `TEMP_CLEANUP` 辞書と `TEMP_CLEANUP_LOCK` の定義。一時パスのライフサイクル管理 |
| 1-2 | file.jl | `base/file.jl` | 722行目: `temp_prefix` 定数 |

#### Step 2: エントリーポイントを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | file.jl | `base/file.jl` | 904-919行目: `mktemp(fn, parent)` 関数 |
| 2-2 | file.jl | `base/file.jl` | 932-950行目: `mktempdir(fn, parent)` 関数 |
| 2-3 | file.jl | `base/file.jl` | 668-686行目: `temp_cleanup_purge_all` 関数 |
| 2-4 | file.jl | `base/file.jl` | 713-716行目: `temp_cleanup_atexit` 関数 |

#### Step 3: 通知発生箇所を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | file.jl | `base/file.jl` | 647-666行目: `temp_cleanup_purge_prelocked` 関数。657-660行目に `@warn` |
| 3-2 | file.jl | `base/file.jl` | 691-711行目: `temp_cleanup_postprocess` 関数。700行目に `@warn` |
| 3-3 | file.jl | `base/file.jl` | 910-917行目: `mktemp` の `finally` ブロック。914行目に `@error` |
| 3-4 | file.jl | `base/file.jl` | 938-948行目: `mktempdir` の `finally` ブロック。945行目に `@error` |

**主要処理フロー（パターン1 - purge_prelocked）**:
- **648行目**: `filter!(TEMP_CLEANUP)` で一時パスリストをフィルタリング
- **649-655行目**: `try` ブロック内で `ispath` チェックと `rm` 実行
- **656行目**: `catch ex` で例外キャッチ
- **657-660行目**: `@warn "Failed to clean up temporary path $(repr(path))\n$ex" _group=:file` で警告
- **661行目**: `InterruptException` は `rethrow()`
- **662行目**: `return true` で `TEMP_CLEANUP` に残す

**主要処理フロー（パターン3 - mktemp）**:
- **908-909行目**: `finally` ブロック。`temp_cleanup_forget` でクリーンアップリストから除去
- **910行目**: `try` ブロック内で `close` と `rm`
- **913行目**: `catch ex`
- **914行目**: `@error "mktemp cleanup" _group=:file exception=(ex, catch_backtrace())`
- **916行目**: `temp_cleanup_later(tmp_path, asap=true)` で後日削除をスケジュール

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

```
mktemp(fn, parent) [file.jl:904]
    │
    ├─ mktemp(parent) [一時ファイル作成]
    │
    ├─ fn(tmp_path, tmp_io) [ユーザーコールバック]
    │
    └─ finally [file.jl:908]
           ├─ temp_cleanup_forget(tmp_path) [file.jl:642]
           └─ try [file.jl:910]
                  ├─ close(tmp_io) + rm(tmp_path)
                  └─ catch: @error "mktemp cleanup" [file.jl:914]
                         └─ temp_cleanup_later(tmp_path, asap=true)

temp_cleanup_atexit() [file.jl:713]
    │
    ├─ temp_cleanup_purge_all() [file.jl:668]
    │      └─ temp_cleanup_purge_prelocked(true) [file.jl:647]
    │             └─ @warn "Failed to clean up..." [file.jl:657]
    │
    └─ temp_cleanup_postprocess(keys) [file.jl:691]
           └─ 子プロセス内: @warn "Failed to clean up..." [file.jl:700]
```

### データフロー図

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

TEMP_CLEANUP (Dict) ──────────▶ temp_cleanup_purge_prelocked
                               │
一時パス (path) ───────────────▶ ├─ ispath チェック
                               │
                               ├─ rm(path) 試行 ──────────────▶ 削除成功
                               │
                               └─ 削除失敗 ───────────────────▶ @warn/@error ──▶ stderr
                                    │
                                    └─ temp_cleanup_later ────▶ TEMP_CLEANUP 更新
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| file.jl | `base/file.jl` | ソース | 通知の発生元。一時ファイル管理全体の実装 |
| logging.jl | `base/logging/logging.jl` | ソース | `@warn`, `@error` マクロの実装 |
