Bug #496 [ruby-core:18381]

DRb.start_service(nil) is very slow

Added by Hongli Lai 570 days ago. Updated 179 days ago.

Status :Open Start :08/25/2008
Priority :Normal Due date :
Assigned to :Masatoshi Seki % Done :

0%

Category :lib
Target version :-
ruby -v :

-


Description

On some systems - such as mine - DRB.start_service(nil) is very slow. This is caused by the fact that DRb.open_server calls TCPServer.open(nil, 0).addr[1]. On my system, this takes about 3 seconds, and during those 3 seconds there is 0% CPU usage.

I suspect it has got something to do with the fact that retrieving the port of a server socket that's bound to 0.0.0.0 is for some unknown reason very slow.

The problem can be fixed by replacing the following line in drb.rb

  uri = 'druby://:0' unless uri

with:

  uri = 'druby://localhost:0' unless uri

This does not violate DRb.start_service's contract, because a nil indicates that it will bind to the default local host name.

The following monkeypatch works around the issue:

  module DRb
    class << self
      alias orig_start_service start_service
    end
    
    def self.start_service(uri = nil, front = nil, config = nil)
      if uri.nil?
        orig_start_service("druby://localhost:0", front, config)
      else
        orig_start_service(uri, front, config)
      end
    end
  end

Related issues

related to Feature #1811 Default BasicSocket.do_not_reverse_lookup to true Closed 07/23/2009

History

08/27/2008 09:56 PM - Brian Candler

I suspect a DNS/hostname resolution problem on your system.

Looking at 1.8.6p114, notice that DRbTCPSocket.getservername first calls Socket.gethostname to get the hostname, then calls Socket.gethostbyname which does both forward and reverse name resolution, presumably to map to a "canonical" name. Then DRbTCPSocket.open_server_inaddr_any also calls Socket.getaddrinfo, although it discards the results apart from using them to decide whether to bind to IPv4, IPv6, or both.

Try the following in irb - how long does each step take?

{{{
require 'socket'
h1 = Socket.gethostname
h2 = Socket.gethostbyname(h1)[0]
Socket.getaddrinfo(h2, nil, Socket::AF_UNSPEC, Socket::SOCK_STREAM, 0, Socket::AI_PASSIVE)
}}}

If one of these steps is slow, you may be able to solve the problem by adding an entry in /etc/resolv.conf to map your hostname to your primary interface IP.

08/27/2008 09:59 PM - Hongli Lai

All of those steps finish instantaneously.

I'm on Ubuntu 8.04.

09/04/2008 08:47 PM - Brian Candler

OK, how about this:

  require 'socket'
  t = TCPServer.new("", nil)
  t.addr
  t.addr

  Socket.do_not_reverse_lookup=true
  t.addr

On my machine, the first 't.addr' invocations take about 5 seconds. The last one is instant. I believe this is because of the delay looking up 0.0.0.0 in the DNS.

09/04/2008 10:25 PM - Hongli Lai

The last t.addr is instant for me as well.

12/31/2008 11:51 PM - Hongli Lai

Any update on this? Is my proposed fix acceptable?

01/01/2009 03:04 AM - Roger Pack

Sounds like the problem is that 
  require 'socket'
  t = TCPServer.new("", nil)
  t.addr

is slow, is that the root of the problem?
-=r

02/03/2009 12:19 PM - Shyouhei Urabe

  • Assigned to set to Masatoshi Seki

09/21/2009 04:11 AM - Marc-Andre Lafortune

  • Category set to lib
  • ruby -v set to -

Also available in: Atom PDF