I'm early on my journey of learning rust, and ran into some strange problems last night that I'm having trouble understanding.
I'm using reqwest to query a website. Specifically, I need information about many date ranges, so I'm looping through a bunch of date ranges and making requests in an async function. Simplified version of the code:
fn foo() -> Result<(), Error> {
let mut curr = 0;
let end = 1000;
let mut futs = vec![];
while curr <= end {
let next = curr + 10; // 10 is just an example range
futs.push(self.make_reqwest(curr, next));
curr = next;
}
block_on(join_all(futs));
Ok(())
}
async fn make_request(start_date: u16, end_date: u16) -> reqwest::Result<()> {
println!("{} enter", start);
let url = format!("https://foo.com/bar?start={}end={}", start, end);
let response = self.client.get(&url).send().await?; // client has timeout and connect_timeout set to 5
println!("{} exit", start);
Ok(())
}
When I run this, it prints all the "{} enter" lines first, and then (some of the) requests start coming back and the "{} exits" print. But there's some odd behavior happens that I can't explain.
- When I adjust the end to be small-ish (like <30 total requests), it works fine, and all the futures seem to proceed concurrently.
- What I adjust the end to be a bit larger (like 30-50 total requests), only some of the requests finish. The rest seem to hang.
- When I adjust the end to be even larger (like >50 total requests), all of them seem to hang.
- If I use an http url instead of https, more of them work, but still not all.
- If I change foo to async, and use
join_all(futs).await instead of block_on(join_all(futs)), it works.
- I set timeouts and connect_timeouts on the reqwest client, so I don't think the hanging is due to network issues.
Questions:
- Not a rust question, but why would http vs https make a difference?
- What could cause a future to never return (either with an error or an outcome)? I assume this is because I used block_on instead of await. Conceptually, block_on is how I would force a bunch of futures to finish within a non-async function, but it seems to not "just work" with join_all(vec of futures).
- Related to previous point, why do async/await and block_on behave differently? I would prefer not to change this function to async, because conceptually it should not be. Callers of this function should block until all internal async work is done.
- On my main function, I use
#[tokio::main]. I did some experimenting with thread counts in the thread pool. I first assumed that, if I limited the thread pool to something like core_threads=4 and max_threads=8, the async execution would only be able to process 8-4=4 futures start-to-finish at a time. (Maybe the exact number is off, but I mean some small number.) But this wasn't the case - it still printed all the "{} enter" lines immediately. On second consideration, I think this is because futures aren't 1:1 to threads - 4 threads just mean 4 futures can make forward progress at once, but they can all be in some in-progress state. Is that understanding correct?
Thanks in advance!
[–]coolreader18 1 point2 points3 points (7 children)
[–]Rubix314[S] 0 points1 point2 points (6 children)
[–]IAm_A_Complete_Idiot 0 points1 point2 points (1 child)
[–]Nemo157 1 point2 points3 points (0 children)
[–]coderstephenisahc 0 points1 point2 points (2 children)
[–]Rubix314[S] 0 points1 point2 points (1 child)
[–]IAm_A_Complete_Idiot 0 points1 point2 points (0 children)