gopls/internal/lsp/cache: limit module scan to 100K files
When no go.work or go.mod file is found, gopls searches to see if there
is exactly one module in a nested directory, in which case it narrows
the workspace to this one module. This is a legacy workaround for
polyglot repositories, and will be made obsolete by golang/go#57979.
However, in the meantime this feature is still necessary, and is the
last remaining place where we walk the workspace looking for modules. As
reported in golang/go#56496, this search can be expensive in very large
directories.
Reduce the search limit 10x, from 1M->100K, and use the more efficient
filepath.WalkDir.
Fixes golang/go#56496
Change-Id: Ia46dd90ac2220b09debc68742dd882885c38eb42
Reviewed-on: https://go-review.googlesource.com/c/tools/+/496880
Reviewed-by: Alan Donovan <adonovan@google.com>
TryBot-Result: Gopher Robot <gobot@golang.org>
Run-TryBot: Robert Findley <rfindley@google.com>
diff --git a/gopls/internal/lsp/cache/workspace.go b/gopls/internal/lsp/cache/workspace.go
index de36da6..28179f5 100644
--- a/gopls/internal/lsp/cache/workspace.go
+++ b/gopls/internal/lsp/cache/workspace.go
@@ -8,6 +8,7 @@
"context"
"errors"
"fmt"
+ "io/fs"
"os"
"path/filepath"
"sort"
@@ -127,7 +128,10 @@
// Limit go.mod search to 1 million files. As a point of reference,
// Kubernetes has 22K files (as of 2020-11-24).
-const fileLimit = 1000000
+//
+// Note: per golang/go#56496, the previous limit of 1M files was too slow, at
+// which point this limit was decreased to 100K.
+const fileLimit = 100_000
// findModules recursively walks the root directory looking for go.mod files,
// returning the set of modules it discovers. If modLimit is non-zero,
@@ -139,7 +143,7 @@
modFiles := make(map[span.URI]struct{})
searched := 0
errDone := errors.New("done")
- err := filepath.Walk(root.Filename(), func(path string, info os.FileInfo, err error) error {
+ err := filepath.WalkDir(root.Filename(), func(path string, info fs.DirEntry, err error) error {
if err != nil {
// Probably a permission error. Keep looking.
return filepath.SkipDir