-
Notifications
You must be signed in to change notification settings - Fork 3.9k
Add support for anonymous in-process servers. #8589
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
Conversation
} | ||
} | ||
|
||
@Override | ||
public SocketAddress getListenSocketAddress() { | ||
return new InProcessSocketAddress(name); | ||
return new InProcessSocketAddress(name, anonymous ? this : null); |
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.
This choice is notable. The address instance will only reference the server if it's anonymous.
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.
Yes, I think that is the correct behavior.
At first I was like, "oh, definitely," then I realized "but that might produce weird behavior/semantics" because depending on how the address was created you'd get different behavior. But I actually think it works well. Essentially it is the same as the anonymous server having a randomly-generated ID that is never reused.
ee09e13
to
8cd4bc5
Compare
Anonymous servers aren't registered statically, meaning they can't be referenced by name. Only the InProcessSocketAddress, fetched via Server.getListenSockets() can be used to connect to the server. This is particularly useful for production Android usage of in-process servers, where process startup latency is crucial, since a custom name resolver can be used to create the server instance on demand without directly impacting the startup latency of in-process gRPC clients. This approach supports a more-standard approach to "OnDeviceServer" referenced in gRFC L73. https://github.com/grpc/proposal/blob/master/L73-java-binderchannel.md#ondeviceserver
8cd4bc5
to
babfb2b
Compare
That's technically possible without anonymous in-process servers. That just requires |
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.
Overall, looks good and is surprisingly minor tweaks. The SocketAddress identity thing though is an important bug to resolve.
} | ||
} | ||
|
||
@Override | ||
public SocketAddress getListenSocketAddress() { | ||
return new InProcessSocketAddress(name); | ||
return new InProcessSocketAddress(name, anonymous ? this : null); |
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.
Yes, I think that is the correct behavior.
At first I was like, "oh, definitely," then I realized "but that might produce weird behavior/semantics" because depending on how the address was created you'd get different behavior. But I actually think it works well. Essentially it is the same as the anonymous server having a randomly-generated ID that is never reused.
core/src/main/java/io/grpc/inprocess/InProcessServerBuilder.java
Outdated
Show resolved
Hide resolved
core/src/main/java/io/grpc/inprocess/InProcessServerBuilder.java
Outdated
Show resolved
Hide resolved
Good point, I'd meant to mention that change explicitly. Updated. |
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.
Looks pretty good, I have two concerns:
- Always using "anonymous" in the InProcessSocketAddress. If I use
InProcessServer.forName("anonymous")
the two socket addresses will look identical, but won't be equal/interchangable. Essentially, there's no reliable way to learn the address is anonymous. - InProcessServer.anonymous() seems slightly weak, in that it isn't obvious it would create a new builder instance. We may want
newAnonymous()
or the like. I've added this to the API review schedule to discuss.
API review:
We ran out of time in the meeting. It seems we should maybe schedule a one-off meeting to discuss this, since I sense some resistance to concepts/behavior that make anonymous memory-only; if anonymous servers had to be shut down, it seems that'd simplify the conversation. |
Created a new AnonymousInProcessSocketAddress class which is distinct from the existing InProcessSocketAddress. This simplifies the server builder API since we can have forAddress() instead of anonymous(), and removes the need for InProcessSocketAddress to handle anonymous servers, which had some tricy implications like getName() returning null.
} | ||
InProcessServer otherServer = ((AnonymousInProcessSocketAddress) obj).getServer(); | ||
synchronized (this) { | ||
return otherServer == server; |
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.
Notable choice: I chose to let two instances to be equal if they share the same server instance. There's currently no way that can happen (given InProcessServer's impl), but I figured it was a reasonable choice from this classes perspective.
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.
Since this address is the central registration location, this should use instance equality of the address. So we shouldn't have equals/hashCode at all here. We also don't want hashCode() to change over time.
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.
Oh good point re: hashcode, I missed that. Done.
@@ -40,11 +40,16 @@ | |||
private static final ConcurrentMap<String, InProcessServer> registry | |||
= new ConcurrentHashMap<>(); | |||
|
|||
static InProcessServer findServer(String name) { |
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 possibility of creating a base AbstractInProcessSocketAddress class with package visible findServer(), registerServer(InProcessServer) and unregisterServer(InProcessServer) methods occurred to me.
That would allow this method, and registerInstance() and unregisterInstance() below to just be methods on the address, removing the need for instance checks.
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.
Optimizing this method seems like a micro-optimization. If this method is the only one that benefits, then avoiding public API seems clearly superior. If we think user's code would improve, then common base class is more interesting. Seems we can always do it in the future, so let's favor less API for now.
Your suggestion would also mean moving the registry to InProcessSocketAddress, which propagates the "strangeness" of anonymous socket address. Seems counter to our preferences.
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.
Makes sense. I was attracted to the approach making everything more consistent, even if that does mean "embracing the strangeness" to some degree. But I like philosophy that improving user code is the key goal, so agreed this probably isn't worth it.
core/src/main/java/io/grpc/inprocess/AnonymousInProcessSocketAddress.java
Show resolved
Hide resolved
} | ||
InProcessServer otherServer = ((AnonymousInProcessSocketAddress) obj).getServer(); | ||
synchronized (this) { | ||
return otherServer == server; |
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.
Since this address is the central registration location, this should use instance equality of the address. So we shouldn't have equals/hashCode at all here. We also don't want hashCode() to change over time.
@@ -40,11 +40,16 @@ | |||
private static final ConcurrentMap<String, InProcessServer> registry | |||
= new ConcurrentHashMap<>(); | |||
|
|||
static InProcessServer findServer(String name) { |
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.
Optimizing this method seems like a micro-optimization. If this method is the only one that benefits, then avoiding public API seems clearly superior. If we think user's code would improve, then common base class is more interesting. Seems we can always do it in the future, so let's favor less API for now.
Your suggestion would also mean moving the registry to InProcessSocketAddress, which propagates the "strangeness" of anonymous socket address. Seems counter to our preferences.
core/src/test/java/io/grpc/inprocess/AnonymousInProcessTransportTest.java
Outdated
Show resolved
Hide resolved
core/src/test/java/io/grpc/inprocess/InProcessTransportTest.java
Outdated
Show resolved
Hide resolved
API review notes:
There was enough agreement for anonymous at this point that everyone felt comfortable with the feature moving forward. We agreed any nits could be handled after this PR went in. |
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.
Only one comment, and it is probably-good-to-have-but-not-a-blocker.
Anonymous servers aren't registered statically, meaning they can't be referenced by name.
Only the InProcessSocketAddress, fetched via Server.getListenSockets()
can be used to connect to the server.
We also support the creation of in-process channels via forTarget(String), which
is particularly useful for production Android usage of in-process servers,
where process startup latency is crucial. A custom name resolver can be
used to create the server instance on demand without directly impacting
the startup latency of in-process gRPC clients.
This supports a more-standard approach to "OnDeviceServer" referenced in gRFC L73.
https://github.com/grpc/proposal/blob/master/L73-java-binderchannel.md#ondeviceserver