go: proxy.golang.org: don't cache /@latest for modules that no longer exist at the upstream head revision

In gomodule/redigo#440, a nested github.com/gomodule/redigo/redis module was created and subsequently removed from the github.com/gomodule/redigo repository: today, the nested module does not exist at the head revision of that repo, and also does not exist in the latest tagged release of the parent module (github.com/gomodule/redigo).

In https://github.com/gomodule/redigo/pull/440#issuecomment-621892705 and https://github.com/gomodule/redigo/pull/440#issuecomment-621930004, I noted that proxy.golang.org was still serving an old commit for /@latest for the nested module over an hour later, even though GOPROXY=direct correctly reported that there was no latest version of that module, and even though proxy.golang.org did know about a higher (and later) pseudo-version of that module.

As a workaround, the owners of the repo had to tag an explicit release of the nested module, even though they did not intend for anyone to actually use that release.

I don’t think that workaround should have been necessary: I think proxy.golang.org should not serve known-stale contents for the /@latest endpoint. That endpoint should be invalidated whenever a newer version of the named module is successfully fetched, even if that version is not itself latest (as interpreted by the go command).

That may mean that /@latest transitions from advertising a version of the module to no longer advertising any such version, but that behavior is no worse than what would happen if the repo owner took some other drastic action to remove the module — such as publishing a commit on the default branch with all of the source code deleted, or publishing a commit with the module line declaring some other path. Regardless of the reason, proxy.golang.org should view the affirmative non-existence of a module as a valid “latest” state for that module, and not get stuck advertising “the last version that did exist”.

This only really matters for modules that never explicitly tagged any release or pre-release version, since the go command does not consult the /@latest endpoint unless the list of release and pre-release versions is empty.

(Note that, even if the /@latest endpoint itself does not advertise any version, the proxy should continue to serve all previously-fetched versions of any module with a suitable license. I am not proposing that we evict any explicit versions from the cache — only the /@latest endpoint itself.)

CC @jayconrod @matloob @katiehockman @heschik @hyangah @stevenh

About this issue

  • Original URL
  • State: open
  • Created 4 years ago
  • Reactions: 3
  • Comments: 15 (15 by maintainers)

Most upvoted comments

The reason that proxy is treating @latest differently is because that’s part of the module mirror’s promise (no more left-pad) and affects end users.

The “mirror’s promise” is that existing users will be able to continue to use the dependency versions that they were already using. It does not promise that the latest version of a module will always be a usable version of that module.

As noted in https://github.com/golang/go/issues/39007#issuecomment-628351207, it is already possible for a module author to break latest for new users if they so choose, and in the case of left-pad the decision to break users seems to have been intentional.

There are many tools that do not use the versioned tags. For example, the vscode go extension depends on many tools and many of them still go by pseudo-versions.

Expiring the cache entry for latest would not prevent any of those existing users from continuing to use the versions they already depend on.