Local resource available in the wild, thanks to DRb

Ruby 3 Comments »

You have a resource that you want to share between multiple processes, and it could be a resource persited on the local hard drive, like an index, a persitent hash (Berkeley DB, InfinitiyDB), or simply a file.

With DRb, aka Distributed Ruby, you can share a resource via TCP. DRb will do the annoying job for you: marshalling. And that is COOL, and RMI is NOT COOL.

As usual in Ruby, using a library is as simple as calling the require method. To use DRb in your application, write this:

require 'drb'

In this post, we’ll implement a Remote Hash. It will be accessible to an unlimited number of processes on an unlimited number of computers. Let’s code a simple DRb server for your resource.

class Server
  def start
    print "starting Ferret servers..."
    DRb.start_service("druby://localhost:7000", HashProxy.new)
    puts " done"
  end
 
  def join
    DRb.thread.join
  end
 
  def shutdown
    print "stopping Ferret servers..."
    DRb.stop_service
    puts " done"
  end
end
 
s = Server.new
s.start
trap("INT") {s.shutdown} # Catch CTRL-C to do a clean shutdown of the DRb server
s.join

The instance of HashProxy will be the distributed object between the DRb server and the DRb clients. We call it “proxy” because it will exactly have the same behaviour as the real resource hidden behind it. This is where the method_missing magic happen.

class HashProxy
  def initialize *args
    @local_resource = Hash.new *args
  end
 
  def method_missing(name, *args, &block)
    @local_resource.__send__(name, *args, &block)
  end
end

The Object.__send__ method is an alias to Object.send, to avoid conflics with a possibly existing method named send in the current object or its superclasses or included modules.

There is one problem with this implementation, DRb will, like a web server, handle client requests simultaneously. We have to protect our hash thanks to a mutex. Every clients will have to wait in line to access the remote resource.

require 'thread'
 
class HashProxy
  def initialize *args
    @mutex = Mutex.new
    @local_resource = Hash.new *args
  end
 
  def method_missing(name, *args, &block)
    @mutex.synchronize do
      @local_resource.__send__(name, *args, &block)
    end
  end
end

As we said earlier, each method of HashProxy, and so each method of Hash, is now available to any remote Ruby code, using the HashProxy class, instead of Hash:

class RemoteHash
  def initialize
    @hash_proxy = DRbObject.new(nil,"druby://localhost:7000")
  end
 
  def method_missing(name, *args, &block)
    @hash_proxy.__send__(name, *args, &block)
  end
end

In your application, you’ll use your remote resource like a local resource, without knowing about those network and marshalling things.

h = RemoteHash.new
h[:roger] = 1
h[:moore] = -1
p h[:roger] # => 1

Unfortunately, I couldn’t call methods with blocks.

h.select {|k,v| v > 0}
# =>
# ArgumentError: wrong number of arguments (0 for 1)
# 
# method select at line 9
# method __send__ at line 9
# method method_missing at line 9
# at top level  at line 17
# Program exited.
 
p h.sort {|a,b| a[1]<=>b[1]}
# =>
# DRb::DRbConnError: DRb::DRbServerNotFound
# 
# method current_server in drb.rb at line 1650
# method to_id  in drb.rb at line 1712
# method initialize in drb.rb at line 1048
# method new  in drb.rb at line 642
# method make_proxy in drb.rb at line 642
# method dump in drb.rb at line 559
# method send_request in drb.rb at line 605
# method send_request in drb.rb at line 906
# method send_message in drb.rb at line 1194
# method method_missing in drb.rb at line 1086
# method open in drb.rb at line 1170
# method method_missing in drb.rb at line 1085
# method with_friend  in drb.rb at line 1103
# method method_missing in drb.rb at line 1084
# method __send__ at line 9
# method method_missing at line 9
# at top level  at line 18
# Program exited.

One last word, about method_missing, Jay Field wrote an excellent article about dynamically defining the methods of an external class, instead of using method_missing. It will surely help you debugging your piece of art.

WP Theme & Icons by N.Design Studio
Entries RSS Log in