-
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
Having compile time issues? Post here! #200
Comments
I get really slow compile times when using In my real production-app, the compile time goes from 3 seconds to around a minute when using boxed. I have a small reproduction here, where the effect is a bit less, but it still takes 10 seconds to compile (and 0.5 seconds without boxed). https://github.com/sindreij/boxed-slow-axum
Update: My bad, it's still slow. I have updated the example-code. https://github.com/sindreij/boxed-slow-axum is now even simpler, and takes multiple minutes to compile on my computer (I stopped the process after 2.5 minutes). Looks like the time is exponential when increasing the number of routes. When not using boxed, it completes in under a second. |
Every time i change the code, the compilation is very slow. Example: https://github.com/paulzhang5511/axum-compile-slow |
@davidpdrsn My bad, it's still slow. I have updated my original post. |
I also get a similar slowness when using nest also when the router is not boxed. Example: https://github.com/sindreij/boxed-slow-axum/blob/nest/src/main.rs |
Compile times improve when we remove the trait bounds from the methods like However we cannot remove the bounds from |
Okay, thanks. Until it's fixed, I work around it by defining all the routes in one place. And btw, thanks for a great framework, it's really nice to use. |
This improves compiles further when using lots of nested routes. Such as the example posted [here](#200 (comment)). It seems rustc is really slow at checking bounds on these kinds of intermediate builder methods. Should probably file an issue about that.
This improves compiles further when using lots of nested routes. Such as the example posted [here](#200 (comment)). It seems rustc is really slow at checking bounds on these kinds of intermediate builder methods. Should probably file an issue about that.
@paulzhang5511 I just merged #220 which should help with your issue, if you remove |
FYI, another workaround is also to declare all routes as E.g., this takes years to compile: let app = Router::new()
.route("/a", get(get_a))
.route("/a", get(get_a))
.route("/a", get(get_a))
.route("/a", get(get_a))
.route("/a", get(get_a))
.route("/a", get(get_a))
.route("/a", get(get_a))
.route("/a", get(get_a))
.route("/a", get(get_a))
.route("/a", get(get_a))
.route("/a", get(get_a))
.route("/a", get(get_a)).boxed(); and this is extremely fast: let app = Router::new()
.route("/a", get(get_a)).boxed()
.route("/a", get(get_a)).boxed()
.route("/a", get(get_a)).boxed()
.route("/a", get(get_a)).boxed()
.route("/a", get(get_a)).boxed()
.route("/a", get(get_a)).boxed()
.route("/a", get(get_a)).boxed()
.route("/a", get(get_a)).boxed()
.route("/a", get(get_a)).boxed()
.route("/a", get(get_a)).boxed()
.route("/a", get(get_a)).boxed()
.route("/a", get(get_a)).boxed(); BTW, I am not sure whether it has any runtime performance hit. |
It will have some perf impact but exactly how big I don't know. That'd require benchmarking which I haven't done. |
After a great experience testing axum out by converting some of my warp based projects I ran into this problem with boxed(), and its a real blocker for me. The problem is that boxed() is required for testing - since I need to be able to specify a type in order to get a Router against which I can run my tests. Sorry if I'm missing something obvious here :) |
@nicrgren you can add boxed() for every route. It s a bit annoying, but it remove compiler issue. And I don't feel that it's critically affect performance |
@nicrgren yes this is unfortunate. Here is a workaround: use axum::{
handler::{get, post},
routing::BoxRoute,
Json, Router,
};
use tower_http::trace::TraceLayer;
#[tokio::main]
async fn main() {
let addr = std::net::SocketAddr::from(([127, 0, 0, 1], 3000));
let listener = std::net::TcpListener::bind(addr).unwrap();
run(listener).await;
}
async fn run(listener: std::net::TcpListener) {
let app = Router::new()
.route("/", get(|| async { "Hello, World!" }))
.route(
"/json",
post(|payload: Json<serde_json::Value>| async move {
Json(serde_json::json!({ "data": payload.0 }))
}),
)
// We can still add middleware
.layer(TraceLayer::new_for_http());
let addr = listener.local_addr().unwrap();
tracing::debug!("listening on {}", addr);
axum::Server::from_tcp(listener)
.unwrap()
.serve(app.into_make_service())
.await
.unwrap();
}
#[cfg(test)]
mod tests {
use super::*;
use axum::body::Body;
use axum::http::{self, Request, StatusCode};
use serde_json::{json, Value};
use std::net::{SocketAddr, TcpListener};
// You can also spawn a server and talk to it like any other HTTP server:
#[tokio::test]
async fn a_test() {
let listener = TcpListener::bind("0.0.0.0:0".parse::<SocketAddr>().unwrap()).unwrap();
let addr = listener.local_addr().unwrap();
tokio::spawn(run(listener));
let client = hyper::Client::new();
let response = client
.request(
Request::builder()
.uri(format!("http://{}", addr))
.body(Body::empty())
.unwrap(),
)
.await
.unwrap();
let body = hyper::body::to_bytes(response.into_body()).await.unwrap();
assert_eq!(&body[..], b"Hello, World!");
}
} |
@z4rx Yeah, that's also a good solution. I'll try it out. Thanks for helping out! @davidpdrsn thanks so much! I'll try it out |
This code compiles very slowly. When the memory usage of async fn handler() -> &'static str {
"hello"
}
let service = axum::handler::get(
handler
.layer(axum::AddExtensionLayer::new(1))
.layer(axum::AddExtensionLayer::new((1, 1)))
.layer(axum::AddExtensionLayer::new((1, 1, 1)))
.layer(axum::AddExtensionLayer::new((1, 1, 1, 1)))
.layer(axum::AddExtensionLayer::new((1, 1, 1, 1, 1)))
.layer(axum::AddExtensionLayer::new((1, 1, 1, 1, 1, 1)))
.layer(axum::AddExtensionLayer::new((1, 1, 1, 1, 1, 1, 1)))
.layer(axum::AddExtensionLayer::new((1, 1, 1, 1, 1, 1, 1, 1))),
); |
@sunli829 good catch! Thats is worth investigating. It works and compiles instantly using Router::new().route(
"/",
get(handler.layer(
ServiceBuilder::new()
.layer(AddExtensionLayer::new(1))
.layer(AddExtensionLayer::new((1, 1)))
.layer(AddExtensionLayer::new((1, 1, 1)))
.layer(AddExtensionLayer::new((1, 1, 1, 1)))
.layer(AddExtensionLayer::new((1, 1, 1, 1, 1)))
.layer(AddExtensionLayer::new((1, 1, 1, 1, 1, 1)))
.layer(AddExtensionLayer::new((1, 1, 1, 1, 1, 1, 1)))
.layer(AddExtensionLayer::new((1, 1, 1, 1, 1, 1, 1, 1)))
.into_inner(),
)),
); This compiles in 0.2 seconds on my machine. Would still be worth investing what you posted though. |
Thank you for your answer. I realized that my |
I have found a workaround for the issues with #![recursion_limit = "1024"]
use axum::{
body::{box_body, Body, BoxBody, Bytes, HttpBody},
handler::get,
http::{Request, Response},
routing::Layered,
BoxError, Router,
};
use std::{marker::PhantomData, net::SocketAddr};
use tower::{buffer::Buffer, util::BoxService, Layer, Service, ServiceBuilder};
use tower_http::map_response_body::MapResponseBodyLayer;
#[tokio::main]
async fn main() {
let app = my_app();
let addr = SocketAddr::from(([127, 0, 0, 1], 3000));
axum::Server::bind(&addr)
.serve(app.into_make_service())
.await
.unwrap();
}
fn my_app() -> Router<Layered<BoxedService<Body>>> {
Router::new()
.route("/", get(|| async {}))
.route("/", get(|| async {}))
.route("/", get(|| async {}))
.route("/", get(|| async {}))
.route("/", get(|| async {}))
.route("/", get(|| async {}))
.route("/", get(|| async {}))
.route("/", get(|| async {}))
.route("/", get(|| async {}))
.route("/", get(|| async {}))
.route("/", get(|| async {}))
.route("/", get(|| async {}))
.route("/", get(|| async {}))
.route("/", get(|| async {}))
.route("/", get(|| async {}))
.route("/", get(|| async {}))
.route("/", get(|| async {}))
.route("/", get(|| async {}))
.route("/", get(|| async {}))
.route("/", get(|| async {}))
.route("/", get(|| async {}))
.route("/", get(|| async {}))
.route("/", get(|| async {}))
.route("/", get(|| async {}))
.route("/", get(|| async {}))
.route("/", get(|| async {}))
.route("/", get(|| async {}))
.route("/", get(|| async {}))
.route("/", get(|| async {}))
.route("/", get(|| async {}))
.route("/", get(|| async {}))
.route("/", get(|| async {}))
// have the specify the request body type here
// otherwise it takes forever to compile
.layer(BoxLayer::<Body, _>::new())
}
// layer that applies that same middleware as `Router::boxed`
struct BoxLayer<ReqBody, ResBody> {
_marker: PhantomData<(ReqBody, ResBody)>,
}
impl<ReqBody, ResBody> BoxLayer<ReqBody, ResBody> {
fn new() -> Self {
Self {
_marker: PhantomData,
}
}
}
impl<S, ReqBody, ResBody> Layer<S> for BoxLayer<ReqBody, ResBody>
where
S: Service<Request<ReqBody>, Response = Response<ResBody>> + Send + 'static,
S::Error: Into<BoxError> + Send,
S::Future: Send,
ReqBody: Send + 'static,
ResBody: HttpBody<Data = Bytes> + Send + Sync + 'static,
ResBody::Error: Into<BoxError>,
{
type Service = BoxedService<ReqBody>;
fn layer(&self, inner: S) -> Self::Service {
ServiceBuilder::new()
.buffer(1024)
.layer(BoxService::layer())
.map_err(Into::into)
.layer(MapResponseBodyLayer::new(box_body))
.service(inner)
}
}
type BoxedService<ReqBody> =
Buffer<BoxService<Request<ReqBody>, Response<BoxBody>, BoxError>, Request<ReqBody>>; |
@davidpdrsn |
I ran into the same problem of extremely slow compile times on 1.56, both sondr3/cv-aas and sondr3/web take forever to compile. |
Well alright. Rust giveth and then taketh away. I'll look into it. Might have to go with #393. |
Might be worth creating an issue in the Rust repo, going from a couple of seconds to build to a few minutes is a serious compile time regression imho. |
Yep we should do that. Just haven't had great luck with the issues we've filed already 🤷 You're welcome to file an issue and link it from here. I'm afk the rest of the week but will look into it next week. |
Having the same problem with my project. Even a simple For now I pinned it to rust 1.55 to get around it. You can reproduce it by switching to |
I believe #404 fixed all known compile time issues. All services are now boxed internally which makes the size of the router type constant so the type no longer grows as you add more routes. It'll be shipped in 0.3 which is coming within a week or two 🤞 |
I'll close this now since 0.3 is out and fixed all issues mentioned here. If someone still has compile time issues I'd very interested to hear about it. |
Crates should not be slow to compile due to axum. If you're having compile issues please post a reproduction script here so we can fix it!
Note that compiles have been improved for the upcoming 0.2 release. If you're having issues try depending directly on
main
.Known issues
These are the currently known compile time issues:
.route(...)
calls that ends in.boxed()
can be slow to compile. We believe this is a rustc bug and don't have a fix for it in axum. See Having compile time issues? Post here! #200 (comment) for a possible workaround.Handler::layer
multiple times to add several middleware. See Having compile time issues? Post here! #200 (comment). Note that usingtower::ServiceBuilder
fixes the issue and is the recommended way to add multiple middleware.The text was updated successfully, but these errors were encountered: