So I'm loading very large (~5 MB) shapefiles in memory (via the geojson
crate) and trying to reply with their contents using warp. When my server starts, I (synchronously) generate the filters from a configuration that I load from a file.
My filter looks like:
let shapefiles: Arc<HashMap<String, Shapefile>> = /* ... */;
let shapefiles_show = {
let shapefiles = shapefiles.clone();
warp::get()
.and(warp::path!(String))
.map(move |id: String| shapefiles::show(&shapefiles, &id))
.with(warp::compression::gzip())
};
and the definition of shapefiles::show
(which produces the response to a request) looks like:
mod shapefiles {
pub fn show(
shapefiles: &Arc<HashMap<String, Shapefile>>,
id: &String,
) -> hyper::Response<String> {
if let Some(shapefile) = shapefiles.get(id) {
let data: String = shapefile.data.to_string();
let response = {
http::response::Builder::new()
.status(hyper::StatusCode::OK)
.header(hyper::header::CONTENT_TYPE, "application/vnd.geo+json")
.header(hyper::header::CACHE_CONTROL, "public")
.body(data)
.unwrap()
};
response
} else {
let response = {
http::response::Builder::new()
.status(hyper::StatusCode::NOT_FOUND)
.body("{}".to_string())
.unwrap()
};
response
}
}
}
(The full source code is available here if more context is needed. I didn't want to copy-paste 400 lines into a post.)
This code compiles and executes correctly, but the main problem is that the line I've commented with a FIXME
takes 3 seconds to execute, and while this is happening everything is blocked. (Namely, it clones 5 MB from a &String
to a String
, which is very wasteful and seemingly not async-friendly.) My show
function currently has a return type of hyper::Response<String>
, but I am looking to change it to something that will instead pass out a reference.
The Big Problem™️ I am facing is that cloning such a string (i) at request time, and (ii) in such a way that blocks the rest of the server, including other routes is unacceptable. I have tried messing around with returning a &String
, but hyper::Body
only has From<...>
impls for 'static
references… so, is there a way to store my data in a "static" way, such that I can easily get a &'static str
(or something similar) to pass out? Thanks in advance!
Actually, I found that this is not the case. The Big Problem actually seems to be that somehow the full request time (from client request -> client received complete response) is consistently above 5 seconds, and seems to be blocking. Even when I remove the warp::compression::gzip()
wrapper, the request still takes at least 5 seconds.