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

Use google.golang.org/grpc/cmd/protoc-gen-go-grpc #98

Merged
merged 3 commits into from
Feb 21, 2021

Conversation

ash2k
Copy link
Contributor

@ash2k ash2k commented Jan 20, 2021

Closes #85.

Use google.golang.org/grpc/cmd/protoc-gen-go-grpc to generate gRPC code for Go.

Please let me know if I missed something.

WORKSPACE Show resolved Hide resolved
WORKSPACE Show resolved Hide resolved
@@ -55,6 +55,7 @@ var (
)

type routeGuideServer struct {
pb.UnimplementedRouteGuideServer
Copy link
Contributor Author

Choose a reason for hiding this comment

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

I think this is required now.

"@org_golang_google_grpc//:go_default_library",
"@org_golang_google_grpc//codes:go_default_library",
"@org_golang_google_grpc//status:go_default_library",
"@org_golang_x_net//context:go_default_library",
Copy link
Contributor Author

@ash2k ash2k Jan 20, 2021

Choose a reason for hiding this comment

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

This is no longer needed as gRPC now uses the standard library context package, which is the successor to this one.

importpath = kwargs.get("importpath"),
visibility = kwargs.get("visibility"),
tags = kwargs.get("tags"),
)

GRPC_DEPS = [
"@com_github_golang_protobuf//proto:go_default_library",
"@org_golang_google_protobuf//reflect/protoreflect:go_default_library",
"@org_golang_google_protobuf//runtime/protoimpl:go_default_library",
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Replaced with PROTO_DEPS. Is that ok?

Copy link
Member

Choose a reason for hiding this comment

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

IIRC GRPC_DEPS is intended to be the full set of deps for the *_grpc_ rules, but I'd have to check the consistency with other langs. If that's the case, having GRPC_DEPS = [...] + PROTO_DEPS would be suitable though.

go/repositories.bzl Outdated Show resolved Hide resolved
go/repositories.bzl Outdated Show resolved Hide resolved
@@ -20,14 +22,13 @@ load("@rules_proto_grpc//{{ .Lang.Dir }}:repositories.bzl", rules_proto_grpc_{{

rules_proto_grpc_{{ .Lang.Name }}_repos()`)


var goLibraryRuleTemplateString = `load("//{{ .Lang.Dir }}:{{ .Rule.Base}}_{{ .Rule.Kind }}_compile.bzl", "{{ .Rule.Base }}_{{ .Rule.Kind }}_compile")
var goLibraryRuleTemplateString = `load("//{{ .Lang.Dir }}:{{ .Rule.Base}}_proto_compile.bzl", "{{ .Rule.Base }}_proto_compile")
Copy link
Contributor Author

@ash2k ash2k Jan 20, 2021

Choose a reason for hiding this comment

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

Both rules now use go_proto_compile rule so they can share this prefix. gRPC rule needs some more loads, see below.

DisplayName: "grpc-gateway",
Flags: commonLangFlags,
Flags: commonLangFlags,
Copy link
Contributor Author

Choose a reason for hiding this comment

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

goimports formatting things, I guess...

@ash2k ash2k force-pushed the ash2k/grpc-go branch 9 times, most recently from acd57e2 to 5b4d952 Compare January 20, 2021 06:10
@ash2k
Copy link
Contributor Author

ash2k commented Jan 20, 2021

Hm, so I'm trying to use this branch to check that it works for my project and it does not. Here is the branch where I'm trying to switch https://gitlab.com/gitlab-org/cluster-integration/gitlab-agent/-/merge_requests/256

I'm getting

make regenerate-proto                                                
bazel run //build:extract_generated_proto
INFO: Analyzed target //build:extract_generated_proto (102 packages loaded, 779 targets configured).
INFO: Found 1 target...
ERROR: /Users/mikhail/src/gitlab-agent/internal/module/modshared/BUILD.bazel:4:18: output 'internal/module/modshared/proto/go_grpc_compile_aspect_verb0/internal/module/modshared/modshared_grpc.pb.go' was not created
ERROR: /Users/mikhail/src/gitlab-agent/internal/module/modshared/BUILD.bazel:4:18: not all outputs were created or valid

Some of my proto files with gRPC services (e.g. internal/module/agent_configuration/rpc/rpc.proto) depend on other proto files without gRPC serviers (e.g. internal/module/modshared/modshared above). But the gRPC aspect is applied to both and... the gRPC plugin does not generate anything for the file without services. I'm not sure how to resolve this. Also, I may be wrong and the problem is caused by something else.

p.s. I also have cases where non-grpc proto depends on non-grpc proto: internal/module/agent_tracker/agent_tracker.proto also depends on modshared.proto. This does work.

@ash2k
Copy link
Contributor Author

ash2k commented Jan 20, 2021

I've made some simplifications in the third commit and now both plugins should be invoked in a single protoc call but the problem is still the same. 😕

bazel build //internal/module/agent_tracker/rpc:grpc_compile
INFO: Analyzed target //internal/module/agent_tracker/rpc:grpc_compile (0 packages loaded, 0 targets configured).
INFO: Found 1 target...
ERROR: /Users/mikhail/src/gitlab-agent/internal/module/agent_tracker/BUILD.bazel:4:18: output 'internal/module/agent_tracker/proto/go_grpc_compile_aspect_verb0/internal/module/agent_tracker/agent_tracker_grpc.pb.go' was not created
ERROR: /Users/mikhail/src/gitlab-agent/internal/module/agent_tracker/BUILD.bazel:4:18: not all outputs were created or valid


gateway_swagger_compile(
gateway_openapiv2_compile(
Copy link
Member

Choose a reason for hiding this comment

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

This is going to be a backwards incompatible change. I was likely going to have to do 3.0.0 for updated deps anyway, so should be fine

@aaliddell
Copy link
Member

Hm, so I'm trying to use this branch to check that it works for my project and it does not. Here is the branch where I'm trying to switch https://gitlab.com/gitlab-org/cluster-integration/gitlab-agent/-/merge_requests/256

I'm getting

make regenerate-proto                                                
bazel run //build:extract_generated_proto
INFO: Analyzed target //build:extract_generated_proto (102 packages loaded, 779 targets configured).
INFO: Found 1 target...
ERROR: /Users/mikhail/src/gitlab-agent/internal/module/modshared/BUILD.bazel:4:18: output 'internal/module/modshared/proto/go_grpc_compile_aspect_verb0/internal/module/modshared/modshared_grpc.pb.go' was not created
ERROR: /Users/mikhail/src/gitlab-agent/internal/module/modshared/BUILD.bazel:4:18: not all outputs were created or valid

Some of my proto files with gRPC services (e.g. internal/module/agent_configuration/rpc/rpc.proto) depend on other proto files without gRPC serviers (e.g. internal/module/modshared/modshared above). But the gRPC aspect is applied to both and... the gRPC plugin does not generate anything for the file without services. I'm not sure how to resolve this. Also, I may be wrong and the problem is caused by something else.

p.s. I also have cases where non-grpc proto depends on non-grpc proto: internal/module/agent_tracker/agent_tracker.proto also depends on modshared.proto. This does work.

This is a frustrating recurring problem with some languages in this repo. In short, some protoc plugins behave deterministically w.r.t the the input files, such that they create an output file for every input file regardless of content. Unfortunately some plugins only output files that contain services, which makes their output non-predictable and therefore they don't play too well with Bazel, which needs to know output files prior to running a tool. I've had to 'fight' this a number of times to make plugins more deterministic.

For some languages, we can get around this by declaring the entire output directory and passing that to the underlying library rule. However, this requires that rules_<lang> is able to handle receiving a directory containing files, rather than the files themselves. I can't remember where rules_go falls with regard to supporting that.

What I presume we are seeing here is that the old go plugin had the deterministic behaviour, but the new plugin has the non-deterministic behaviour.

@ash2k
Copy link
Contributor Author

ash2k commented Feb 9, 2021

@aaliddell Thanks!

What I presume we are seeing here is that the old go plugin had the deterministic behaviour, but the new plugin has the non-deterministic behaviour.

I think the old plugin generates proto+grpc into a single file (at least that is my understanding) but the new one only generates gRPC code (and the proto generation is left to the old plugin, which generates proto API v2 code already).

However, I'm not sure that's the issue. I think the issue is that we had a single plugin which could handle both proto and gRPC generation, but now we have two. And because of that we need to run two plugins for the same file. However, when the file does not have gRPC services, the new plugin does not produce anything.

It seems to me that the solution to this would be to run the new gRPC generation plugin only on proto files which are known to have gRPC services. Currently it's run on the whole tree of dependencies, right? I.e. in my case services.proto depends on/imports no-services.proto and the error is because new plugin is ran on the no-services.proto, when it shouldn't.

To rephrase the above - my understanding (which might be wrong!) is that currently a set of plugins is invoked on all nodes of the dependency graph an aspect finds. This does not work because in this case not all plugins should be invoked on each node.

What if we have two aspects - one for go_proto_compile and another one for go_grpc_compile:

  • go_proto_compile would call the old plugin on the proto file and on all the dependencies
  • go_grpc_compile would call the old plugin on the proto file and on all the dependencies AND the new plugin too, but only on the the sources, passed to go_grpc_compile.

I have no idea if this is possible or makes sense, just an idea.

@aaliddell
Copy link
Member

Ok, gotcha.

However, when the file does not have gRPC services, the new plugin does not produce anything.

Yep, this is the issue of determinism I mentioned.

It seems to me that the solution to this would be to run the new gRPC generation plugin only on proto files which are known to have gRPC services. Currently it's run on the whole tree of dependencies, right?

Yep, the trouble is that we cannot determine where in the tree of deps a service might turn up. We certainly could be opinionated and make certain plugins non-traversing, such that we only the direct srcs get the gRPC plugins applied. I would like to explore that as an option if rules_go does not support directory inputs, since this'd be a reasonable solution for other languages in the same predicament.

I will retarget this PR onto the dev branch, so that I can merge your changes with the dependency updates I've done. Then we can experiment there with solving the gRPC issue 👍

This was referenced Feb 9, 2021
Name: "gateway_openapiv2_compile",
Kind: "grpc",
Implementation: aspectRuleTemplate,
Plugins: []string{"//github.com/grpc-ecosystem/grpc-gateway:openapiv2_plugin", "//go:go_plugin"},
Copy link
Member

Choose a reason for hiding this comment

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

Why was go_plugin added for the JSON generation?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I only vaguely remember - I think it was because the new openapiv2_plugin didn't generate the Go sources when used only by itself. It may be a wrong way to fix it, I'm not sure.

@ash2k
Copy link
Contributor Author

ash2k commented Feb 9, 2021

Yep, the trouble is that we cannot determine where in the tree of deps a service might turn up.

It might be good to add this as a testcase - currently the build is green for this PR even though it doesn't actually work for such a common (?) case.

@aaliddell
Copy link
Member

Yep, the trouble is that we cannot determine where in the tree of deps a service might turn up.

It might be good to add this as a testcase - currently the build is green for this PR even though it doesn't actually work for such a common (?) case.

This should have been caught by the routeguide tests, so I'm somewhat puzzled that passed 🤔
I just so happened to push a commit today (fd1ec1d) that adds another variant of testing this and has flagged up at least Objective-C as having a problem.

On further diving into the aspects, I'm remembering why we weren't able to use the opinionated approach of only using the gRPC plugin on direct deps: basically, the aspect has no concept of the 'depth' of a dependency tree, since a target could simultaneously be a direct dep and a transitive dep and the aspect will only run once for both cases. To resolve this, we'd have to add a different attribute to the grpc rules to disambiguate this, which would be a very breaking change.

@aaliddell
Copy link
Member

On second thought, the routeguide tests won't have caught this, as routeguide.proto is based on the gRPC repo's example and does not have an external dependency on any well-known protos etc. So the commit above is the first time that service tree failures would have been explicitly tested.

@aaliddell aaliddell merged commit 9cfd8ad into rules-proto-grpc:master Feb 21, 2021
@ash2k ash2k deleted the ash2k/grpc-go branch February 21, 2021 23:46
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.

Update protoc-gen-go-grpc
2 participants