Skip to content

Apply cooldown to Go modules - #564

Merged
andrew merged 2 commits into
git-pkgs:mainfrom
simonchrz:feat/go-cooldown
Oct 23, 2026
Merged

andrew merged 2 commits into
git-pkgs:mainfrom
simonchrz:feat/go-cooldown

Conversation

@simonchrz

@simonchrz simonchrz commented Oct 23, 2026 •

Copy link
Copy Markdown
Contributor

Go part of #132, on the existing proxy.Cooldown hook as suggested there.

With cooldown on:

  • @v/list leaves out versions whose .info time is inside the window. Each listed version's .info is fetched once, eight at a time, and its time is stored in the versions table, the same way npm stores its times. The key uses the escaped module path (github.com/!azure/...), so modules that differ only in case don't share a row. Only canonical versions are stored, so a query like master is never stored.
  • If a .info can't be fetched, the list or download fails with 502 instead of letting the version through. A .mod or .zip whose version doesn't unescape gets 404 and is not passed upstream.
  • @latest is served unchanged when upstream's answer is out of cooldown. Otherwise the proxy serves the newest listed release out of cooldown, or the newest pre-release if there is no release.
  • A version in cooldown gets 403 for its .info, .mod and .zip, and so does a query such as @v/main.info that resolves to one. It is 403 rather than 404 because the go command tries the next GOPROXY entry after 404 or 410, so with GOPROXY=proxy,direct a 404 would fetch the version directly.

Caveat: Go has no publish time. .info gives the commit time of the revision, so a version tagged on an old commit counts as old. I noted this in the README and in docs/configuration.md.

I checked this with the go command against proxy.golang.org through a local proxy with a 30-day cooldown and GOPROXY=proxy,direct:

  • go get golang.org/x/mod@latest added v0.41.0. v0.42.0 is from 9 October.
  • go get golang.org/x/mod@v0.42.0 failed with 403 Forbidden: version is in cooldown and did not fall back to direct.

With cooldown on, @v/list leaves out versions whose .info time is
inside the window, and @latest falls back to the newest release out of
cooldown when upstream's answer is too new. Each listed version's
.info is read once and its time is stored in the versions table, as
npm does, except for modules whose path has uppercase letters, since
golang PURLs are lowercased.

A .info, .mod or .zip for a version in cooldown, and a query such as a
branch name that resolves to one, gets 403. The go command moves on to
the next GOPROXY entry after 404 or 410, so a 404 would let
GOPROXY=proxy,direct fetch the version directly.

Go has no publish time: .info carries the commit time of the revision,
so a version tagged on an old commit counts as old. The docs say so.

Part of git-pkgs#132
A failed .info fetch counted as an unknown time, so a rate-limited or
flaky upstream let versions in cooldown through. It now fails the list
or download with 502; only a version upstream doesn't know, or whose
.info has no time, is let through.

Times are stored under the escaped module path, which keeps modules
that differ only in case apart. Modules with uppercase letters were not
stored before and fetched every .info again on each list request. Only
canonical versions are stored, since a query such as a branch name can
resolve to a later commit.

A .mod or .zip whose version doesn't unescape is answered with 404
instead of being passed upstream unchecked.

@andrew andrew left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Small follow-up worth doing: with cooldown on, every @v/list request calls GetVersionByPURL once per listed version, so a module with 2,000 versions costs 2,000 DB queries per list even when every time is stored. One GetVersionsByPackagePURL at the top of allowedVersions, then fetching .info only for versions missing from that map, would cut it to one query.

@andrew
andrew merged commit 20e648f into git-pkgs:main Oct 23, 2026
6 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants