Borrowing a Service with Distribunaut
As you probably remember from Chapter 2, when we retrieve Tuples from the RingServer, we have two choices. We can either read the Tuple or take the Tuple. The former leaves the Tuple in the RingServer for others to access simultaneously. The latter removes the Tuple from the RingServer; as a consequence, no one else can gain access to that Tuple.
So what happens when we access a service using Distribunaut? Are we doing a read or a take from the RingServer? Distribunaut does a read from the RingServer, thereby allowing others to access the same service at the same time.
Most of the time this is the exact behavior you want. You usually want to be a good citizen and let others access the service you are accessing as well. Sometimes, however, you might need to grab hold of a service exclusively, do a few things with that service, and then return it to the RingServer for others to have access to.
So how do we take a service from the RingServer, use that service, and then return it for wider use? We could use raw Rinda and DRb code, but that would be ugly, and prone to error should any of the underpinnings of Distribunaut change. Instead, Distribunaut offers the concept of borrowing a service.
To demonstrate how to borrow a service, let's use our simple HelloWorld class as the service we want to borrow:
require 'rubygems' require 'distribunaut' configatron.distribunaut.app_name = :hello_world_app class HelloWorld include Distribunaut::Distributable def say_hi "Hello, World!" end end DRb.thread.join
Here is what our client code would look like to borrow the HelloWorld service:
require 'rubygems' require 'distribunaut' Distribunaut::Distributed::HelloWorld.borrow do |hw_class| hw = hw_class.new puts hw.say_hi begin hw = Distribunaut::Distributed::HelloWorld.new rescue Rinda::RequestExpiredError => e puts "Oops - We couldn't find the HelloWorld class!" end end hw = Distribunaut::Distributed::HelloWorld.new puts hw.say_hi
If we were to run this code, we should see the following printed:
Hello, World! Oops - We couldn't find the HelloWorld class! Hello, World!
So exactly what did we do, and how did it work? The borrow method takes a block and yields a reference to our proxy service, as we discussed earlier in this chapter. This works the same way as if we had called Distribunaut::Distributed::HelloWorld directly. The difference is, before the block gets executed, the service is located and removed from the RingServer. It is then "locked" and placed back into the RingServer in such a way that others can't retrieve it. After the block finishes executing, the service is unlocked in the RingServer and is available again for public consumption.
If we look at what is happening in the block, we see that we call the new method on the hw_class variable, which is the reference to the HelloWorld service. The new method creates a new instance of the HelloWorld class, and we can call the say_hi method on it.
To demonstrate that we can't access the HelloWorld service directly, we attempt to call it, but, as we see, it raises a Rinda::RequestExpiredError because the service cannot be found.
After the block has finished executing, we again try to access the HelloWorld service as we would normally. This time, instead of an exception, we are greeted with a pleasant "Hello, World!".
As you can see, the concept of borrowing a service allows us to easily take control of the service we want, do the work we need to do on that service, and then have it automatically returned to the RingServer for others to use. It also has the added benefit of being quite easy to code. We don't have to write fragile code that takes the service from the RingServer, handles exceptions that may arise, and ensures that the service gets placed back into the RingServer correctly.