Search code examples
rubydnsresolutionfreeze

Ruby DNS resolution hangs every other time


I have this piece of ruby code that works intermittently, I can call it in the shell multiple times ctrl-c-ing when it hangs and it will work instantaneously half of the times and hang forever the other half.

require 'resolv'
puts "initializing"
txt = Resolv::DNS.open do |dns|
    records = dns.getresources("www.google.com", Resolv::DNS::Resource::IN::A)
    records.empty? ? nil : records.map{|rec| rec.address}.compact
end
puts "records are #{txt}"

Here the output I see in both cases

[ ~]$ ruby test.rb 
initializing
records are 216.58.217.132
[ ~]$ ruby test.rb 
initializing
records are 216.58.217.132
[ ~]$ ruby test.rb 
initializing

^C/usr/lib/ruby/1.8/resolv.rb:620:in `select': Interrupt
        from /usr/lib/ruby/1.8/resolv.rb:620:in `request'
        from /usr/lib/ruby/1.8/resolv.rb:489:in `each_resource'
        from /usr/lib/ruby/1.8/resolv.rb:975:in `resolv'
        from /usr/lib/ruby/1.8/resolv.rb:973:in `each'
        from /usr/lib/ruby/1.8/resolv.rb:973:in `resolv'
        from /usr/lib/ruby/1.8/resolv.rb:972:in `each'
        from /usr/lib/ruby/1.8/resolv.rb:972:in `resolv'
        from /usr/lib/ruby/1.8/resolv.rb:970:in `each'
        from /usr/lib/ruby/1.8/resolv.rb:970:in `resolv'
        from /usr/lib/ruby/1.8/resolv.rb:481:in `each_resource'
        from /usr/lib/ruby/1.8/resolv.rb:468:in `getresources'
        from test.rb:4
        from /usr/lib/ruby/1.8/resolv.rb:307:in `open'
        from test.rb:3

I understand that DNS is an external service and it might be flaky, but that does not explain how it can work immediately sometimes and hang forever other times, and also, when I use the host www.google.com command it always returns immediately.

How can I make this work predictably?


Solution

  • I tried your code and cannot reproduce the problem. Note that for your specific example the response is not always the same, which makes sense for www.google.com.

    tmp> ruby resolv.rb
    # initializing
    # records are [#<Resolv::IPv4 216.58.211.100>]
    
    tmp> ruby resolv.rb
    # initializing
    # records are [#<Resolv::IPv4 74.125.136.105>, #<Resolv::IPv4 74.125.136.106>, #<Resolv::IPv4 74.125.136.147>, #<Resolv::IPv4 74.125.136.99>, #<Resolv::IPv4 74.125.136.103>, #<Resolv::IPv4 74.125.136.104>]
    

    I think that the problem you are experiencing lies outside of your code. The host command does a bit more than making a DNS request. It also checks the /etc/hosts file, and there might be some local cache involved. You should try to test your DNS, maybe with the dig command, and check if the answers are really always coming from the DNS server, not from a cache.

    In your code, you might want to enable a timeout, e.g.:

    dns.timeouts = 3