aboutsummaryrefslogtreecommitdiff
path: root/src/cmd
diff options
context:
space:
mode:
authormatloob <matloob@golang.org>2025-12-01 16:41:53 -0500
committerGopher Robot <gobot@golang.org>2026-03-09 15:47:55 -0700
commit43f8fb27ae5aed5a65b50c8dc49ef1557c1b293b (patch)
treed8995cee159c54cfc86f01ab73fe84ffd1312ef0 /src/cmd
parent710014bcc348d2bb36db8ee1b808170b18749899 (diff)
downloadgo-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.go105
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) {