Skip to content

perf(tmux): skip dead sockets so list_panes doesn't spawn tmux per orphan#56

Merged
jiunbae merged 1 commit into
mainfrom
perf/skip-dead-tmux-sockets
Jun 16, 2026
Merged

perf(tmux): skip dead sockets so list_panes doesn't spawn tmux per orphan#56
jiunbae merged 1 commit into
mainfrom
perf/skip-dead-tmux-sockets

Conversation

@jiunbae

@jiunbae jiunbae commented Jun 16, 2026

Copy link
Copy Markdown
Member

Problem

muxa watch showed its list ~1s late on first paint. Root cause was not the watch client or streaming — it was crate::tmux::list_panes() (called by the watch's priming refresh, muxa status, and the daemon scanner) enumerating every socket file under /tmp/tmux-<uid> and spawning a tmux -S <sock> list-panes for each.

tmux only unlinks a server's own socket on clean shutdown, so servers killed abnormally (test timeouts, SIGKILL, parent shell dying) leave orphan socket files. On a host without a working /tmp reaper (a container where systemd-tmpfiles never runs) they accumulate without bound — ~220 orphans were observed locally. Each orphan cost a full tmux process spawn (~2ms) on every scan → ~0.5s, surfacing as the late first paint.

Fix

enumerate_sockets() now drops dead sockets with a cheap connect() probe before any caller spawns tmux: a live server accepts, a stale file refuses instantly with ECONNREFUSED (no blocking, no spawn). Other connect errors keep the socket so we never hide one we merely failed to probe. Benefits every caller (list_panes, scanner, daemon reconciler/discovery).

Measured

muxa status (shares the list_panes path): 485ms → ~20ms with ~220 orphan sockets present.

Test

New unit test socket_is_live_distinguishes_listening_from_orphan (bound listener = live; bind-then-drop orphan = dead; missing = dead). cargo fmt/clippy -D warnings/test -p muxa green.

🤖 Generated with Claude Code

…n tmux spawns

`list_panes()` / the scanner enumerate every socket file under
`/tmp/tmux-<uid>` and spawn a `tmux -S <sock> list-panes` per file. tmux
only unlinks a server's own socket on clean shutdown, so servers killed
abnormally (test timeouts, SIGKILL, parent shell dying) leave orphan socket
files, and on a host without a working /tmp reaper (e.g. a container where
systemd-tmpfiles never runs) they accumulate without bound — hundreds were
seen in the wild. Each orphan cost a full tmux process spawn (~2ms) on every
scan, so a few hundred turned into ~0.5s, which showed up as a visibly late
first paint in `muxa watch` (the priming refresh waits on list_panes).

enumerate_sockets now drops dead sockets with a cheap connect() probe before
any caller spawns tmux: a live server accepts, a stale file refuses instantly
with ECONNREFUSED (no blocking, no spawn). Other connect errors keep the
socket so we never hide one we merely failed to probe. Measured locally:
`muxa status` 485ms -> ~20ms with ~220 orphan sockets present.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@jiunbae jiunbae merged commit 278c331 into main Jun 16, 2026
5 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