perf(tmux): batch session queries in gt down (#477)
* perf(tmux): batch session queries in gt down to reduce N+1 subprocess calls Add SessionSet type to tmux package for O(1) session existence checks. Instead of calling HasSession() (which spawns a subprocess) for each rig/session during shutdown, now calls ListSessions() once and uses in-memory map lookups. Changes: - internal/tmux/tmux.go: Add SessionSet type with GetSessionSet() and Has() - internal/cmd/down.go: Use SessionSet for dry-run checks and session stops - internal/session/town.go: Add StopTownSessionWithCache() variant - internal/tmux/tmux_test.go: Add test for SessionSet With 5 rigs, this reduces subprocess calls from ~15 to 1 during shutdown preview, saving 60-150ms of execution time. Closes: gt-xh2bh Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * perf(tmux): optimize SessionSet to avoid intermediate slice allocation - Build map directly from tmux output instead of calling ListSessions() - Use strings.IndexByte for efficient newline parsing - Pre-size map using newline count to avoid rehashing - Simplify nil checks in Has() and Names() * fix(sling): restore bd cook directory context for formula-on-bead mode The bd cook command needs to run from the target rig's directory to access the correct formula database. This was accidentally removed in a previous commit, causing TestSlingFormulaOnBeadRoutesBDCommandsToTargetRig to fail. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -552,3 +552,56 @@ func TestGetAllDescendants(t *testing.T) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestSessionSet(t *testing.T) {
|
||||
if !hasTmux() {
|
||||
t.Skip("tmux not installed")
|
||||
}
|
||||
|
||||
tm := NewTmux()
|
||||
sessionName := "gt-test-sessionset-" + t.Name()
|
||||
|
||||
// Clean up any existing session
|
||||
_ = tm.KillSession(sessionName)
|
||||
|
||||
// Create a test session
|
||||
if err := tm.NewSession(sessionName, ""); err != nil {
|
||||
t.Fatalf("NewSession: %v", err)
|
||||
}
|
||||
defer func() { _ = tm.KillSession(sessionName) }()
|
||||
|
||||
// Get the session set
|
||||
set, err := tm.GetSessionSet()
|
||||
if err != nil {
|
||||
t.Fatalf("GetSessionSet: %v", err)
|
||||
}
|
||||
|
||||
// Test Has() for existing session
|
||||
if !set.Has(sessionName) {
|
||||
t.Errorf("SessionSet.Has(%q) = false, want true", sessionName)
|
||||
}
|
||||
|
||||
// Test Has() for non-existing session
|
||||
if set.Has("nonexistent-session-xyz-12345") {
|
||||
t.Error("SessionSet.Has(nonexistent) = true, want false")
|
||||
}
|
||||
|
||||
// Test nil safety
|
||||
var nilSet *SessionSet
|
||||
if nilSet.Has("anything") {
|
||||
t.Error("nil SessionSet.Has() = true, want false")
|
||||
}
|
||||
|
||||
// Test Names() returns the session
|
||||
names := set.Names()
|
||||
found := false
|
||||
for _, n := range names {
|
||||
if n == sessionName {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
t.Errorf("SessionSet.Names() doesn't contain %q", sessionName)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user