mirror of
https://github.com/golang/go.git
synced 2026-04-03 17:59:55 +09:00
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>
This commit is contained in:
@@ -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()
|
||||
|
||||
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
|
||||
return r.statCache.Do(rev, func() (*RevInfo, error) {
|
||||
unlock, err := r.mu.Lock()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
defer unlock()
|
||||
|
||||
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
|
||||
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
|
||||
})
|
||||
}
|
||||
|
||||
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) {
|
||||
|
||||
Reference in New Issue
Block a user