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

Why all the new dependencies? #707

Open
markbates opened this issue May 25, 2019 · 28 comments
Open

Why all the new dependencies? #707

markbates opened this issue May 25, 2019 · 28 comments
Labels
kind/question A question related to the project
Milestone

Comments

@markbates
Copy link

markbates commented May 25, 2019

module github.com/spf13/viper

require (
	github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6 // indirect
	github.com/coreos/bbolt v1.3.2 // indirect
	github.com/coreos/etcd v3.3.10+incompatible // indirect
	github.com/coreos/go-semver v0.2.0 // indirect
	github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e // indirect
	github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f // indirect
	github.com/dgrijalva/jwt-go v3.2.0+incompatible // indirect
	github.com/fsnotify/fsnotify v1.4.7
	github.com/gogo/protobuf v1.2.1 // indirect
	github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef // indirect
	github.com/google/btree v1.0.0 // indirect
	github.com/gorilla/websocket v1.4.0 // indirect
	github.com/grpc-ecosystem/go-grpc-middleware v1.0.0 // indirect
	github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 // indirect
	github.com/grpc-ecosystem/grpc-gateway v1.9.0 // indirect
	github.com/hashicorp/hcl v1.0.0
	github.com/jonboulle/clockwork v0.1.0 // indirect
	github.com/magiconair/properties v1.8.0
	github.com/mitchellh/mapstructure v1.1.2
	github.com/pelletier/go-toml v1.2.0
	github.com/prometheus/client_golang v0.9.3 // indirect
	github.com/soheilhy/cmux v0.1.4 // indirect
	github.com/spf13/afero v1.1.2
	github.com/spf13/cast v1.3.0
	github.com/spf13/jwalterweatherman v1.0.0
	github.com/spf13/pflag v1.0.3
	github.com/stretchr/testify v1.2.2
	github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5 // indirect
	github.com/ugorji/go v1.1.4 // indirect
	github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2 // indirect
	github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77
	go.etcd.io/bbolt v1.3.2 // indirect
	go.uber.org/atomic v1.4.0 // indirect
	go.uber.org/multierr v1.1.0 // indirect
	go.uber.org/zap v1.10.0 // indirect
	golang.org/x/net v0.0.0-20190522155817-f3200d17e092 // indirect
	golang.org/x/time v0.0.0-20190308202827-9d24e82272b4 // indirect
	google.golang.org/grpc v1.21.0 // indirect
	gopkg.in/yaml.v2 v2.2.2
)

Why do I need GRPC for configuration?

The commit that did this, b5bf975, added 24 dependencies, while only removing 5 to the go.mod and a rather large 148 additions to 5 deletions in the go.sum

I believe it is the "remote" feature that is causing all of these imports, but I'm not sure. Perhaps that feature should be behind a build tag? Or perhaps it's introduction should be considered a breaking change?

cc @spf13

@anthonyfok
Copy link
Collaborator

Hi @markbates, I wondered about the same question too. The added dependencies turns out to be a direct result of merely running go mod tidy.

Go Wiki provides an answer to "Why does 'go mod tidy' record indirect and test dependencies in my 'go.mod'?" at https://github.com/golang/go/wiki/Modules#why-does-go-mod-tidy-record-indirect-and-test-dependencies-in-my-gomod

And here is an example command showing why GRPC is added:

$ go mod why -m google.golang.org/grpc
# google.golang.org/grpc
github.com/spf13/viper/remote
github.com/xordataexchange/crypt/config
github.com/xordataexchange/crypt/backend/etcd
github.com/coreos/etcd/client
github.com/coreos/etcd/client.test
github.com/coreos/etcd/integration
google.golang.org/grpc

Granted, being a relatively Go perpetual newbie myself, I am still trying to wrap my head around it, but looks like it is what the Go developers would call #WAI, working as intended.

@anthonyfok anthonyfok added the kind/question A question related to the project label May 26, 2019
@spf13
Copy link
Owner

spf13 commented Jun 7, 2019

I'm looking into this. I'm also not quite clear why this is a problem. Viper + remote does depend on all of these but I believe that if you are using a mirror, it won't download or build them unless you are using the remote functionality.

@markbates
Copy link
Author

It definitely needs all these new deps, but my question is more abstract in that I question whether this featured which brings all these deps is one that the majority of people will use or not. I know I don’t need the feature and now I have a bunch of crazy deps. :(

I’m considering moving all my projects off of Cobra to cut out all of these extra deps.

@spf13
Copy link
Owner

spf13 commented Jun 7, 2019

I'm confused about what you mean by "deps" or really, why you are concerned. Right now they are just lines in a config file. They aren't dependencies until you import them. Viper doesn't import them by default, it requires you to add an additional import. What additional cost are these lines having on you? (I'm not being sarcastic, I'm genuinely trying to understand better).

@markbates
Copy link
Author

I’m not using that feature but all of these new dependencies are part of my app now.

For example none of my apps use grpc, but do use cobra and now I have all these dependencies in all of my applications.

Go modules downloads all of these deps even though I’m not using them. That’s my concern.

@markbates
Copy link
Author

markbates commented Jun 7, 2019

@spf13 to give you an idea:

go get -u github.com/spf13/cobra
go: finding golang.org/x/crypto latest
go: finding github.com/tmc/grpc-websocket-proxy latest
go: finding golang.org/x/time latest
go: finding golang.org/x/net latest
go: finding golang.org/x/sys latest
go: finding github.com/xiang90/probing latest
go: finding golang.org/x/tools latest
go: finding gopkg.in/check.v1 latest
go: finding github.com/golang/groupcache latest
go: finding github.com/golang/glog latest
go: finding github.com/armon/consul-api latest
go: finding github.com/coreos/go-systemd latest
go: finding github.com/coreos/pkg latest
go: finding github.com/prometheus/client_model latest
go: finding github.com/prometheus/client_golang v0.9.4
go: finding github.com/coreos/etcd v3.3.13+incompatible
go: finding google.golang.org/genproto latest
go: finding github.com/prometheus/common v0.4.1
go: finding golang.org/x/sync latest
go: finding github.com/mwitkow/go-conntrack latest
go: finding github.com/alecthomas/units latest
go: finding google.golang.org/appengine v1.6.1
go: finding google.golang.org/grpc v1.21.1
go: finding github.com/prometheus/tsdb v0.8.0
go: finding github.com/kr/logfmt latest
go: finding golang.org/x/lint latest
go: finding github.com/dgryski/go-sip13 latest
go: finding golang.org/x/sys v0.0.0-20190606165138-5da285871e9c
go: finding golang.org/x/oauth2 latest
go: finding honnef.co/go/tools latest
go: finding golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b
go: finding github.com/prometheus/procfs v0.0.2
go: finding github.com/spaolacci/murmur3 v1.1.0
go: finding golang.org/x/net v0.0.0-20190603091049-60506f45cf65
go: finding github.com/alecthomas/template latest
go: finding github.com/json-iterator/go v1.1.6
go: finding github.com/OneOfOne/xxhash v1.2.5
go: finding github.com/modern-go/concurrent latest
go: finding github.com/google/renameio v0.1.0
go: finding golang.org/x/exp latest
go: finding github.com/google/pprof latest
go: finding golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e
go: finding google.golang.org/api v0.6.0
go: finding github.com/jstemmer/go-junit-report latest
go: finding go.opencensus.io v0.22.0
go: finding golang.org/x/tools v0.0.0-20190530171427-2b03ca6e44eb
go: finding golang.org/x/image latest
go: finding golang.org/x/mod v0.1.0
go: finding github.com/BurntSushi/xgb latest
go: finding golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529
go: finding golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd
go: finding golang.org/x/mobile latest
go: finding google.golang.org/grpc v1.20.1
go: finding google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb
go: finding golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c
go: finding golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b
go: finding google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873
go: finding cloud.google.com/go v0.38.0
go: finding golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c
go: finding google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7
go: finding google.golang.org/api v0.4.0

# I gave up because I'm on hotel wifi

Notice things like this:

go: finding github.com/armon/consul-api latest
go: finding github.com/coreos/go-systemd latest
go: finding github.com/coreos/pkg latest
go: finding github.com/prometheus/client_model latest
go: finding github.com/prometheus/client_golang v0.9.4
go: finding github.com/coreos/etcd v3.3.13+incompatible
go: finding google.golang.org/genproto latest
go: finding github.com/prometheus/common v0.4.1

It's pulling down all of those packages to my machine that I'm not actually using. On my hotel wifi it means I'm basically stuck from working because of the downloading modules needs to do.

Since nothing in my app uses consul, prometheus, grpc, coreos, etc... they're all still being downloaded. :(

@spf13
Copy link
Owner

spf13 commented Jun 7, 2019

Ok. I understand now. I wouldn't say they are part of your app (as you aren't really importing them), but they are definitely part of your download right now and that's obviously a problem.

However, once the Go module mirror is in place, which should be soon, it won't be. The mirror will permit you to only download the modules you use. Without the mirror there's no mechanism to get just the go.mod files from the dependencies so it downloads the entire repo.

For what it's worth, these dependencies were added several years ago, but moved into another package to require an additional import to avoid exactly this problem. Unfortunately we're in this odd transition time between modules and the mirror being live so this problem appeared again.

I'm not sure what the right short term solution is TBH. I'm open to suggestions though.

@rsc
Copy link

rsc commented Jun 11, 2019

Right, the dependencies existed already. The go mod tidy commit just made them more visible. Because dependencies were invisible pre-modules, many popular packages in the Go ecosystem carry far more dependencies than you might expect. Viper is one example; there are more. The right long-term fix is to think more carefully about which dependencies make sense and which don't.

We do have some fixes on the go command side on the way too, though.

The go get -u github.com/spf13/cobra in @markbates's transcript is resolving not just the latest cobra but the latest of every module listed in cobra's go.mod, and their go.mod files, recursively. For Go 1.13, we have cut this back to be more like the old GOPATH mode: instead of working a module granularity it will work at package granularity, so that go get -u github.com/spf13/cobra will get the latest cobra, and then the latest modules for the packages github.com/spf13/cobra actually imports, and so on, recursively. If cobra/remote is what pulls in tons of deps and your program does not import cobra/remote even indirectly, then go get -u will not go looking for the latest copies of those anymore (again, in Go 1.13). Go 1.14 may improve things further.

(I am not sure about the difference between viper and cobra here, but cobra is what the transcript said even though we are in the viper issue tracker.)

@spf13
Copy link
Owner

spf13 commented Jun 11, 2019 via email

@thepudds
Copy link

thepudds commented Jun 12, 2019

An almost identical question on viper happen to come up today in another forum, so adding some quick comments here as well.

One thing that might help is to at least initially not use the -u for something like go get -u github.com/spf13/cobra and instead do something like go get github.com/spf13/cobra@latest. That still gets the latest version of cobra, but no -u means it does not have to also hunt to find the most recent versions of all of the large number of direct and indirect dependencies of cobra. That can help the go command do less work, especially if on something like a development laptop that has built up a module cache in GOPATH/pkg/mod. If desired, the -u can then be used later at a more convenient time if there is a desire to upgrade to the most recent versions of all the direct and indirect dependencies of cobra.

Also, a quick test with a simple viper client does show things like etcd and prometheus in the module-level graph, but things like etcd and prometheus do not appear to be package-level dependencies in the actual build. One way to see that (from the modules wiki):

go list can show the exact versions used in your build excluding test-only dependencies:

go list -deps -f '{{with .Module}}{{.Path}} {{.Version}}{{end}}' ./... | sort -u

Finally, in advance of Go 1.13, there is a healthy chance export GOPROXY=https://proxy.golang.org would speed things up for this example in Go 1.12. (proxy.golang.org is beta status. Alternatively, one can try export GOPROXY=https://gocenter.io, which has been around longer). One approach is you can turn GOPROXY on temporarily when getting the public modules you depend on that have the largest module graphs. It is not uncommon for there to be something like a 5–10x improvement in go get download time and total bytes downloaded if you have a project with a large module graph (including due to the ability to get remote go.mod files via a small HTTPS GET rather than a repo fetch).

@sagikazarmark
Copy link
Collaborator

sagikazarmark commented Jun 12, 2019

That's something I would love for the long term, but that's a breaking change for the existing code.

Update: Looks like the comment I was responding to had been deleted. I meant separate packages would be a nice solution for remote implementations, but would also be a breaking change.

@tep
Copy link

tep commented Jun 15, 2019

Something to consider might be making github.com/spf13/viper/remote a separate module (with its own go.mod) -- but still in the same repo. This would also require an additional git tag (e.g. remote/v1.4.1) so it's sort of a PITA to maintain, but it would remove all of the etcd and/or consul dependencies from viper proper and only pull them in if the user is actually leveraging remote configs.

I've done something similar with toolman.org/net/peercred and its subordinate package toolman.org/net/peercred/grpcpeer so that gRPC's giant dependency tree is only included when it's actually being used.

Whether this is a recommended or idiomatic solution I can't say, but at least it works.

@knadh
Copy link

knadh commented Jun 20, 2019

Although tightly coupled dependencies were probably always the case, only recently did we start digging deep into viper after #635. These issues have unfortunately pushed us to remove viper from a couple dozen of our projects and reinvent the wheel (github.com/knadh/koanf).

The following example, although not a 1:1 comparison, is illustrative of the effects of hardcoded dependencies (go1.12 linux/amd64).

This produces a 2.4 MB binary.

package main

import (
	"fmt"
	"encoding/json"
)

func main() {
	// Dummy, just to bring in a parser.
	var o map[string]interface{}
	json.Unmarshal([]byte(`{"hello": "world}`), &o)
	fmt.Println(o)
}

Here, simply including viper, irrespective of the features used, produces a 12 MB binary.

package main

import (
	"fmt"

	"github.com/spf13/viper"
)

func main() {
	fmt.Println(viper.GetString("hello world"))
}

I agree with @sagikazarmark . The only probable solution is to split dependencies into separate packages, but like he said, it would be a breaking change.

@spf13
Copy link
Owner

spf13 commented Jun 20, 2019 via email

@zarraozaga
Copy link

Hi, I need some assistance but is remote config reader library xordataexchange/crypt deprecated?

@sagikazarmark
Copy link
Collaborator

etcd 3.5 will improve this significantly: client packages will be released as separate modules allowing to import only what's necessary for client functions. See etcd-io/etcd#12330 and etcd-io/etcd#12498

Sadly, there is no ETA for the release.

I prepared a draft PR for the crypt library: bketelsen/crypt#12

As you can see there is a significant change in dependencies.

@andig
Copy link
Contributor

andig commented Mar 15, 2021

@sagikazarmark it seems that etcd is sort of not-happening anytime soonish.
In #867 I had made a suggestion that split remote config from local config by simply moving remote config to a separate module. This breaks compatibility but seems a low-hanging fruit since it doesn't require extensive changes and is easy to adapt (I may have broken it though).
Would you consider that suggestion as a short-term intermediate step (say v2) before doing larger refactoring in a longer-term v3?

Update I've just recompiled my application with viper 1.7.1 instead of my local fork using go 1.16 and can no longer find a significant difference in binary size. I'm not sure if that was expected but it sort of removes the need for action.

@sagikazarmark
Copy link
Collaborator

etcd 3.5 will be released around June. Until then, here is the set of changes that we will get the new version: #1115

@sagikazarmark
Copy link
Collaborator

#1154 should improve the situation considerably.

@rhim
Copy link

rhim commented Mar 4, 2022

Hi Folks,

I have a related question. I see that even though I am pulling latest viper (1.10.1), and my code is only relying on that, it ends up referencing two versions of gogo/protobuf from go.sum

My go.mod
require github.com/spf13/viper v1.10.1

My go.sum (only showing gogo/protobuf part, but the list is huge!)

118 github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
119 github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=

And I can't figure out why. From the code, I see only pkg/mod/github.com/gogo/protobuf@v1.3.2/.

Where's that extra ref for v1.1.1 coming from?

I see same for a lot of other packages:

...
cloud.google.com/go v0.90.0/go.mod h1:kRX0mNRHe0e2rC6oNakvwQqzyDmg57xJ+SZU1eT2aDQ=
cloud.google.com/go v0.93.3/go.mod h1:8utlLll2EF5XMAV15woO4lSbWQlk8rer9aLOfLh7+YI=
cloud.google.com/go v0.94.1/go.mod h1:qAlAugsXlC+JWO+Bke5vCtc9ONxjQT3drlTTnAplMW4=
cloud.google.com/go v0.97.0/go.mod h1:GF7l59pYBVlXQIBLx3a761cZ41F9bBH3JUlihCt2Udc=
cloud.google.com/go v0.99.0/go.mod h1:w0Xx2nLzqWJPuozYQX+hFfCSI8WioryfRDzkoI/Y2ZA=

@sagikazarmark @spf13

@spf13
Copy link
Owner

spf13 commented Mar 5, 2022 via email

@rhim
Copy link

rhim commented Mar 5, 2022 via email

@rhim
Copy link

rhim commented Mar 18, 2022

So I have tracked it down to github.com/spf13/viper@v1.10.1->github.com/sagikazarmark/crypt@v0.4.0->github.com/armon/go-metrics@v0.3.10->github.com/prometheus/client_golang@v1.4.0->github.com/prometheus/common@v0.9.1->github.com/prometheus/client_golang@v1.0.0->github.com/prometheus/common@v0.4.1->github.com/gogo/protobuf@v1.1.1

Command that helped track it down go mod graph

So the real culprit is prometheus. This problem exists even in their latest version 1.12.1.

@skitt
Copy link
Contributor

skitt commented May 30, 2024

@sagikazarmark it is possible to split out remote into a separate sub-module without causing undue friction on downstreams:

  1. Add an empty go.mod in remote (so that the package is no longer part of the github.com/spf13/viper module).
  2. Run go mod tidy to clean up the root go.mod.
  3. Commit the changes, tag a release (v1.19.0 for example) and push the tag to publish it.
  4. In remote, add a dependency on github.com/spf13/viper v1.19.0, and run go mod tidy to populate go.mod and go.sum.
  5. Commit the changes, tag a sub-module release (remote/v1.19.0).

When downstreams try to upgrade to the new release, those using remote will get a new dependency on the sub-module through go mod tidy (which will figure out that the package has moved).

See https://github.com/skitt/viper-demo for verification. This does constitute a breaking change, technically, but go mod tidy can figure it out so it doesn’t seem all that bad to me. See also https://go.dev/wiki/Modules#is-it-possible-to-add-a-module-to-a-multi-module-repository

@sagikazarmark
Copy link
Collaborator

@skitt Thanks for the suggestion. I thought about that before, but honestly, I thought v2 would move along much faster. I had some good and some bad experiences with doing this in the past (admittedly, Go modules became much more mature since then).

I wrote up a proposal based on your comment: #1845

I leave it open for a while to see how others react. But overall, I think it's a solid plan.

@sagikazarmark
Copy link
Collaborator

sagikazarmark commented Jun 6, 2024

I tagged an alpha version of the upcoming release that contains a change that removes most of the third-party dependencies: https://github.com/spf13/viper/releases/tag/v1.20.0-alpha.1

Please give it a try and report back any issues! Thanks! ♥️

Also: thanks @skitt for the suggestion!

@skitt
Copy link
Contributor

skitt commented Jun 6, 2024

Fantastic, this seems to be working fine at least in projects without a remote dependency: skitt/kustomize#1 (look at the reduction in go.sum — not a concern for most projects but some like Kubernetes end up caring about the module dependency tree).

Thanks for taking the plunge!

@sagikazarmark
Copy link
Collaborator

As of v1.20.0-alpha.3 three additional dependencies are dropped from Viper: HCL, Java properties, INI.

Although we could externalize other dependencies (YAML, TOML), I'm satisfied with the current state for v1, so I'm going to close this issue once 1.20.0 is tagged.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
kind/question A question related to the project
Projects
None yet
Development

Successfully merging a pull request may close this issue.