diff options
| author | matloob <matloob@golang.org> | 2025-12-01 16:41:53 -0500 |
|---|---|---|
| committer | Gopher Robot <gobot@golang.org> | 2026-03-09 15:47:55 -0700 |
| commit | 43f8fb27ae5aed5a65b50c8dc49ef1557c1b293b (patch) | |
| tree | d8995cee159c54cfc86f01ab73fe84ffd1312ef0 /src/cmd | |
| parent | 710014bcc348d2bb36db8ee1b808170b18749899 (diff) | |
| download | go-43f8fb27ae5aed5a65b50c8dc49ef1557c1b293b.tar.xz | |
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 <matloob@google.com>
Reviewed-by: Dmitri Shuralyov <dmitshur@golang.org>
Auto-Submit: Michael Matloob <matloob@golang.org>
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Reviewed-by: Dmitri Shuralyov <dmitshur@google.com>
Diffstat (limited to 'src/cmd')
| -rw-r--r-- | src/cmd/go/internal/modfetch/codehost/vcs.go | 105 |
1 files changed, 56 insertions, 49 deletions
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) { |
