From 2a5890cd46e5658b5ceab6cfd7a06b3bfe947fb9 Mon Sep 17 00:00:00 2001 From: Michael Matloob Date: Mon, 9 Mar 2026 13:51:56 -0400 Subject: [PATCH] cmd/go/internal/cache: update trim timestamp before trimming This reduces the chance that multiple go commands running in CI will try to trim at the same time, causing contention and slowing things down. Fixes #76314 Change-Id: I3edf818fc9583795f3f51b715fdbe75b6a6a6964 Reviewed-on: https://go-review.googlesource.com/c/go/+/753240 Reviewed-by: Michael Matloob LUCI-TryBot-Result: Go LUCI Auto-Submit: Michael Matloob Reviewed-by: Alan Donovan --- src/cmd/go/internal/cache/cache.go | 41 ++++++++++++++++++++++-------- 1 file changed, 31 insertions(+), 10 deletions(-) diff --git a/src/cmd/go/internal/cache/cache.go b/src/cmd/go/internal/cache/cache.go index 23cc531e69..67f8202c06 100644 --- a/src/cmd/go/internal/cache/cache.go +++ b/src/cmd/go/internal/cache/cache.go @@ -385,13 +385,42 @@ func (c *DiskCache) Trim() error { // trim time is too far in the future, attempt the trim anyway. It's possible that // the cache was full when the corruption happened. Attempting a trim on // an empty cache is cheap, so there wouldn't be a big performance hit in that case. - if data, err := lockedfile.Read(filepath.Join(c.dir, "trim.txt")); err == nil { + skipTrim := func(data []byte) bool { if t, err := strconv.ParseInt(strings.TrimSpace(string(data)), 10, 64); err == nil { lastTrim := time.Unix(t, 0) if d := now.Sub(lastTrim); d < trimInterval && d > -mtimeInterval { - return nil + return true } } + return false + } + // Check to see if we need a trim. Do this check separately from the lockedfile.Transform + // so that we can skip getting an exclusive lock in the common case. + if data, err := lockedfile.Read(filepath.Join(c.dir, "trim.txt")); err == nil { + if skipTrim(data) { + return nil + } + } + + errFileChanged := errors.New("file changed") + + // Write the new timestamp before we start trimming to reduce the chance that multiple invocations + // try to trim at the same time, causing contention in CI (#76314). + err := lockedfile.Transform(filepath.Join(c.dir, "trim.txt"), func(data []byte) ([]byte, error) { + if skipTrim(data) { + // The timestamp in the file no longer meets the criteria for us to + // do a trim. It must have been updated by another go command invocation + // since we last read it. Skip the trim. + return nil, errFileChanged + } + return fmt.Appendf(nil, "%d", now.Unix()), nil + }) + if errors.Is(err, errors.ErrUnsupported) { + return err + } + if errors.Is(err, errFileChanged) { + // Skip the trim because we don't need it anymore. + return nil } // Trim each of the 256 subdirectories. @@ -403,14 +432,6 @@ func (c *DiskCache) Trim() error { c.trimSubdir(subdir, cutoff) } - // Ignore errors from here: if we don't write the complete timestamp, the - // cache will appear older than it is, and we'll trim it again next time. - var b bytes.Buffer - fmt.Fprintf(&b, "%d", now.Unix()) - if err := lockedfile.Write(filepath.Join(c.dir, "trim.txt"), &b, 0o666); err != nil { - return err - } - return nil }