-
Notifications
You must be signed in to change notification settings - Fork 1.1k
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
Implement routing in Router
#240
Comments
I've read through your implementation and my initial impression is that it feels like a lot of changes, and new API, for not much gain.
By "ambiguous" do you something like
I don't think this is an issue. The actual work performed by hyper or your app is likely taking up way more time than routing.
I disagree. I actually like that 404 handling is able to be built directly using existing middleware. |
I can't speak for whether it has been a problem someone has actually encountered, but I imagine it would be quite easy to accidentally leave some old code in when refactoring that would create a situation like that.
That's a fair point. I sometimes do enjoy optimization a little too much 😛.
The reason I consider the current 404 handling to be hacky is that I see Another reason I devised this approach is that I see it as providing a sort of consistency between services and routers. There are two approaches that a library could take:
Axum currently is sort of halfway between the two; on the one hand, it has a |
I agree with pretty much all of @KaiJewson's points. I'd also be worried about possible route ambiguity when writing larger services with axum's current routing, feel slightly uneasy about As a potentially relevant data point, in Rocket IIRC you can't even start your app if the routes are ambiguous. This is not as painful as it might sound, since it disambiguates many common cases that would otherwise be ambiguous through a simple rule that decides the default "rank" of a route (lower rank will be preferred if otherwise ambiguous), in combination with allowing users to explicitly specify a route's rank. Only routes that overlap and have the same rank will result in an error. IME this results in really intuitive and easy-to-use routing. Disclaimer: I haven't actually had a look at the implementation, only read the discussion here. |
Firstly I wanna apologies for my first response being a bit dismissive. I really appreciate this kind of insightful feedback ❤️
I understand where you're coming from but I personally don't have this opinion. I think part of tower's power comes from
In a way
Thats an interesting way of looking at it. I think I see your point. You are right at the I wonder if you'd be up for building a minimal working version of your prototype. Doesn't have to copy all of axum's API of course, just enough to route a request to one of a few services. Doing it in a fresh repo is fine. I think having some working code to look at would help. |
This is certainly interesting, I had the previous 404 example in mind when I said I wasn't a fan of the current solution. This is a whole lot better IMHO. |
I agree with the vision that the application shouldn't compile or should panic before serving requests if any routes are ambiguous. It would feel more rusty not having to debug why you can't reach the endpoint you thought you set up correctly when your server is running. I cannot tell whether the proposed solution is the way forward to solve this topic, though.
No idea about the influence of it on real apps.
The 404 example was updated recently and it looks great. It fits very well with the rest of the framework. |
Oh, you're right - I didn't realize the 404 handling had been changed. It's a lot cleaner now :D In that case, it might be the best thing to do might be to add |
Ok, I have finished my implementation on this branch. Some notes:
|
@KaiJewson thats fantastic! I'll give it a read within a week or two. |
@KaiJewson it looks great! I think it may be possible to close #174 by constraining The new routing works just by denying ambiguity + routing to the less generic ones first? |
I have considered that as well but it doesn't prevent people from doing |
If we document that asterisk routes should not be used with another
Yes, exact values take precedence over captures which take precedence over asterisk routes. |
@KaiJewson Your implementation also removes the need for the |
I've been working on a different implementation that replaces the router with matchit #363 Some points of comparison to @KaiJewson implementation from #240 (comment)
|
Thanks @KaiJewson for the initial PoC and @davidpdrsn for the final implementation. The common prefix based route matching is great 👍 |
Yes! Huge thanks to @KaiJewson for their work ❤️ 0.3 is going to great 🎊 |
Feature Request
Motivation
Proposal
Move all route matching into the
Router
type itself. I have implemented a sketch of this proposal on the playground, based on an imaginaryNode
type that does all the actual routing, and can be optimized to O(log n) because it knows all the routes up front. It is similar tomatchit::Node
but with a larger API; we might be able to upstream the required features there and use it. The sketch uses un-newtyped future types, which would obviously be changed in the actual library. We also might want to make the implementation details (Destinations
,RouteDestination
, etc) inaccessible (public in a private module) to avoid polluting the public API.Now that routing is all done in one place, 404 handling can be implemented simply as another generic field of the
Router
type, which would be anEmpty404
service by default, but can be changed to any service.Drawbacks
The current design makes
route("/", get(a)).route("/", post(b))
not work anymore; it will panic at server startup. Similarlyroute("/", get(a)).or(route("/", post(b)))
panics as well. However this might not be necessary now that we can panic instead of silently fail on that code.The implementation uses an hlist to store the list of services the routes point to, meaning routing is technically O(n). It should be really easy for the optimizer to turn it into O(1), but I haven't checked so we're kind of at the mercy of LLVM there. That said, even if it is O(n) it's just a sequence of integer comparisons which should be pretty cheap.
Alternatives
Router
type that boxes all internal services, but otherwise operates in a similar fashion to above. This would make it a lot easier to pass around and return routers, even when.boxed()
exists. Not sure how it would impact performance.The text was updated successfully, but these errors were encountered: