Skip to content

Commit 561a9be

Browse files
committed
gopls/internal/lsp/filecache: actually delete files
This change fixes an embarrassing blunder: the filename we gave to os.Rename was absolutized twice (goplsDir+goplsDir+path), so of course it was not found. The error was rightly ignored, but this meant the bug was undetected. CI builder machines filled their disks. Also, this change causes filecache's GC to delete files older than maxAge as soon as it encounters them, instead of in the second pass over the sorted list of all files in the cache. This should allow short-lived processes (e.g. tests) to make progress on garbage collection. Though this now seems like a distinctly third-order effect compared to... not deleting files at all. Also: - don't delay between stats after deleting files based on age. - reduce the statDelay to 100us (was 1ms). Scanning a file tree on macOS is already very slow, at least on my Google-issued machine. - reduce maxAge to 5 days (was 7), which should still tide most users over a long weekend. Fixes golang/go#57900 Change-Id: I053f2891d6c52c94f4d5dd18903280dff2282eab Reviewed-on: https://go-review.googlesource.com/c/tools/+/462597 Reviewed-by: Bryan Mills <[email protected]> TryBot-Result: Gopher Robot <[email protected]> gopls-CI: kokoro <[email protected]> Run-TryBot: Alan Donovan <[email protected]>
1 parent 9682b0d commit 561a9be

File tree

1 file changed

+40
-15
lines changed

1 file changed

+40
-15
lines changed

gopls/internal/lsp/filecache/filecache.go

Lines changed: 40 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -243,9 +243,22 @@ func hashExecutable() (hash [32]byte, err error) {
243243
// process, possibly running a different version of gopls, possibly
244244
// running concurrently.
245245
func gc(goplsDir string) {
246-
const period = 1 * time.Minute // period between collections
247-
const statDelay = 1 * time.Millisecond // delay between stats to smooth out I/O
248-
const maxAge = 7 * 24 * time.Hour // max time since last access before file is deleted
246+
const period = 1 * time.Minute // period between collections
247+
const statDelay = 100 * time.Microsecond // delay between stats to smooth out I/O
248+
const maxAge = 5 * 24 * time.Hour // max time since last access before file is deleted
249+
250+
// The macOS filesystem is strikingly slow, at least on some machines.
251+
// /usr/bin/find achieves only about 25,000 stats per second
252+
// at full speed (no pause between items), meaning a large
253+
// cache may take several minutes to scan.
254+
// We must ensure that short-lived processes (crucially,
255+
// tests) are able to make progress sweeping garbage.
256+
//
257+
// (gopls' caches should never actually get this big in
258+
// practise: the example mentioned above resulted from a bug
259+
// that caused filecache to fail to delete any files.)
260+
261+
const debug = false
249262

250263
for {
251264
// Enumerate all files in the cache.
@@ -259,9 +272,21 @@ func gc(goplsDir string) {
259272
// TODO(adonovan): opt: also collect empty directories,
260273
// as they typically occupy around 1KB.
261274
if err == nil && !stat.IsDir() {
262-
files = append(files, item{path, stat})
263-
total += stat.Size()
264-
time.Sleep(statDelay)
275+
// Unconditionally delete files we haven't used in ages.
276+
// (We do this here, not in the second loop, so that we
277+
// perform age-based collection even in short-lived processes.)
278+
age := time.Since(stat.ModTime())
279+
if age > maxAge {
280+
if debug {
281+
log.Printf("age: deleting stale file %s (%dB, age %v)",
282+
path, stat.Size(), age)
283+
}
284+
os.Remove(path) // ignore error
285+
} else {
286+
files = append(files, item{path, stat})
287+
total += stat.Size()
288+
time.Sleep(statDelay)
289+
}
265290
}
266291
return nil
267292
})
@@ -272,18 +297,18 @@ func gc(goplsDir string) {
272297
})
273298

274299
// Delete oldest files until we're under budget.
275-
// Unconditionally delete files we haven't used in ages.
276300
budget := atomic.LoadInt64(&budget)
277301
for _, file := range files {
278-
age := time.Since(file.stat.ModTime())
279-
if total > budget || age > maxAge {
280-
if false { // debugging
281-
log.Printf("deleting stale file %s (%dB, age %v)",
282-
file.path, file.stat.Size(), age)
283-
}
284-
os.Remove(filepath.Join(goplsDir, file.path)) // ignore error
285-
total -= file.stat.Size()
302+
if total < budget {
303+
break
304+
}
305+
if debug {
306+
age := time.Since(file.stat.ModTime())
307+
log.Printf("budget: deleting stale file %s (%dB, age %v)",
308+
file.path, file.stat.Size(), age)
286309
}
310+
os.Remove(file.path) // ignore error
311+
total -= file.stat.Size()
287312
}
288313

289314
time.Sleep(period)

0 commit comments

Comments
 (0)