-
-
Notifications
You must be signed in to change notification settings - Fork 1.6k
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
Async DNS resolution #13619
Comments
DNS issue related to crystal-lang/crystal#13619
A great benefit of Its main issue is that its blocking the current thread. We can alleviate that a bit by running it in a dedicated thread. This could happen implicitly and would be a nice feature in the context of the ongoing multi-threading refactor (ref crystal-lang/rfcs#2). So I think we'll eventually need a native implementation for DNS resolution that uses Crystal's concurrency. We might take further inspiration from Go: https://pkg.go.dev/net#hdr-Name_Resolution That said, looking at the Go implementation it really shouldn't be that difficult to implement our own algorithm. |
It's impossible to reach feature parity with Let us pause to contemplate the idea of using plugins at the libc level, instead of running a local resolver, that allows such plugins 😮💨 Using a custom DNS resolver, be it libevent dns or a pure Crystal one, means bypassing all that customization. Even going through a custom DNS resolver then fallback to Let's not even talk about security extensions (DNSSEC, DoT, DoH, DNSCrypt). The dedicated thread is still likely a good idea. Even with an io/event aware DNS resolver, we'd still need the ability to call |
On Linux there's also |
I don't think we really have to stick to using |
building a crystal native implementation sounds like the easiest way to provide cross platform non-blocking support. chatgpt pumped out a robust (albeit basic) crystal implementation supports IPv4, IPv6 and ALPN defaulting to using |
I found this great resource explaining details of the different implementations for DNS resolves on Linux: Haven't read all of it yet. But I think an easy takeaway is that |
Another observation is that the implementation of |
There's also an overview of which resolvers are used in popular programming languages' standard libraries:
|
This issue has been mentioned on Crystal Forum. There might be relevant details there: https://forum.crystal-lang.org/t/charting-the-route-to-multi-threading-support/7320/1 |
One way to get started fast could be to start with one of the implementations for ruby, translate that and then refactor until happiness ensues. Available options there are https://github.com/ruby/resolv and very perhaps https://github.com/socketry/rubydns even if that one mainly is a server and therefore has more functionality than we want. If nothing else they could be good places to look for inspiration. |
The implementations in Go and musl libc should also be good inspiration. |
I'll state it again: languages don't implement custom DNS because it's much harder and complex than it first looks like. We already removed support for libevent DNS because it failed in practice in simple setups, because glibc, nsswitch, bonjour, docker DNS, etc. See #2660 #2426 #2745 and the issues they link to. Stdlib should merely provide a configurable backend and either keep the current blocking behavior and/or delegate to a pool of threads. Then shards can implement custom DNS resolvers and applications replace the default adapter. |
Not sure I agree without reservations. I think it depends a lot on use case. The complications only apply if you expect feature parity with That being said, this would be very low priorty. I don't think we will see a resolver implementation in stdlib anytime soon. If ever. An immediate action should be to ensure |
I created a shard with a possible implementation: https://github.com/spider-gazelle/dns Currently features:
Happy to work on getting this into crystal std lib with community collaboration if there is interest / people think it's going in the right direction - or at least hooks in the std lib so an alternative resolver can be used without monkey patching |
I have https://github.com/spider-gazelle/dns probably resolving most of the issues seen in previous getaddrinfo replacements
I would like to progress this into something that might be considered for inclusion in the std lib My opinion is:
I imagine something like this in the standard lib (of course no one would be forced to use this implementation either, assuming point 2 is implemented) # configures the standard lib DNS classes to handle DNS resolutions (as per point 2)
# by default passing through to getaddrinfo (i.e. no perceivable change in behaviour when included in an application)
# on platforms without async support, runs getaddrinfo in a thread pool so it doesn't block the reactors
require "dns"
# configure a custom resolver to be used by default (break through any corporate filters for example)
DNS.default_resolver = DNS::Resolver::HTTPS.new(["https://1.1.1.1/dns-query"])
# configures mDNS support, platforms like windows before win10 don't have it and it's often disabled on enterprise devices
DNS.resolvers[/.+\.local$/i] = DNS::Resolver::MDNS.new Does the above sound like a way forward? |
This sounds amazing to me! Even if Granted my use-case is non-standard, but I believe that even a vanilla Crystal use-case like a high-performance Kemal app could easily be affected by this design decision (e.g. an app that routinely calls customer-specified webhook urls might periodically hang for a few seconds until So if it's feasible to have the default |
@atlantis we could make the DNS lib native resolver perform the resolutions in a thread pool on platforms that don't support async - which would solve the issue Using native async system calls would be lighter weight so I think doing that by preference would work best. That way we don't complicate things beyond the 3 points I outlined above (I'll edit the message with these changes) |
Currently, the socket runtime will call LibC.getaddrinfo, which will block the event loop until it completes.
For latency-sensitive applications, this can cause slowdowns that stack, or other issues. It is easy to write a python script that makes a bunch of HTTP requests to some hosts, and it will be much faster than Crystal, because it doesn't get stuck on DNS.
Since all other discussions I could find are quite old & closed, opening this new one for visibility & broader view of the problem's history.
Related material
The text was updated successfully, but these errors were encountered: