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
[FLINK-9386] Embed netty router #6031
Conversation
5f98447
to
018b6ef
Compare
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 checked the copied classes and compared them to the originals. The simplications/merges look good to me and make a lot of sense. I also like the renamings. 👍
-
I've manually checked that registration order is respected. We still need to manually make sure that we register the handlers in the correct order. In the legacy
WebRuntimeMonitor
this was accidental as far as I can tell. In the newRestServerEndpoint
we explicitly sort them before registering:Collections.sort(handlers, RestHandlerUrlComparator.INSTANCE);
I'm wondering whether we should make this fact more explicit by adding comments to the
add*
methods inRouter
. Another approach is theRoutes
builder I mentioned as an inline comment. -
I've furthermore build the project and check that job submission via REST and other endpoints for the web UI work as expected.
-
Do we file a follow up ticket to remove netty-router from our shaded Netty? Optionally, we might consider adding an import suppression for
org.apache.flink.shaded.netty4.io.netty.handler.codec.http.router.*
to guard against any accidental usage until we remove the dependency. -
I don't know what your opinion is on cleaning up the copied classes. I'm OK with keeping everything as is, but I get a bunch of minor/trivial warnings about the code (like code visibility, unchecked calls, unused variables, etc.). I understand if we want to stay close to the copied classes.
import static java.util.Objects.requireNonNull; | ||
|
||
/** | ||
* This class replaces the standard error response to be identical with those sent by the {@link AbstractRestHandler}. |
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 would add a comment that this was copied and simplified from https://github.com/sinetja/netty-router/blob/1.10/src/main/java/io/netty/handler/codec/http/router/Handler.java and https://github.com/sinetja/netty-router/blob/1.10/src/main/java/io/netty/handler/codec/http/router/AbstractHandler.java for future reference. That can be beneficial in the future.
- I would also copy the high-level comment from that class:
Inbound handler that converts HttpRequest to RoutedRequest and passes RoutedRequest to the matched handler
as found in the original Handler class.
/** | ||
* This class replaces the standard error response to be identical with those sent by the {@link AbstractRestHandler}. | ||
*/ | ||
public class RouterHandler extends SimpleChannelInboundHandler<HttpRequest> { |
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've verified that this class merges the behaviour of Handler
and AbstractHandler
. 👍
Since we copied the code, we might leave it as is, but I noticed the following minor things (there are similar warnings in the other copied classes):
L46
,L47
: fields can be privateL62
:throws Exception
can be removedL98
: I get a unchecked call warning forRouteResult
. We could use<?>
to get rid of it I think
import java.util.Map.Entry; | ||
|
||
/** | ||
* This is adopted and simplified code from tv.cntt:netty-router library. For more information check {@link Router}. |
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.
private static final Logger log = LoggerFactory.getLogger(MethodlessRouter.class); | ||
|
||
// A path pattern can only point to one target | ||
private final Map<PathPattern, T> routes = new LinkedHashMap<>(); |
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.
Using the LinkedHashMap
is what preserves the order of adding handlers, right? Or is there another thing that I've missed?
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.
Please correct me if I'm wrong but I have a question regarding memory visibility: The thread that updates this map and the Netty event loop threads are different, so there might theoretically be a memory visibility issue if routes are added after the router has been passed to the RouterHandler
. I don't think that we do this currently, but the API theoretically allows it. I'm wondering whether it makes sense to make the routes immutable, maybe something like creating the routes with a builder:
Routes routes = new RoutesBuilder()
.addGet(...)
...
.build();
Router router = new Router(routes);
Or use a thread-safe map here that also preserves ordering (maybe wrap using synchronizedMap
).
Since we currently rely on correct registration order (e.g. /jobs/overview
before /jobs/:id
for correct matching), the immutable approach would allow us to include a utility in Routes
that sorts pattern as done in RestServerEndpoint:143
:
Collections.sort(handlers,RestHandlerUrlComparator.INSTANCE);
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'm not convinced that embedding the sorting logic inside router is good idea. Now it's clear that order of adding methods matter and it should be handled by the caller. If we provide sorting option, it can add confusion, especially because it can not be mandatory option - our sorting based on number of parameters is just a hack that kind of works with our patterns at the moment, but there are corner cases that would have to be "sorted"/"ordered" manually. Like patterns:
/jobs/foo/:B
/jobs/:A/bar
Which one of it should be matched to url /jobs/foo/bar
?
Regarding thread safety, this class is just simply not thread safe, and it should be the user's responsibility to handle it appropriately. On the other hand, turning it into immutable would hardcode some assumption and prevent some possible use cases and I'm not sure if that's worth the effort (it would add some substantial boiler plate code in storing intermediate builder's state, without clear benefits for the future since this code hasn't changed much for last couple of years). Don't get me wrong, the builder approach is nicer (regardless of sorting/immutability), but I'm just not sure if it's worth the extra effort.
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.
Fine with me to not invest more time into this and keep it as is 👍
} | ||
} | ||
|
||
channelHandlerContext.fireChannelRead(new RoutedRequest(routeResult, httpRequest).retain()); |
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.
Do you think it makes sense to move new RoutedRequest(routeResult, httpRequest).retain()
to a separate line? I was wondering whether it is retained as in the original handler, because I missed the retain()
here.
I find the following clearer, but feel free to ignore:
RoutedRequest request = new RoutedRequest(routeResult, httpRequest);
request.retain();
channelHandlerContext.fireChannelRead(request);
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.
changed into
RoutedRequest<?> request = new RoutedRequest<>(routeResult, httpRequest);
channelHandlerContext.fireChannelRead(request.retain());
I like having .retain()
calls to be in the same line where they are actually designed for, since it's kind of self documenting which line is the reason behind retaining.
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.
👍
CompletableFuture<FullHttpResponse> responseFuture; | ||
RouteResult result = routedRequest.getRouteResult(); |
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 think if you do RouteResult<?> result = routedRequest.getRouteResult();
you don't need the cast to Set<String>
in lines 101 and 106.
* <p>The pattern will be broken to tokens, example: | ||
* {@code ["constant1", ":variable", "constant2", ":*"]} | ||
*/ | ||
final class PathPattern { |
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.
* | ||
* <p>Result of calling {@link Router#route(HttpMethod, String)}. | ||
*/ | ||
public class RouteResult<T> { |
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.
import java.util.Set; | ||
|
||
/** | ||
* This is adopted and simplified code from tv.cntt:netty-router library. Compared to original version this one |
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 for the review and manual checks! I either addressed your comments in fixup commit and left comment responses otherwise.
I planned to upgrade netty and drop netty-router in one step of upgrading
I would be open to clean it up as we think it's best. I do not see a point in maintaining compatibility with the original sources. If you have other such comments, please feel free to write them. |
👍 to merge. Thanks for taking care of this. Looking forward to finally have a recent Netty version. |
This commit replaces netty-router dependency with our own version of it, which is simplified and adds guarantees about order of matching router patterns. This is a prerequisite for FLINK-3952. netty-router 1.10 is incompatible with Netty 4.1, while netty-router 2.2.0 brakes a compatibility in a way that we were unable to use it.
Thanks :) I have squashed commits and rebased the PR on latest master. Let's maybe wait for the final green travis before merging. |
This commit replaces netty-router dependency with our own version of it, which is simplified and adds guarantees about order of matching router patterns. This is a prerequisite for FLINK-3952. netty-router 1.10 is incompatible with Netty 4.1, while netty-router 2.2.0 brakes a compatibility in a way that we were unable to use it. This closes apache#6031.
This commit replaces netty-router dependency with our own version of it, which is simplified and adds guarantees about order of matching router patterns. This is a prerequisite for FLINK-3952. netty-router 1.10 is incompatible with Netty 4.1, while netty-router 2.2.0 brakes a compatibility in a way that we were unable to use it. This closes apache#6031.
This commit replaces netty-router dependency with our own version of it, which is simplified and adds guarantees about order of matching router patterns. This is a prerequisite for FLINK-3952. netty-router 1.10 is incompatible with Netty 4.1, while netty-router 2.2.0 brakes a compatibility in a way that we were unable to use it. This closes apache#6031.
This commit replaces netty-router dependency with our own version of it, which is simplified and adds guarantees about order of matching router patterns. This is a prerequisite for FLINK-3952. netty-router 1.10 is incompatible with Netty 4.1, while netty-router 2.2.0 brakes a compatibility in a way that we were unable to use it. This closes apache#6031.
This commit replaces netty-router dependency with our own version of it, which is simplified and adds guarantees about order of matching router patterns. This is a prerequisite for FLINK-3952. netty-router 1.10 is incompatible with Netty 4.1, while netty-router 2.2.0 brakes a compatibility in a way that we were unable to use it. This closes #6031. Signed-off-by: Nico Kruber <nico@data-artisans.com>
This replaces netty-router dependency with our own version of it, which is simplified and adds guarantees about order of matching router patterns.
This is a prerequisite for https://issues.apache.org/jira/browse/FLINK-3952 . netty-router 1.10 is incompatible with Netty 4.1, while netty-router 2.2.0 brakes a compatibility in a way that we were unable to use it.
Verifying this change
This change ports part of the netty-router code, including tests and adds a test for preserving patter matching order by the
Router
. Additionally this change is covered by various of our ITCases.Does this pull request potentially affect one of the following parts:
@Public(Evolving)
: (yes / no)Documentation