[GH-ISSUE #736] How to send DNS queries in parallel via UDP? #287

Closed
opened 2026-03-07 23:16:31 +03:00 by kerem · 8 comments
Owner

Originally created by @ovidiu-ionescu on GitHub (Apr 7, 2019).
Original GitHub issue: https://github.com/hickory-dns/hickory-dns/issues/736

I tried to expand on the async DNS query example but the wait time seems to increase linearly with the amount of DNS queries. I get the impression the system is waiting for the first request to get a reply before issuing the second request and so on.

Please provide an example where a number of UDP DNS requests are being submitted without waiting for the server to respond in between.

Originally created by @ovidiu-ionescu on GitHub (Apr 7, 2019). Original GitHub issue: https://github.com/hickory-dns/hickory-dns/issues/736 I tried to expand on the async DNS query example but the wait time seems to increase linearly with the amount of DNS queries. I get the impression the system is waiting for the first request to get a reply before issuing the second request and so on. Please provide an example where a number of UDP DNS requests are being submitted without waiting for the server to respond in between.
kerem 2026-03-07 23:16:31 +03:00
Author
Owner

@bluejekyll commented on GitHub (Apr 8, 2019):

This shouldn’t be the case. Could you share your code?

<!-- gh-comment-id:480673396 --> @bluejekyll commented on GitHub (Apr 8, 2019): This shouldn’t be the case. Could you share your code?
Author
Owner

@ovidiu-ionescu commented on GitHub (Apr 8, 2019):

Here's my code:
github.com/ovidiu-ionescu/learning-rust@fc8514355f/src/dns_resolver.rs

I just took the async example from trust-dns and added a for loop to it.

<!-- gh-comment-id:480684155 --> @ovidiu-ionescu commented on GitHub (Apr 8, 2019): Here's my code: https://github.com/ovidiu-ionescu/learning-rust/blob/fc8514355f0f79e0084f03105ef4f91fb6bde125/src/dns_resolver.rs I just took the async example from trust-dns and added a for loop to it.
Author
Owner

@bluejekyll commented on GitHub (Apr 8, 2019):

I won’t have time to provide a full example for a little bit, but your second loop is serializing the futures, what you want is to throw your collection into a select, specifically, select_all: https://docs.rs/futures/0.1.26/futures/future/fn.select_all.html

<!-- gh-comment-id:480862350 --> @bluejekyll commented on GitHub (Apr 8, 2019): I won’t have time to provide a full example for a little bit, but your second loop is serializing the futures, what you want is to throw your collection into a select, specifically, `select_all`: https://docs.rs/futures/0.1.26/futures/future/fn.select_all.html
Author
Owner

@ovidiu-ionescu commented on GitHub (Apr 8, 2019):

I don't mind waiting for all of them to finish before proceeding, trouble is that one request takes about 20 milliseconds and five take a bit more than 100. I suspect most of that time is actually taken by the packets traveling to Google's DNS and back. I was hoping to get a shorter time overall by sending all the requests at once so that they all travel together and hopefully all five only take a little bit more than just one.

<!-- gh-comment-id:480870177 --> @ovidiu-ionescu commented on GitHub (Apr 8, 2019): I don't mind waiting for all of them to finish before proceeding, trouble is that one request takes about 20 milliseconds and five take a bit more than 100. I suspect most of that time is actually taken by the packets traveling to Google's DNS and back. I was hoping to get a shorter time overall by sending all the requests at once so that they all travel together and hopefully all five only take a little bit more than just one.
Author
Owner

@bluejekyll commented on GitHub (Apr 8, 2019):

In that case, look at https://docs.rs/futures/0.1.26/futures/future/fn.join_all.html, that will combine all the futures and execute them all in parallel. I think it will work better than your loop. You could also configure the resolver to only use tcp, which might provide more reliable performance?

<!-- gh-comment-id:480877379 --> @bluejekyll commented on GitHub (Apr 8, 2019): In that case, look at https://docs.rs/futures/0.1.26/futures/future/fn.join_all.html, that will combine all the futures and execute them all in parallel. I think it will work better than your loop. You could also configure the resolver to only use tcp, which might provide more reliable performance?
Author
Owner

@bluejekyll commented on GitHub (Apr 8, 2019):

It might be good to make sure we're both on the same page in our understanding of futures in Rust. Futures are lazy, this means they shouldn't start executing before being polled. The futures you're creating should not actually even submit the query until you poll them, that would be the block_on call in your code. I used to have some bugs in this area, it can be a subtle mistake, I think they're all cleaned up at this point. The reason the join_all I mention above would be better than your loop, is that each of those futures you've created for the query, won't actually start until the block_on, which means each is serial and waiting for the previous to execute. With a join_all, all the futures will be started and then waited on if that is the thing you are putting into the block_on.

That should fix your issue.

<!-- gh-comment-id:480911883 --> @bluejekyll commented on GitHub (Apr 8, 2019): It might be good to make sure we're both on the same page in our understanding of futures in Rust. Futures are lazy, this means they shouldn't start executing before being polled. The futures you're creating should not actually even submit the query until you poll them, that would be the `block_on` call in your code. I used to have some bugs in this area, it can be a subtle mistake, I think they're all cleaned up at this point. The reason the `join_all` I mention above would be better than your loop, is that each of those futures you've created for the query, won't actually start until the `block_on`, which means each is serial and waiting for the previous to execute. With a `join_all`, all the futures will be started and then waited on if that is the thing you are putting into the `block_on`. That *should* fix your issue.
Author
Owner

@bluejekyll commented on GitHub (Apr 8, 2019):

Also, if you want a recursive stub resolver for queries, I would recommend using the resolver: https://docs.rs/trust-dns-resolver/0.11.0-alpha.3/trust_dns_resolver/

It will handle things like CNAME chain resolutions and managing upgrades from UDP to TCP connections on truncation, etc...

<!-- gh-comment-id:480913367 --> @bluejekyll commented on GitHub (Apr 8, 2019): Also, if you want a recursive stub resolver for queries, I would recommend using the resolver: https://docs.rs/trust-dns-resolver/0.11.0-alpha.3/trust_dns_resolver/ It will handle things like CNAME chain resolutions and managing upgrades from UDP to TCP connections on truncation, etc...
Author
Owner

@ovidiu-ionescu commented on GitHub (Apr 8, 2019):

join_all did make a difference, I see much better times.
I also learned that futures are lazy.

A double thank you!

The improved version of the code, using join_all: github.com/ovidiu-ionescu/learning-rust@70d4db1110/src/dns_resolver.rs

<!-- gh-comment-id:480994138 --> @ovidiu-ionescu commented on GitHub (Apr 8, 2019): `join_all` did make a difference, I see much better times. I also learned that futures are lazy. A double thank you! The improved version of the code, using `join_all`: https://github.com/ovidiu-ionescu/learning-rust/blob/70d4db1110f80d565adacbc55c71a76fbefd1cfa/src/dns_resolver.rs
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
starred/hickory-dns#287
No description provided.