Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

cmd/go: add GOMODCACHE #34527

Closed
mvdan opened this issue Sep 25, 2019 · 39 comments
Closed

cmd/go: add GOMODCACHE #34527

mvdan opened this issue Sep 25, 2019 · 39 comments
Labels
FrozenDueToAge GoCommand cmd/go modules NeedsFix The path to resolution is known, but the work has not been done. Proposal Proposal-Accepted
Milestone

Comments

@mvdan
Copy link
Member

mvdan commented Sep 25, 2019

Summary

Add GOMODCACHE to control where the module download cache lives. Its default can continue to be GOPATH[0]/pkg/mod, and the variable would be very similar and consistent with GOCACHE.

Description

The module download cache has lived in GOPATH[0]/pkg/mod/ since it first appeared. It's understandable why it doesn't live under GOCACHE, where the build cache is located; builds are generally fast and reliable if one has the source, but downloading a module from the internet isn't nearly as reliable.

I also understand why it was put under GOPATH; until recently, it was the only persistent directory that Go made use of. That only changed in the last release, with the addition of os.UserConfigDir()+"/go/env" for go env -w.

However, there's no way to configure where the module download cache is located. For example, this is useful in CI environments to place the build and module download caches somewhere that's persisted between builds.

The only way to store the download cache elsewhere is to move GOPATH entirely. This has several disadvantages:

  1. GOPATH contains much more. For many users, it still contains code. For almost everyone, it also contains the installed binaries, unless they've set GOBIN. It's too big of a knob to just change the location of the module download cache.

  2. Many environments explicitly set GOPATH, such as the golang:1.13 image, meaning that we can't simply go env -w GOPATH=/custom/path just like we could with GOCACHE. This makes the module download cache harder to deal with than the build cache, for no apparent reason.

  3. GOPATH's future is uncertain; it might contain more in the future, or it might go away entirely. Relying on it to set the module download cache location is not a good long-term plan.

This idea first came up in #31283, which was closed by its author. I left a comment there a while ago, but thought it would be better to open a new proposal.

@gopherbot gopherbot added this to the Proposal milestone Sep 25, 2019
@mvdan
Copy link
Member Author

mvdan commented Sep 25, 2019

One unfortunate side of this proposal is that GOCACHE seems to mean "directory holding all Go cached data", while GOMODCACHE would be separate. If we had a time machine, I'd argue that GOCACHE should be called GOBUILDCACHE instead.

An alternative idea is to eventually re-define GOPATH as a directory to store persistent data that doesn't belong in the short-lived GOCACHE nor in <config>/go/env. That would solve points 1 and 3, but not 2.

An alternative partial solution to point 2 is to stop explicitly declaring GOPATH in the official Docker images. That might break users who haven't switched over to $(go env GOPATH) to handle the default properly, but I hope that eventually the env var can be removed anyway.

@dmitshur
Copy link
Contributor

Thanks for making this proposal. I briefly recall conversations in the past that we might want to move the module cache out of $GOPATH/pkg eventually, but it's helpful to have something more actionable open.

I would like to see the module cache to not be in GOPATH in the future. If it's viable, I think a better outcome is if we can do this without adding configuration. That may not be viable.

@mvdan
Copy link
Member Author

mvdan commented Sep 26, 2019

I think we have multiple options if moving the module download cache is an option. For example:

  • Put it under GOCACHE, since it's a cache after all - just an expensive one to rebuild. One could end up with a directory structure like GOCACHE/build and GOCACHE/mod, to separate the cheap build cache.
  • Put it alongside go-build in os.UserCacheDir, for example <go-mod>. Though for consistency, you'd probably still require GOMODCACHE.

Both of these options put the expensive and cheap caches in the same place by default, though. If we want to properly separate the moduel cache from the build cache, here's another idea:

  • Put it under os.UserDataDir, which in an ideal world would be a suitable and portable place to put non-config, non-temporary data.

@bcmills
Copy link
Contributor

bcmills commented Sep 27, 2019

CC @jayconrod

@jayconrod
Copy link
Contributor

Adding a GOMODCACHE variable to control the cache location makes total sense to me.

Moving the default location of the module cache seems more disruptive. Should we have some logic that checks if $GOPATH[0]/pkg/mod exists, then if not, some directory in os.UserCacheDir()?

@mvdan
Copy link
Member Author

mvdan commented Sep 27, 2019

I think if we're happy with adding an env var, we shouldn't worry about moving the default location right now. I only replied to that point to show some thoughts that might be helpful in the long term, if we do want to move it at some point.

@shoenig
Copy link
Contributor

shoenig commented Oct 13, 2019

I found myself wanting a GOMODCACHE variable recently for another use case:
If for example you have a local machine running an IDE talking to a remote server where your source tree lives mounted over NFS, Go modules are going to cause a bad time due to their use of flock, which NFS does not support.

go: failed to lock file at /Volumes/nfs/go/pkg/mod/cache/lock

Of course there is a workaround by creating a dummy GOPATH tree on the local machine and configuring the IDE to prepend that GOPATH ahead of the real one, so that it caches modules locally. That's a rather counter-intuitive thing to do though, and a direct environment variable for controlling the mod directory would be a nice feature.

@rsc
Copy link
Contributor

rsc commented Nov 27, 2019

Please don't move the default location of the download cache into GOCACHE.
Quoting https://groups.google.com/forum/#!msg/golang-dev/RjSj4bGSmsw/KMHhU8fmAwAJ:

I understand why this would seem surprising at first, but cached compilation resuilts and cached downloaded source code are a bit different.

The build cache ($GOCACHE, defaulting to $HOME/.cache/go-build) is for storing recent compilation results, so that if you need to do that exact compilation again, you can just reuse the file. The build cache holds entries that are like "if you run this exact compiler on these exact inputs. this is the output you'd get." If the answer is not in the cache, your build uses a little more CPU to run the compiler nstead of reusing the output. But you are guaranteed to be able to run the compiler instead, since you have the exact inputs and the compiler binary (or else you couldn't even look up the answer in the cache).

The module cache ($GOPATH/src/mod, defaulting to $HOME/go/src/mod) is for storing downloaded source code, so that every build does not redownload the same code and does not require the network or the original code to be available. The module cache holds entries that are like "if you need to download mymodule@v1.2.3, here are the files you'd get." If the answer is not in the cache, you have to go out to the network. Maybe you don't have a network right now. Maybe the code has been deleted. It's not anywhere near guaranteed that you can redownload the sources and also get the same result. Hopefully you can, but it's not an absolute certainty like for the build cache. (The go.sum file will detect if you get a different answer on re-download, but knowing you got the wrong bits doesn't help you make progress on actually building your code. Also these paths end up in file-line information in binaries, so they show up in stack traces, and the like and feed into tools like text editors or debuggers that don't necessarily know how to trigger the right cache refresh.)

I expect there are cron jobs or other tools that clean $HOME/.cache periodically. If part of the build cache got deleted, it would be no big deal, so it's fine to store the build cache there. But if downloaded source code got deleted unasked, I think that would potentially be quite surprising and problematic in various ways. That's why we store the source code in $GOPATH/src/mod, to keep it away from more expendable data.

@rsc
Copy link
Contributor

rsc commented Nov 27, 2019

Adding GOMODCACHE seems fine to me, and @jayconrod said that too. @bcmills?

@rsc rsc changed the title Proposal: cmd/go: add GOMODCACHE proposal: cmd/go: add GOMODCACHE Nov 27, 2019
@jayconrod
Copy link
Contributor

Just to echo @rsc and my earlier comment: adding GOMODCACHE seems fine. Let's not change the default location though.

@bcmills
Copy link
Contributor

bcmills commented Dec 4, 2019

Agreed: a GOMODCACHE variable seems useful enough.

The non-parallel naming with GOCACHE is unfortunate, but probably not unfortunate enough to warrant anything as drastic as renaming GOCACHE or relocating the default GOMODCACHE.

@mvdan
Copy link
Member Author

mvdan commented Dec 4, 2019

As a quick drive-by thought, if we want to fix the inconsistency with GOCACHE, we could deprecate it in favor of something like GOBUILDCACHE, but keep accepting GOCACHE as a fallback. That still wouldn't be perfect though, as it's not just the build cache, but also the test cache. So I agree that it's not worth the effort, and GOCACHE is good enough as it is.

@flibustenet
Copy link

An other value of GOMODCACHE is to change it per project. For example to keep a different cache for volatile project and one for persistent project.
With GOMODCACHE=./cache it could even replace ./vendor !

@h12w
Copy link

h12w commented Dec 5, 2019

Why not call it GOMODPATH to avoid the unfortunate non-parallel naming? (It is not a cache like build cache or test cache, but more of a source code path like $GOPATH/src). @mvdan

@mvdan
Copy link
Member Author

mvdan commented Dec 5, 2019

@h12w because everywhere in the docs it's called module cache or module download cache. I think "cache" is an okay name, and the cost of changing it is too high - confusing a large portion of Go developers.

@h12w
Copy link

h12w commented Dec 5, 2019

A module cache directory/path is a module directory/path. The word cache could be omitted because there are no "non-cache" directories for modules. Looking through go env, there are also GOTMPDIR and GOTOOLDIR, so IMHO, both GOMODDIR and GOMODPATH are okay names too. @mvdan

@aofei
Copy link
Contributor

aofei commented Dec 5, 2019

Since we can't use GOCACHE, then I think GOMODCACHE is our best option. Because we already have some things that are using MODCACHE, such as go clean -modcache. I think DIR is weird (at least we have never used) and one GOPATH is enough, please don't bring more PATHs. 😂

@mvdan
Copy link
Member Author

mvdan commented Dec 5, 2019

What @aofei said. We don't do go mod clean -modpath or go mod clean -modcachepath.

@bcmills bcmills modified the milestones: Proposal, Go1.15 Jan 28, 2020
@bcmills bcmills added NeedsFix The path to resolution is known, but the work has not been done. and removed Proposal labels Feb 14, 2020
@kaey
Copy link

kaey commented Feb 27, 2020

There is no need to run an explicit server if you already have all of the files cached

I do not and I already mentioned above what it takes to use go mod download see fetch() function example above.

But this is really getting off into a tangent. #35922 is probably a more relevant venue for this discussion.

My original proposal is still about expanding scope of GOMODCACHE, I only provide reasoning and potential alternative approaches.

@rsc
Copy link
Contributor

rsc commented Mar 25, 2020

I talked to @bcmills, @jayconrod, and @matloob (the team working on the go command) two weeks ago about this, but I forgot to summarize here. I think we all agree about the following.

We should add GOMODCACHE, defaulting to GOPATH/pkg/mod. Setting it would redirect all current accesses in GOPATH/pkg/mod to GOMODCACHE instead.

We should not move GOPATH/pkg/sumdb, nor make it controlled by GOMODCACHE.

The module cache is just that: a cache. If the module cache is removed, it can safely be refilled by redownloading. During the redownloading, all the bits will be reverified using the checksum database,
which is what makes it safe to start over. No one can replace the bits you used to have with different bits. Even large fragments of the checksum database are cached in GOMODCACHE. Those are okay to delete too; they'll be reverified on download as well.

The one thing that makes all of this safe is the single file GOPATH/pkg/sumdb/sum.golang.org/latest. It contains the single hash that all of this other verification relies upon. It cannot be deleted without giving all that up. It therefore does not belong in the module cache next to all the deletable things. (That's why it's not.)

CI/CD systems that want to point GOMODCACHE at a different directory should not need to worry about GOPATH/pkg/sumdb. As long as the repo being built has a complete go.sum, the go command will not do anything with the checksum database. I believe -mod=readonly will keep from even attempting to use the checksum database, and that's what CI/CD systems should be using anyway.

@rsc
Copy link
Contributor

rsc commented Apr 1, 2020

Based on the discussion above, it sounds like this is a likely accept.

@rsc
Copy link
Contributor

rsc commented Apr 8, 2020

No change in consensus, so accepted.

@rsc rsc changed the title proposal: cmd/go: add GOMODCACHE cmd/go: add GOMODCACHE Apr 8, 2020
@gopherbot
Copy link
Contributor

Change https://golang.org/cl/230537 mentions this issue: doc/go1.15: add notes for GOMODCACHE, modcacheunzipinplace

gopherbot pushed a commit that referenced this issue Apr 28, 2020
For #36568
For #34527

Change-Id: Ieea4b4a7644e9c957f48d08d2e172e39b571502f
Reviewed-on: https://go-review.googlesource.com/c/go/+/230537
Reviewed-by: Michael Matloob <matloob@golang.org>
Reviewed-by: Bryan C. Mills <bcmills@google.com>
xujianhai666 pushed a commit to xujianhai666/go-1 that referenced this issue May 21, 2020
For golang#36568
For golang#34527

Change-Id: Ieea4b4a7644e9c957f48d08d2e172e39b571502f
Reviewed-on: https://go-review.googlesource.com/c/go/+/230537
Reviewed-by: Michael Matloob <matloob@golang.org>
Reviewed-by: Bryan C. Mills <bcmills@google.com>
@gopherbot
Copy link
Contributor

Change https://golang.org/cl/241275 mentions this issue: cmd/go: include GOMODCACHE in 'go help environment'

gopherbot pushed a commit that referenced this issue Jul 13, 2020
Updates #34527
Fixes #40089

Change-Id: Ie9c8573536e5c31e874d755f4d888ffc805b796f
Reviewed-on: https://go-review.googlesource.com/c/go/+/241275
Run-TryBot: Jay Conrod <jayconrod@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Michael Matloob <matloob@golang.org>
smira added a commit to smira/kres that referenced this issue Aug 20, 2020
In Go 1.15 there's separate environment variable to control the cache
locaiton: golang/go#34527, but as we're on Go
1.14 yet, we use compatible approach with `GOPATH/pkg` being cached
across all Go-related build steps.

This speeds up `go mod download` a lot when only some module
dependencies got changed.

Signed-off-by: Andrey Smirnov <smirnov.andrey@gmail.com>
talos-bot pushed a commit to siderolabs/kres that referenced this issue Aug 20, 2020
In Go 1.15 there's separate environment variable to control the cache
locaiton: golang/go#34527, but as we're on Go
1.14 yet, we use compatible approach with `GOPATH/pkg` being cached
across all Go-related build steps.

This speeds up `go mod download` a lot when only some module
dependencies got changed.

Signed-off-by: Andrey Smirnov <smirnov.andrey@gmail.com>
@golang golang locked and limited conversation to collaborators Jul 7, 2021
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
FrozenDueToAge GoCommand cmd/go modules NeedsFix The path to resolution is known, but the work has not been done. Proposal Proposal-Accepted
Projects
None yet
Development

No branches or pull requests