-
Notifications
You must be signed in to change notification settings - Fork 1.6k
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
protoc-gen-go/grpc: Add null guard for gRPC connection #942
Conversation
\cc @dfawley |
It would possibly make more sense to test this during the 😕 It’s hard to say what the correct behavior here really should be because there is no way to actually recover or deal with the error. Either there was a fundamental mistake in the calling code (failing to test an |
@puellanivis I agree that usually it makes more sense to test it before calling the constructor. On the other hand, since If we think about how expensive is adding a null check for each RPC call, I would guess that it increases insignificant CPU time usage at all. However, we may think why would it be necessary? I would say that consistency is enough, altough a |
Ideally this would fail at client creation, but given the API, there is no way to fail there except with a panic. That said, as @puellanivis said, this situation can only happen as a result of programmer error. So it seems there are three options:
Of the three, possibly my least favorite is today's behavior. Panicking at creation is strictly better, as it moves the failure closer to the mistake. I'm fine with panics as a result of programmer error, but the main problem with changing to panic at creation is that we would be introducing a new panic. If the client is unused, we'd be making existing code that was safe (albeit strange) panic, e.g.: cc, err := grpc.Dial(...)
foo := NewFooClient(cc)
if err != nil {
return err
} So, I don't want to change the code to panic at creation. That leaves erroring at use. This option, I feel like, is just papering over programmer error. @yagotome, this use case is exactly how gRPC clients are supposed to work (but not how grpc-go works, unfortunately). The initial state of the client is IDLE until the first RPC is attempted, at which point the client starts connecting. Updating Go to this behavior is tracked here: grpc/grpc-go#1786. However, I don't see how checking for nil ahead of time vs. for a special error after helps save any code or complexity. The fact that this expands the API of the stub is concerning. Also, the error code used, IMO we should leave things as they are, and work toward supporting idleness in grpc-go instead. |
My concern about checking it at every function call is less concerned with performance. A smart compiler should be able to elide the code where the condition is never possible. And actually, I’m pretty sure, the compiler conditionally generates code to test for My concern is centrally that it should be a programming fault for such an error path to ever even be possible. Safety guards can be good, but runtime panics can sometimes be the correct choice. 🤷♀️ P.S.: Yeah, the documentation says |
@dfawley alright! I agree that introducing a new
I wasn't aware of that open issue. That's exactly what I would like to have 🙂
The scenario is that if I tried to connect before each RPC, I would need to check for
My fault! If we go ahead with this PR, I will replace with a proper error.
Totally agree! Let's focus on supporting idleness in grpc-go! That said, what about v1/v2's question? Should that |
@dfawley may correct me here, but I'm not aware of a v2 effort for gRPC. The discussion about v2 on this repository is specifically about the Go protobuf implementation, which does not cover the grpc generator. The fact that the grpc generator is part of this repository is more for historical reasons and we intend for it to eventually be part of the grpc-go repository in the future. |
Generated code for gRPC Service Client does an RPC call by deferencing a connection pointer (
grpc.ClientConn
) without checking whether it is nil.In this PR, an error is returned if that connection pointer is nil, so that no nil pointer dereference is thrown (panic) due to nil connection.