on main help sign in
fix: faster shutdown (#1570)

Signed-off-by: Carlos Alexandro Becker <caarlos0@users.noreply.github.com>

commit95b9a4298e1a23c20a5023b41565c3d797530753 authorCarlos Alexandro Becker <caarlos0@users.noreply.github.com> dateMon 8 Dec 2025, 11:00 (1 year ago) parentf50eabd
4 files changed+48−25
internal/agent/tools/diagnostics.go+1−1 internal/agent/tools/mcp/init.go+21−10 internal/app/app.go+22−10 internal/app/lsp.go+4−4
internal/agent/tools/diagnostics.go+1−1
@@ -94,7 +94,7 @@ func getDiagnostics(filePath string, lsps *csync.Map[string, *lsp.Client]) strin
9494 }
9595
9696 out := output.String()
97− slog.Info("Diagnostics", "output", out)
97+ slog.Debug("Diagnostics", "output", out)
9898 return out
9999}
100100
internal/agent/tools/mcp/init.go+21−10
@@ -109,14 +109,26 @@ func GetState(name string) (ClientInfo, bool) {
109109// Close closes all MCP clients. This should be called during application shutdown.
110110func Close() error {
111111 var errs []error
112+ var wg sync.WaitGroup
112113 for name, session := range sessions.Seq2() {
113− if err := session.Close(); err != nil &&
114− !errors.Is(err, io.EOF) &&
115− !errors.Is(err, context.Canceled) &&
116− err.Error() != "signal: killed" {
117− errs = append(errs, fmt.Errorf("close mcp: %s: %w", name, err))
118− }
114+ wg.Go(func() {
115+ done := make(chan bool, 1)
116+ go func() {
117+ if err := session.Close(); err != nil &&
118+ !errors.Is(err, io.EOF) &&
119+ !errors.Is(err, context.Canceled) &&
120+ err.Error() != "signal: killed" {
121+ errs = append(errs, fmt.Errorf("close mcp: %s: %w", name, err))
122+ }
123+ done <- true
124+ }()
125+ select {
126+ case <-done:
127+ case <-time.After(time.Millisecond * 250):
128+ }
129+ })
119130 }
131+ wg.Wait()
120132 broker.Shutdown()
121133 return errors.Join(errs...)
122134}
@@ -279,9 +291,8 @@ func createSession(ctx context.Context, name string, m config.MCPConfig, resolve
279291 })
280292 },
281293 LoggingMessageHandler: func(_ context.Context, req *mcp.LoggingMessageRequest) {
282− slog.Info("mcp log", "name", name, "data", req.Params.Data)
294+ slog.Info("MCP log", "name", name, "data", req.Params.Data)
283295 },
284− KeepAlive: time.Minute * 10,
285296 },
286297 )
287298
@@ -289,14 +300,14 @@ func createSession(ctx context.Context, name string, m config.MCPConfig, resolve
289300 if err != nil {
290301 err = maybeStdioErr(err, transport)
291302 updateState(name, StateError, maybeTimeoutErr(err, timeout), nil, Counts{})
292− slog.Error("error starting mcp client", "error", err, "name", name)
303+ slog.Error("MCP client failed to initialize", "error", err, "name", name)
293304 cancel()
294305 cancelTimer.Stop()
295306 return nil, err
296307 }
297308
298309 cancelTimer.Stop()
299− slog.Info("Initialized mcp client", "name", name)
310+ slog.Info("MCP client initialized", "name", name)
300311 return session, nil
301312}
302313
internal/app/app.go+22−10
@@ -370,30 +370,42 @@ func (app *App) Subscribe(program *tea.Program) {
370370
371371// Shutdown performs a graceful shutdown of the application.
372372func (app *App) Shutdown() {
373+ start := time.Now()
374+ defer func() { slog.Info("Shutdown took " + time.Since(start).String()) }()
375+ var wg sync.WaitGroup
373376 if app.AgentCoordinator != nil {
374− app.AgentCoordinator.CancelAll()
377+ wg.Go(func() {
378+ app.AgentCoordinator.CancelAll()
379+ })
375380 }
376381
377382 // Kill all background shells.
378− shell.GetBackgroundShellManager().KillAll()
383+ wg.Go(func() {
384+ shell.GetBackgroundShellManager().KillAll()
385+ })
379386
380387 // Shutdown all LSP clients.
381388 for name, client := range app.LSPClients.Seq2() {
382− shutdownCtx, cancel := context.WithTimeout(app.globalCtx, 5*time.Second)
383− if err := client.Close(shutdownCtx); err != nil {
384− slog.Error("Failed to shutdown LSP client", "name", name, "error", err)
385− }
386− cancel()
389+ wg.Go(func() {
390+ shutdownCtx, cancel := context.WithTimeout(app.globalCtx, 5*time.Second)
391+ defer cancel()
392+ if err := client.Close(shutdownCtx); err != nil {
393+ slog.Error("Failed to shutdown LSP client", "name", name, "error", err)
394+ }
395+ })
387396 }
388397
389398 // Call call cleanup functions.
390399 for _, cleanup := range app.cleanupFuncs {
391400 if cleanup != nil {
392− if err := cleanup(); err != nil {
393− slog.Error("Failed to cleanup app properly on shutdown", "error", err)
394− }
401+ wg.Go(func() {
402+ if err := cleanup(); err != nil {
403+ slog.Error("Failed to cleanup app properly on shutdown", "error", err)
404+ }
405+ })
395406 }
396407 }
408+ wg.Wait()
397409}
398410
399411// checkForUpdates checks for available updates.
internal/app/lsp.go+4−4
@@ -23,11 +23,11 @@ func (app *App) initLSPClients(ctx context.Context) {
2323
2424// createAndStartLSPClient creates a new LSP client, initializes it, and starts its workspace watcher
2525func (app *App) createAndStartLSPClient(ctx context.Context, name string, config config.LSPConfig) {
26− slog.Info("Creating LSP client", "name", name, "command", config.Command, "fileTypes", config.FileTypes, "args", config.Args)
26+ slog.Debug("Creating LSP client", "name", name, "command", config.Command, "fileTypes", config.FileTypes, "args", config.Args)
2727
2828 // Check if any root markers exist in the working directory (config now has defaults)
2929 if !lsp.HasRootMarkers(app.config.WorkingDir(), config.RootMarkers) {
30− slog.Info("Skipping LSP client - no root markers found", "name", name, "rootMarkers", config.RootMarkers)
30+ slog.Debug("Skipping LSP client: no root markers found", "name", name, "rootMarkers", config.RootMarkers)
3131 updateLSPState(name, lsp.StateDisabled, nil, nil, 0)
3232 return
3333 }
@@ -53,7 +53,7 @@ func (app *App) createAndStartLSPClient(ctx context.Context, name string, config
5353 // Initialize LSP client.
5454 _, err = lspClient.Initialize(initCtx, app.config.WorkingDir())
5555 if err != nil {
56− slog.Error("Initialize failed", "name", name, "error", err)
56+ slog.Error("LSP client initialization failed", "name", name, "error", err)
5757 updateLSPState(name, lsp.StateError, err, lspClient, 0)
5858 lspClient.Close(ctx)
5959 return
@@ -68,7 +68,7 @@ func (app *App) createAndStartLSPClient(ctx context.Context, name string, config
6868 updateLSPState(name, lsp.StateError, err, lspClient, 0)
6969 } else {
7070 // Server reached a ready state scuccessfully.
71− slog.Info("LSP server is ready", "name", name)
71+ slog.Debug("LSP server is ready", "name", name)
7272 lspClient.SetServerState(lsp.StateReady)
7373 updateLSPState(name, lsp.StateReady, nil, lspClient, 0)
7474 }