From 43f8fb27ae5aed5a65b50c8dc49ef1557c1b293b Mon Sep 17 00:00:00 2001 From: matloob Date: Mon, 1 Dec 2025 16:41:53 -0500 Subject: cmd/go/internal/modfetch/codehost: cache some vcs ops hg is very slow (takes about 150ms per invocation on my machine) and in modfetch.(*codeRepo).convert we do many vcs operations that are sometimes repeated. We can cache the stat and readFile operations and make a single go get operation twice as fast (about 4 to 2 seconds). Change-Id: I3f30403c941ff9d91461c8f8a458615c6a6a6964 Reviewed-on: https://go-review.googlesource.com/c/go/+/725700 Reviewed-by: Michael Matloob Reviewed-by: Dmitri Shuralyov Auto-Submit: Michael Matloob LUCI-TryBot-Result: Go LUCI Reviewed-by: Dmitri Shuralyov --- src/cmd/go/internal/modfetch/codehost/vcs.go | 105 ++++++++++++++------------- 1 file changed, 56 insertions(+), 49 deletions(-) (limited to 'src/cmd') diff --git a/src/cmd/go/internal/modfetch/codehost/vcs.go b/src/cmd/go/internal/modfetch/codehost/vcs.go index 59264a3b90..dacda2c8b2 100644 --- a/src/cmd/go/internal/modfetch/codehost/vcs.go +++ b/src/cmd/go/internal/modfetch/codehost/vcs.go @@ -88,6 +88,9 @@ type vcsRepo struct { repoSumOnce sync.Once repoSum string + + statCache par.ErrCache[string, *RevInfo] // cache key is revision + readFileCache par.ErrCache[[2]string, []byte] // cache key is revision and file path } func newVCSRepo(ctx context.Context, vcs, remote string, local bool) (Repo, error) { @@ -471,40 +474,42 @@ func (r *vcsRepo) Tags(ctx context.Context, prefix string) (*Tags, error) { } func (r *vcsRepo) Stat(ctx context.Context, rev string) (*RevInfo, error) { - unlock, err := r.mu.Lock() - if err != nil { - return nil, err - } - defer unlock() + return r.statCache.Do(rev, func() (*RevInfo, error) { + unlock, err := r.mu.Lock() + if err != nil { + return nil, err + } + defer unlock() - if rev == "latest" { - rev = r.cmd.latest - } - r.branchesOnce.Do(func() { r.loadBranches(ctx) }) - if r.local { - // Ignore the badLocalRevRE precondition in local only mode. - // We cannot fetch latest upstream changes so only serve what's in the local cache. - return r.statLocal(ctx, rev) - } - revOK := (r.cmd.badLocalRevRE == nil || !r.cmd.badLocalRevRE.MatchString(rev)) && !r.branches[rev] - if revOK { - if info, err := r.statLocal(ctx, rev); err == nil { - return info, nil + if rev == "latest" { + rev = r.cmd.latest + } + r.branchesOnce.Do(func() { r.loadBranches(ctx) }) + if r.local { + // Ignore the badLocalRevRE precondition in local only mode. + // We cannot fetch latest upstream changes so only serve what's in the local cache. + return r.statLocal(ctx, rev) + } + revOK := (r.cmd.badLocalRevRE == nil || !r.cmd.badLocalRevRE.MatchString(rev)) && !r.branches[rev] + if revOK { + if info, err := r.statLocal(ctx, rev); err == nil { + return info, nil + } } - } - r.fetchOnce.Do(func() { r.fetch(ctx) }) - if r.fetchErr != nil { - return nil, r.fetchErr - } - info, err := r.statLocal(ctx, rev) - if err != nil { - return info, err - } - if !revOK { - info.Version = info.Name - } - return info, nil + r.fetchOnce.Do(func() { r.fetch(ctx) }) + if r.fetchErr != nil { + return nil, r.fetchErr + } + info, err := r.statLocal(ctx, rev) + if err != nil { + return info, err + } + if !revOK { + info.Version = info.Name + } + return info, nil + }) } func (r *vcsRepo) fetch(ctx context.Context) { @@ -547,26 +552,28 @@ func (r *vcsRepo) Latest(ctx context.Context) (*RevInfo, error) { } func (r *vcsRepo) ReadFile(ctx context.Context, rev, file string, maxSize int64) ([]byte, error) { - if rev == "latest" { - rev = r.cmd.latest - } - _, err := r.Stat(ctx, rev) // download rev into local repo - if err != nil { - return nil, err - } + return r.readFileCache.Do([2]string{rev, file}, func() ([]byte, error) { + if rev == "latest" { + rev = r.cmd.latest + } + _, err := r.Stat(ctx, rev) // download rev into local repo + if err != nil { + return nil, err + } - // r.Stat acquires r.mu, so lock after that. - unlock, err := r.mu.Lock() - if err != nil { - return nil, err - } - defer unlock() + // r.Stat acquires r.mu, so lock after that. + unlock, err := r.mu.Lock() + if err != nil { + return nil, err + } + defer unlock() - out, err := Run(ctx, r.dir, r.cmd.readFile(rev, file, r.remote)) - if err != nil { - return nil, fs.ErrNotExist - } - return out, nil + out, err := Run(ctx, r.dir, r.cmd.readFile(rev, file, r.remote)) + if err != nil { + return nil, fs.ErrNotExist + } + return out, nil + }) } func (r *vcsRepo) RecentTag(ctx context.Context, rev, prefix string, allowed func(string) bool) (tag string, err error) { -- cgit v1.3-6-g1900