From ea6076091491d35a5a49c83e6a46dd82e82f7a23 Mon Sep 17 00:00:00 2001 From: "claude[bot]" <41898282+claude[bot]@users.noreply.github.com> Date: Fri, 27 Feb 2026 06:27:05 +0000 Subject: [PATCH] perf: run GetGraph and GetCircularDependencies in parallel Both API calls are independent; run them concurrently using goroutines and buffered channels so total wall-clock time is max(GetGraph, GetCircularDeps) instead of the sum. Fixes #52. Co-Authored-By: Grey Newell Co-Authored-By: Claude Sonnet 4.6 --- cmd/run.go | 46 ++++++++++++++++++++++++++++++++++------------ 1 file changed, 34 insertions(+), 12 deletions(-) diff --git a/cmd/run.go b/cmd/run.go index bfe58f7..bca899a 100644 --- a/cmd/run.go +++ b/cmd/run.go @@ -245,8 +245,8 @@ func runWithoutCache(cfg *config.Config, proj *project.Info, wm *project.Working return nil } -// fetchGraphWithCircularDeps calls GetGraph and GetCircularDependencies, storing -// cycle count in Stats so it is cached alongside the graph. +// fetchGraphWithCircularDeps calls GetGraph and GetCircularDependencies concurrently, +// storing cycle count in Stats so it is cached alongside the graph. func fetchGraphWithCircularDeps( ctx context.Context, client *api.Client, @@ -254,20 +254,42 @@ func fetchGraphWithCircularDeps( repoZip []byte, logFn func(string, ...interface{}), ) (*api.ProjectGraph, error) { - graph, err := client.GetGraph(ctx, projectName, repoZip) - if err != nil { - return nil, err + type graphResult struct { + graph *api.ProjectGraph + err error + } + type circResult struct { + circDeps *api.CircularDependencyResponse + err error } - circDeps, err := client.GetCircularDependencies(ctx, projectName, repoZip) - if err != nil { - logFn("[warn] circular dependency check failed: %v", err) - } else if circDeps != nil { - graph.Stats.CircularDependencyCycles = len(circDeps.Cycles) - logFn("[debug] circular dependency cycles found: %d", graph.Stats.CircularDependencyCycles) + graphCh := make(chan graphResult, 1) + circCh := make(chan circResult, 1) + + go func() { + g, err := client.GetGraph(ctx, projectName, repoZip) + graphCh <- graphResult{g, err} + }() + + go func() { + c, err := client.GetCircularDependencies(ctx, projectName, repoZip) + circCh <- circResult{c, err} + }() + + gr := <-graphCh + if gr.err != nil { + return nil, gr.err + } + + cr := <-circCh + if cr.err != nil { + logFn("[warn] circular dependency check failed: %v", cr.err) + } else if cr.circDeps != nil { + gr.graph.Stats.CircularDependencyCycles = len(cr.circDeps.Cycles) + logFn("[debug] circular dependency cycles found: %d", gr.graph.Stats.CircularDependencyCycles) } - return graph, nil + return gr.graph, nil } // silentExit returns nil (success) so we never block Claude Code sessions.