-
Notifications
You must be signed in to change notification settings - Fork 4.5k
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
Reflection library is tightly coupled with golang/protobuf #1873
Comments
This proposal SGTM. Assuming this interface will be defined with Some other thoughts: This would also require the underlying message interchange format to support similar features as protobuf's file descriptor. |
@menghanl Thanks for your reply. The nested nature of the proto.Message definition is a problem, as gogo/protobuf has managed to avoid importing anything defined in golang/protobuf thus far. This could still be solved with a client compatibility layer wrapping a Inviting @awalterschulze (maintainer of gogo/protobuf) to chime in. |
Totally, gogo/protobuf/proto would like to avoid importing golang/protobuf/proto, because currently gogo/protobuf has zero dependencies and would like to keep it that way. A few years ago, when I was a user, I used to take a git subtree of grpc-go and change all the import paths from github.com/golang/protobuf/proto to github.com/gogo/protobuf/proto. So maybe I would ask, is it possible to create an interface that avoids importing any serialization library, including flatbuffers? For two reasons:
|
@awalterschulze, I'm porting github.com/jhump/protoreflect to gogo and have an experimental branch with a working protoer interface along the lines of what @johanbrandhorst suggested with zero dependencies: https://github.com/prasek/protoreflect/blob/protoer/proto/protoer.go The gogo and golang wrappers implement UntypedProtoer type Protoer interface {
MessageType(name string) reflect.Type
MessageName(pb Message) string
FileDescriptor(file string) []byte
Unmarshal(b []byte, pb Message) error
Marshal(pb Message) ([]byte, error)
GetExtension(pb Message, field int32) (extval interface{}, err error)
EnsureNativeMessage(pb Message) (pbout Message, err error)
}
type Message interface {
Reset()
String() string
ProtoMessage()
}
type UntypedProtoer interface {
MessageType(name string) reflect.Type
MessageName(pb interface{}) string
FileDescriptor(file string) []byte
Unmarshal(b []byte, pb interface{}) error
Marshal(pb interface{}) ([]byte, error)
GetExtension(pb interface{}, field int32) (extval interface{}, err error)
EnsureNativeMessage(pb interface{}) (pbout interface{}, err error)
} |
Cool :) May I ask what is the use case for EnsureNativeMessage? |
@awalterschulze, EnsureNativeMessage should really be called NativeDescriptor. There are two use cases for NativeDescriptor, one internal to the wrappers to ensure GetExtension() and RegisteredExtensions() get types they recognize, and the other allows libraries to accept either gogo or golang descriptor types by converting them with NativeDescriptor of a separate protoer instance that matches the package used by the library. For example https://github.com/prasek/protoreflect/blob/protoer/desc/builder.go#L19 (experimental branch) accepts gogo and golang descriptors for some methods but internally has to pick one to use. NativeDescriptor() checks the descriptor type (gogo or golang) and converts it if not the desired type so it will work with descriptor types (gogo or golang) used by the library. The downside to this approach is descriptors are exported as Message and have to be type asserted by the caller, so I'd be fine removing NativeDescriptor from the Protoer interface and just using it internally in the wrappers. |
@johanbrandhorst, ideally the packages would get split into grpc, golang/protobuf, and gogo/protobuf respectively. There are no dependencies across the packages.
The only difference between the gogo and golang protobuf protoer are the imports and default aliases. |
Here are the updated interfaces with RegisteredExtensions added and NativeDescriptor removed. type Protoer interface {
MessageType(name string) reflect.Type
MessageName(pb Message) string
FileDescriptor(file string) []byte
Unmarshal(b []byte, pb Message) error
Marshal(pb Message) ([]byte, error)
RegisteredExtensions(pb Message, desiredType interface{}) (extensions interface{}, err error)
GetExtension(pb Message, field int32) (extval interface{}, err error)
}
type Message interface {
Reset()
String() string
ProtoMessage()
}
type UntypedProtoer interface {
MessageType(name string) reflect.Type
MessageName(pb interface{}) string
FileDescriptor(file string) []byte
Unmarshal(b []byte, pb interface{}) error
Marshal(pb interface{}) ([]byte, error)
RegisteredExtensions(pb interface{}, desiredType interface{}) (interface{}, error)
GetExtension(pb interface{}, field int32) (extval interface{}, err error)
} Note for RegisteredExtensions() the desiredType is needed to check and convert if needed. extensions, err := proto.RegisteredExtensions(pb, (map[int32]*gogo.ExtensionDesc)(nil)) |
A protobuf reflection API that has Unmarshal and Marshal in its interface is probably too high level. Just as Go reflection is abstraction around the Go language itself, protobuf reflection should be an abstraction around the protobuf language itself. As such, it needs to go even lower-level than Unmarshal and Marshal and provide behavior at the field level in order to be reflexive over all aspect of the proto language. Adding reflection functionality into the mainline golang/protobuf repo has been a project I've been working on for some time now and turns out to be a difficult task as there are many devils in the details. A reflection API needs to thought in the context of the totality of how Go protobufs operates. As I've been working on a reflection API, I've had to scrap my design and start over many times. I recently finished writing up the design and published some documents regarding the design: golang/protobuf#364 (comment) |
I realize this is an old-ish issue, but wanted to check if the root cause of an issue I observed is the result of this coupling. I implemented the Envoy v2 external auth service (proto file and generated Go using gogo/protobuf). When using grpcurl or grpc_cli, I can list the services, e.g.
However when I attempt to describe/call the service, I get a "not found" result:
|
I think you'd get an error about trying to lookup a file if it was a gogoproto problem, but it could well be, I haven't tried using reflection with gogoproto recently. I suppose it'd be worthwhile trying to generate with golang/protobuf and see if that works? |
I tried the gogo plugins with a simpler service to build (the Envoy one has quite a few dependencies). It appears that with the |
That result is consistent with a gogoproto registry issue - |
The "registry issue" being the gogo types are not registered with the standard proto registry that the reflection service relies on? (sorry I am fairly new to gogo and getting up to speed on the incompatibilities.) |
That's right - I wrote a blog post on the topic of gogoprotobuf compatibility that should cover it: https://jbrandhorst.com/post/gogoproto/. |
Ah yes. I read this a while ago (thank you for writing it), but never dove into gogo after that until now. I see the relevant section:
|
This issue is labeled as requiring an update from the reporter, and no update has been received after 7 days. If no update is provided in the next 7 days, this issue will be automatically closed. |
Still a problem |
Closing due to lack of activity and priority. If someone feels strongly about this, please write a concrete proposal & commit to implementing a solution, and we can do reviews. |
What version of gRPC are you using?
gRPC 1.10
What version of Go are you using (
go version
)?go version go1.10 linux/amd64
What operating system (Linux, Windows, …) and version?
$ uname -a Linux johan-x1 4.15.3-2-ARCH #1 SMP PREEMPT Thu Feb 15 00:13:49 UTC 2018 x86_64 GNU/Linux
What did you do?
I tried using
grpc/reflection
library withgogo/protobuf
.What did you expect to see?
I expected to be able to use an alternative to
golang/protobuf
with gRPC reflection.What did you see instead?
I found that the gRPC reflection library imports
github.com/golang/protobuf
. I also found it is tightly coupled to this implementation. As I understand, gRPC is meant to be codec agnostic, what with official support for flatbuffers, and the nice Codec interface existing in grpc-go. Given this, it is surprising that it is so tighly coupled togolang/protobuf
, and perhaps by extension so coupled to protobuf itself.I propose some sort of interface is defined in the reflection library so that a user can choose which backend to make use of for resolving protobuf types. As I see it, the reflection library makes use of the following functions (illustrated as an interface) from golang/protobuf:
Ideally, I'd like to be able to use gogo/protobuf with gRPC reflection, but the current implementation would force me to reimplement the reflection server in gogo/protobuf manually.
Any thoughts on this greatly appreciated.
The text was updated successfully, but these errors were encountered: