Skip to content

fix: Sidecar の orphan process 化を防止 (parent process 監視) (Issue #7) - #164

Merged
oboroge0 merged 1 commit into
mainfrom
fix/sidecar-orphan-prevention
May 21, 2026
Merged

fix: Sidecar の orphan process 化を防止 (parent process 監視) (Issue #7)#164
oboroge0 merged 1 commit into
mainfrom
fix/sidecar-orphan-prevention

Conversation

@oboroge0

@oboroge0 oboroge0 commented May 9, 2026

Copy link
Copy Markdown
Owner

概要

検証中に SIGKILL でデスクトップ main プロセスを強制終了した際、Bun sidecar
プロセスが orphan 化 (PPID=1 launchd) して残存する問題を発見、修正します。

問題

これまでの実装 (apps/desktop/src-tauri/src/lib.rs):

.on_window_event(|window, event| {
    if window.label() == "main" && matches!(event, tauri::WindowEvent::Destroyed) {
        let state = window.state::<ServerProcess>();
        let mut guard = state.0.lock().unwrap();
        if let Some(child) = guard.take() {
            let _ = child.kill();
        }
    }
})
  • 正常終了 (ユーザーがウィンドウを閉じる) では機能 ✅
  • SIGKILL / Rust panic / OOM などの異常終了では event handler が走らない
  • macOS には Linux の prctl(PR_SET_PDEATHSIG) 相当機構が無いため、
    親プロセス死亡時に子プロセスへ自動通知できない

実機検証結果

$ ps -p 5633,14247 -o pid,ppid,stat,command
  PID  PPID STAT COMMAND
 5633  5412 S    target/debug/aituber-flow              # main
14247  5633 S    target/debug/server                    # sidecar (親=main)

$ kill -9 5633

$ ps -p 14247 -o pid,ppid,stat,command
  PID  PPID STAT COMMAND
14247     1 S    target/debug/server                    # ⚠️ PPID=1 (launchd) = orphan!

ユーザーが Force Quit / Activity Monitor 強制終了 / アプリクラッシュする度に
sidecar leak、port/DB ロック保持、メモリリーク発生。

修正内容

apps/desktop/src-tauri/src/lib.rs

+ // Pass our PID so the sidecar can self-terminate if we die ungracefully
+ // (SIGKILL, panic, etc.) — see apps/server-ts/src/index.ts parent-monitor.
+ let parent_pid = std::process::id();
let shell = handle.shell();
let (mut rx, child) = shell
    .sidecar("server")
    ...
    .env("AUDIO_DIR", audio_dir.to_string_lossy().to_string())
+   .env("AITUBERFLOW_PARENT_PID", parent_pid.to_string())
    .spawn()

apps/server-ts/src/index.ts

// Parent process monitoring: prevents orphan sidecar when the desktop main
// process is SIGKILLed, panics, or otherwise terminates without firing the
// Tauri WindowEvent::Destroyed handler. macOS lacks Linux's PR_SET_PDEATHSIG,
// so we poll explicitly. The Tauri side passes its PID via env var.
const parentPidEnv = process.env.AITUBERFLOW_PARENT_PID;
if (parentPidEnv) {
  const parentPid = Number(parentPidEnv);
  if (Number.isFinite(parentPid) && parentPid > 0) {
    console.log(`[parent-monitor] watching parent PID ${parentPid}`);
    setInterval(() => {
      try {
        process.kill(parentPid, 0); // signal 0 = existence check only
      } catch {
        console.log(`[parent-monitor] parent PID ${parentPid} no longer alive, exiting sidecar`);
        process.exit(0);
      }
    }, 1000).unref();
  }
}

動作

終了方法 既存 cleanup (WindowEvent) 新 cleanup (parent monitor)
ウィンドウ閉じる ✅ 即時 (走らない、必要なし)
アプリ終了メニュー ✅ 即時 (走らない、必要なし)
Force Quit ❌ 走らず (orphan) 最大1秒以内 (平均0.5秒)
SIGKILL ❌ 走らず (orphan) 最大1秒以内
Rust panic ❌ 走らず (orphan) 最大1秒以内

検証

  • cargo check (apps/desktop/src-tauri) 成功 (7.85s, dev profile clean compile)
  • 親監視ロジックを isolated test で検証 (non-existent PID 999999 → 1002ms で exit 確認)
  • (このPR merge後の手動 E2E)
    • npm run dev:desktop → ps で sidecar の PPID=main 確認
    • kill -9 <main_pid> → 1秒以内に sidecar も終了することを確認
    • 通常終了 (ウィンドウ閉じ) も従来通り動作することを regression 確認

副次影響

  • パフォーマンス: 1秒ごと kill(pid, 0) syscall 1回 = 無視できるレベル
  • 既存の正常終了経路 (WindowEvent::Destroyed) は変更なし、両経路で cleanup
  • AITUBERFLOW_PARENT_PID env が未設定 (sidecar を直接 bun run で起動した場合等) なら
    従来通り、stand-alone server モード互換

ロールバック

revert で完全復元可能。データ・成果物への影響なし。

関連

検証中に SIGKILL でデスクトップ main プロセスを強制終了した際、Bun sidecar
プロセスが orphan 化 (PPID=1 launchd) して残存する問題を発見。

これまでの実装 (apps/desktop/src-tauri/src/lib.rs):
- on_window_event(WindowEvent::Destroyed) で sidecar.kill() するのみ
- 正常終了 (ユーザーがウィンドウを閉じる) では機能
- SIGKILL / Rust panic / OOM などの異常終了では event handler が走らず
  sidecar が孤児プロセスとして残る
- macOS には Linux の prctl(PR_SET_PDEATHSIG) 相当機構が無いため、
  親プロセス死亡時に子プロセスへ自動通知できない

実機検証 (Phase 2):
- main PID 5633、sidecar PID 14247 (PPID=5633)
- kill -9 5633 → main GONE、sidecar 生存 (PPID=1 launchd へ reparent 確認)
- ユーザーが Force Quit する度に sidecar leak、port/DB ロック保持

修正内容:

apps/desktop/src-tauri/src/lib.rs:
- sidecar spawn 時に std::process::id() を取得し AITUBERFLOW_PARENT_PID env で渡す

apps/server-ts/src/index.ts:
- 起動時に AITUBERFLOW_PARENT_PID を読み取り
- setInterval(1秒) で process.kill(parentPid, 0) (signal 0 = 存在チェックのみ)
- ESRCH (親死亡) を catch したら process.exit(0) で自殺
- .unref() で event loop に余計なロックを掛けない

検証:
- cargo check 成功 (7.85s, dev profile)
- 親監視ロジックを isolated test で検証:
  - 非存在 PID (999999) を渡して 1002ms で死亡検出 + exit 確認
- 動作: Force Quit 後 最大 1秒以内 (平均 0.5秒) で sidecar も終了

副次影響:
- パフォーマンス: 1秒ごと kill(pid, 0) syscall 1回 = 無視できるレベル
- 既存の正常終了経路 (WindowEvent::Destroyed) は変更なし、両方の経路で cleanup
- AITUBERFLOW_PARENT_PID env が未設定 (sidecar を直接 bun run で起動した場合等)
  なら従来通り = stand-alone server モード互換

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented May 9, 2026

Copy link
Copy Markdown

Warning

Rate limit exceeded

@oboroge0 has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 45 minutes and 31 seconds before requesting another review.

You’ve run out of usage credits. Purchase more in the billing tab.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 1eed056b-2875-4a39-8941-40e5fbddf06e

📥 Commits

Reviewing files that changed from the base of the PR and between 5f821cf and a98df1d.

📒 Files selected for processing (2)
  • apps/desktop/src-tauri/src/lib.rs
  • apps/server-ts/src/index.ts
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/sidecar-orphan-prevention

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@oboroge0
oboroge0 merged commit 6961e96 into main May 21, 2026
4 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant