Daemon leaks on Linux: `stat -f` is macOS-only + missing PID-file guard
まだ誰も着手していません。
評価
- 難易度
- 3/5
- 見積もり時間
- 1〜2日
- 初心者へのやさしさ
- 55/100
- issue の種類
- バグ
- 明瞭さ
- 明確に書かれている
- 活発さ
- 静か
- 技術スタック
- bash, bun, typescript
調査の方向性
bin/macrodata-hook.sh から始め、特に store_lastmod、check_files_changed、is_daemon_running、start_daemon を確認し、Linux 上での動作を macrodata-daemon.ts と比較してください。Linux 環境で hook を実行し、mtime のチェックが機能すること、また PID ファイルが存在しない場合や古い場合も含め、hook の呼び出しを繰り返しても重複した daemon プロセスが作成されないことを確認してください。
索引モデルが issue の本文から書いたものです。
説明
Summary
On Linux (e.g. WSL2 / Ubuntu), bin/macrodata-hook.sh has two bugs that compound into a daemon leak: every session-start or prompt-submit hook call can spawn a fresh macrodata-daemon.ts that never exits. On a machine that was left running for ~26 days with scheduled dreamtime + morning-prep sessions firing nightly, I ended up with 9 concurrent daemons holding ~14% RSS each, OOM-thrashing and pinning CPU.
Bug 1 — stat -f %m is BSD/macOS syntax
bin/macrodata-hook.sh uses stat -f %m in store_lastmod and check_files_changed. On GNU coreutils (Linux) -f means display filesystem status, not format. The call errors out:
$ stat -f %m ~/some/file
stat: cannot read file system information for '%m': No such file or directory
File: "/home/..."
Because the error is swallowed by 2>/dev/null, the command substitution returns the empty string. check_files_changed then compares "" against whatever is in .lastmod, always sees a mismatch, and reports "files changed" every prompt. This triggers signal_daemon_reload + re-injects static context on every hook fire.
GNU coreutils uses stat -c %Y. BSD/macOS coreutils uses stat -f %m. A portable helper handles both.
Bug 2 — No guard against duplicate daemon spawning when PID file is missing
is_daemon_running() only returns true when $PIDFILE exists and its PID is live. On my machine ~/.config/macrodata/.daemon.pid doesn't exist (possibly never written by the daemon, or removed, or the daemon is running with a different MACRODATA_ROOT). So is_daemon_running always returns false, and every start_daemon call spawns a new nohup bun run macrodata-daemon.ts & — which gets adopted by init when the parent shell dies and keeps running forever via the croner schedulers.
Over 26 days of scheduled sessions, this accumulated to 9 orphaned daemons:
PID ETIME %CPU %MEM CMD
317154 1-04:32:16 3.6 0.3 bun run .../macrodata-daemon.ts
317155 1-04:32:16 3.5 0.3 bun run .../macrodata-daemon.ts
317157 1-04:32:16 3.3 0.3 bun run .../macrodata-daemon.ts
373463 19:31:45 4.6 0.6 bun run .../macrodata-daemon.ts
435928 11:28:12 5.1 12.6 bun run .../macrodata-daemon.ts
453484 52:06 14.6 12.6 bun run .../macrodata-daemon.ts
453533 52:06 14.2 10.6 bun run .../macrodata-daemon.ts
462810 01:47 128 14.0 bun run .../macrodata-daemon.ts
463478 00:13 12.9 2.9 bun run .../macrodata-daemon.ts
Memory was 5.5/7.6 GiB used, 1.7/2 GiB swap used, 15-min load average 252.
Environment
- Linux 6.6.87.2-microsoft-standard-WSL2 (Ubuntu on WSL2)
- macrodata 0.2.1 (Claude Code plugin cache)
- bash, GNU coreutils
Suggested fix
Patch applied locally to bin/macrodata-hook.sh:
-
Portable mtime helper — try GNU
stat -c %Yfirst, fall back to BSDstat -f %m:get_mtime() { stat -c %Y "$1" 2>/dev/null || stat -f %m "$1" 2>/dev/null || echo 0 }Replace all
stat -f %m "$X" 2>/dev/null || echo 0call sites withget_mtime "$X". -
pgrep fallback in
is_daemon_running— if PID file is missing/stale, check whether any live daemon exists for this user before spawning:is_daemon_running() { if [ -f "$PIDFILE" ]; then local pid=$(cat "$PIDFILE") if kill -0 "$pid" 2>/dev/null; then return 0 fi fi if pgrep -u "$(id -u)" -f "macrodata-daemon\.ts" >/dev/null 2>&1; then return 0 fi return 1 }
Happy to open a PR if you'd like — wanted to file the report first in case you'd prefer a different shape (e.g. having the daemon write the PID file more defensively on startup, or adding flock around start_daemon).
- 主要言語
- TypeScript
- スター
- 119
- フォーク
- 6
- 平均マージ
- 1時間 32分
- マージ済み PR(30日)
- 2
コントリビューションガイド
このリポジトリのコントリビューションガイドは索引されていません
はじめの一歩
- issue を最後まで読み、次にプロジェクトのコントリビューションガイドを読みます。
- 着手することを issue にコメントします — 二人が同じ作業をするのを防げます。
- リポジトリをフォークし、ブランチを切って変更します。
- issue 番号を参照したプルリクエストを送ります。
ascorbic/macrodata のほかの issue
-
難易度 1/5 1時間未満 初心者へのやさしさ 70/100
-
難易度 1/5 1時間未満 初心者へのやさしさ 74/100
-
難易度 2/5 1〜3時間 初心者へのやさしさ 68/100
-
難易度 2/5 1〜3時間 初心者へのやさしさ 64/100
-
Support Cursor オープン
難易度 5/5 1週間以上 初心者へのやさしさ 25/100
ascorbic/macrodata の issue をすべて見る
似ている issue
-
難易度 2/5 1〜3時間 初心者へのやさしさ 70/100
-
難易度 2/5 1〜3時間 初心者へのやさしさ 75/100
mksglu/context-mode#1200 ·
-
難易度 2/5 1〜3時間 初心者へのやさしさ 75/100
jaegertracing/jaeger-ui#4506 ·
-
area:desktop area:ui bug platform:macos
難易度 2/5 1〜3時間 初心者へのやさしさ 75/100
anthropics/claude-code#96687 ·
-
good first issue
難易度 1/5 1時間未満 初心者へのやさしさ 95/100
AOSSIE-Org/DebateAI#582 · コメント 2 件 ·