-
Notifications
You must be signed in to change notification settings - Fork 1.6k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
net/proto2: remove <message>.ExtensionMap() from generated messages
Turn generated message struct field XXX_Extensions map[int32]Extension into an embedded proto.InternalExtensions struct InternalExtensions is a struct without any exported fields and methods. This effectively makes the representation of the extension map private. The proto package can access InternalExtensions by checking that the generated struct has the method 'extmap() proto.InternalExtensions'. Also lock accesses to the extension map. This change bumps the Go protobuf generated code version number. Any .pb.go files generated with this version of the proto package or later will require this version or later of the proto package to compile.
- Loading branch information
Showing
15 changed files
with
274 additions
and
82 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
e51d002
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
With this change, there does not appear to be enough plumbing to generically/reflectively iterate through extensions:
HasExtension
works fine if the only populated field in theExtensionDesc
passed in isField
, but you cannot iterate through extensions. AndGetExtension
requires a fully (and correctly) populatedExtensionDesc
; you could pass dummy data, but it would blow up the first time someone called it correctly.GetExtensions
too requires a slice of populatedExtensionDesc
objects.We have a couple of small forks to
protoc-gen-go
that look for boolean field option extensions. We were simply iterating throughfield.GetOptions().ExtensionMap()
, looking for matching extension number, and hand-decoding the boolean value. We actually change the generated code: for example, our "redacted" extension causes the given field to be redacted from the default string representation, so we don't accidentally log PII.Would it be possible to add a method that returns a representation of extensions that can be programmatically manipulated/queried without access to the proper extension protos?
e51d002
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let me make sure I understand: You want to be able to iterate through all extensions, including those whose generated proto code are not linked into the binary, right? And that's why you can't iterate through
proto.RegisteredExtensions(pb)
, right?e51d002
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, more or less. I've actually solved the first of my problems caused by this change by just creating fake
ExtensionDesc
objects - since they're only used in our protoc plugin, nothing else is going to use them.The other uses I'm trying to fix now are (a) code that does serialization, which obviously needs to iterate over all present extensions, registered or not, and (b) logging code that iterates through present extensions rather than all registered extensions for efficiency: we'd wind up calling HasExtension 100 times when only zero or one are probably present.
e51d002
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@matloob @dsymonds A quick question: are we doing something wrong? Are we using parts of the exported protobuf API that we shouldn't be using? Changes like this are incredibly and repeatedly painful for us, but now that we can't update GRPC without also updating protobuf, we're forced to deal with protobuf API churn :-(
Is there a way to stay abreast of or participate in the discussion that happens before changes like this?
e51d002
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The change we made did remove the ability to examine extensions that are not registered. To fix that, we'll add a function to the proto library that looks like this:
Would that solve uses (a) and (b)?
To address your second question: I wouldn't say that you're doing something wrong. It looks to more like an engineering tradeoff: the flexibility of custom generated and serialization code can come with headaches and pain when the protobuf upstream library changes.
It was our mistake to drop support for extracting unregistered extensions, and we'll try to get a fix in soon. On the other hand, we don't and can't support custom serialization code or changes in the generated code. The protobuf library and the generated code it produces are built to work together. The protobuf library supports the code it generates, and (for a reasonable transition period) code generated by earlier versions of the library. It would be too much of a strain on on us to make changes in the library or generated format that would be guaranteed not to break any fork or other unanticipated use of the protobuf library.
e51d002
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmmmm. It depends on what's actually inside that
[]interface{}
. If it's just the extension value, which might be (say)bool
, then that would give no way to get the extension number, so you wouldn't know which boolean extension you were looking at.Also, what is "es" ("which are also listed in es"), and how could you get an error?
e51d002
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I have a branch (and can send a PR) that adds
proto.ExtensionIDs(pb Message) []int32
, which would allow less wasteful iteration…e51d002
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah
ExtensionIDs
is a better solution. An alternative, what do you think of something that returns a[]*proto.ExtensionDesc
, with the actualExtensionDesc
present if the extension is registered, or a dummyExtensionDesc{Field: extensionId}
otherwise?e51d002
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Something like this:
e51d002
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
lgtm… although we'll only use
Field
:-)e51d002
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For completeness, it should probably pass back out any
ExtensionDesc
objects that were cached byGetExtension
too…e51d002
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ideally, I still feel like there should be a way to programmatically work with unknown (and unregistered) extensions… which you can't do right now, since you'll never guess an appropriate
ExtensionDesc
and you can't access the raw bytes. But when I look at our code that was doing that, it was using reflection to access the lowercaseenc
field… at least this change prompted me to get rid of that bit of ugliness! :-)e51d002
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hm... you're probably right, but I'm wary of exposing too much of a proto's internals, so we need to make sure it's the right API.
Could you give me an example use case where you'd want to work with the extension, but you don't know what type it is? I'll try to think about how we can suupport it.
e51d002
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I wouldn't worry about it now.
But for instance if one wanted to properly serialize protos oneself, it would be useful…
e51d002
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
One last question: do you expect
GetAllExtensionDescs
to land soon, or should I cobble together a workaround for now?e51d002
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It should get in over the next few days
e51d002
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Any news on this? I can put together a pull request if that would help…
e51d002
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Whoops, it's in now!
e51d002
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks!