Articles Feed

Authors

Categories

Erlang and the OCP

by: eric | May 24th, 2008 | 0 comments »

OCP was defined in 1988 in Bertrand Meyer’s book “Object Oriented Software Construction” as follows:

Modules should be both open (for extension and adaptation) and closed (to avoid modification that affect clients)

Recently we had an issue in my office with our system which uses Rinda (a Ruby implentation of Linda) as a blackboard architecture. The architecture looks a little like this crude drawing.

Rindapic

This hides the OCP violation. The “rindlet,” our informal name for these subprocesses, actually removes the message from the blackboard. So if you send a message that looked like this [1, 2, 3] and one rindlet took it then no other rindlets can receive it. To repeat myself, this meant all messages had to have as their first data member the name of the rindlet they were sending the message too. This is bad. Adding another rindlet meant changing the client sending the message. Now to quote Agile Software Development: Principles, Patterns, and Practices [Martin 2003] An axis of change is an axis of change only if the changes actually occur. That is to say if only one rindlet cared about a message than this wouldn’t be an issue. When this became a problem was when more than one rindlet wanted the same message. Suddenly we have to send many otherwise identical messages to different rindlets. This is the heart of the OCP, the Server (in this case the one sending the messages), should be closed for implementation, but is not. In our case we changed the architecture to shield the server from those changes, and fixed the issue.

This brings me to Erlang and its message passing system. Last year I began playing with it, but lost interest as the difficulty of the syntax simply ruined the fun for me. I do know enough about it that when this issue came up in our ruby system, I was immediately reminded of Erlang’s message passing system. Specifically it has three methods [Armstrong 2007 pg. 134]:

spawn - Create a new concurrent process that evaluates a passed in Fun. The new process runs in parallel with the creating process.

Pid ! Message - Send a message to a Pid - returned by the previous spawn.

receive…end Receives a message that has been sent to a process.

On a cursory glance it appears we have the same issue since you must know the Pid of the process. Aha, but the command sending a message to the process actually created the process, and therein lies the difference. The blackboard architecture deliberately decouples the constantly running processes from messages, the messages are sent to the blackboard. The sender has no right to know about the constantly running process, and should not need to change to add another one. Erlang on the other hand spawns processes as if they were objects, and sends them messages. Sending messages, heck that’s what you do with an object too isn’t it? I don’t think creating an object and sending it messages is a violation of OCP, and neither is this. The sender can be extended without effecting clients. It turns out that Erlang does not intrinsically violate the OPC like I’d originally surmised, although like any other system I could easily create an architecture that did.

Rinda and setting up Rindlets

by: paul | April 16th, 2008 | 1 comments »

Jim Suchy recently laid down some basics of Rinda in his blog Rinda 101. I would like to build on that and talk a little about the rindlet architecture. A rindlet is some process that is listening to the tuplespace, waiting to read or take messages.

When a tuple is written to the tuplespace, the rindlet will look at the message and determine if this is a tuple of interest to it. If it is, then the engines warm up and the tuple gets processed by the logic in the rindlet. Otherwise, the rindlet will take a pass, and wait for another message to be written to the tuplespace.

These rindlets are autonomous and asynchronous pieces of business logic that are messaging across many systems, or across many modules of the same system. We deploy them as daemon processes.

As a proof of concept, Jim and I built a trivia game, with two different interfaces. One will be a rich client, developed using a ruby framework called limelight, and one will be a command line ruby client.

Lets look at the code in the rich client application which updates the question on a screen for all the trivia participants to answer.

require 'rinda_client'

module CurrentQuestion

  def start_update_question
    Thread.new(self) do |current_question|
      while true
        update_question(current_question)
        sleep 1
      end
    end
  end

  def question_update(current_question)
    client = Rinda::RindaClient.new
    tuple = client.read ["questioner", "response", "current question"], 1

    unless tuple.nil?
      current_question.text = tuple[3] 
      current_question.update
    end

  end

end

This code spawns a thread to sit and listen to the rinda server to see if there are any new questions. The questioner rindlet will post a tuple called “current question” every 30 seconds to change the question. After we create a rinda client, we set up the match criteria for the tuples we are interested in.

tuple = client.read ["questioner", "response", "current question"], 1

We want to read all tuples that match

 
["questioner", "response", "current question"]
current_question.text = tuple[3] 

The fourth parameter, which is the question text to display on the screen.

This example shows you can integrate your application with rinda. Your application can listen to the tuplespace to get messages that are relevant only to it. If you are writing a rails application, then you would have to use the view helper periodically call remote, since rails is single threaded, it isn’t as easy as firing a thread and moving on.

Lets start with some rindlet code.

require "rindlet"

class QuestionerRindlet < Rinda::Rindlet

  def run
    with_standard_tuple("questioner", "next question") do |tuple|
      game = Game.active_game

      question_text = nil
      if game.nil?
        question_text = "No active game!"
      else
        question = game.next_question
        question_text = question.nil? ? "No more questions!" : question.text
      end

      @rinda_client.write(["questioner", "response", "current question", "#{question_text}"])
    end
  end

end

First, the withstandardtuple method is a standard wrapper to match elements and take the tuple if it matches and pass it into the block. Alternatively, you could do:

tuple = client.take [“questioner”, “request”, “next question”]

The rindlet itself then gets the next question from the game, and writes a tuple back to the tuplespace with a response, containing the question text. Notice the code in this rindlet feels a lot like controller code in the MVC pattern. Since rinda is a technology and notation of communication, it will just call the business logic in the models and respond to the actions performed if needed. Rindlets often behave as system level controllers, not specific to any one application.

I have had lots of fun getting rindlets to work, and they have been an interesting tool for decoupling business logic from any specific application. Happy coding!

Note: I will soon be releasing a rinda gem with the rinda server and the base rindlet class, amongst some of its functionality.

Rinda 101

by: jim | February 11th, 2008 | 2 comments »

Background

When building a software system composed of multiple decoupled components, the need typically arises for interprocess coordination and communication. As an example, say that we have a customer requirement that allows emails to be sent based on certain events or conditions that occur while the user is interacting with a web application. We build an email component that takes care of the details of actually sending of the email. The web application could use the email component directly, but that would introduce a dependency that we’d rather avoid. How can the two processes communicate without creating a dependency between the two?

If you’re using Ruby, there’s an option that is built right into the standard library. The Rinda module implements the Linda distributed computing paradigm in Ruby. That’s great, but what is Linda? Linda is a coordination language developed by a couple of Yale researchers in the early 1990s. Linda itself is built on the concept of a tuplespace. A tuple is an ordered list of objects, and a tuplespace contains a set of tuples which can be accessed concurrently. A basic set of operations were defined by the language to allow reads, writes and takes.

Getting back to the example, how can Rinda be utilized to enable communication between the web application and the email component? First, a tuplespace will need to be setup that both components can access. When a condition arises within the web application that requires an email to be sent on its behalf, a specific tuple will be written to the tuplespace. The email component is looking for tuples that match a specific pattern. When it finds the tuple written by the web application that matches this pattern, it processes the request and reports back to the tuplespace when finished. In this way, we end up with only data coupling between the two processes, which is a much looser coupling than a direct dependency. This type of approach bears some resemblance to a blackboard system.

Now that we know a little bit about what Rinda can buy us, let’s dive in and try it out.

Hello World

Let’s put together a simple server and client to demonstrate how each works.

Here’s a simple server (server.rb):

  1. require "rinda/tuplespace"
  2. port = 4000
  3. ts = Rinda::TupleSpace.new
  4. DRb.start_service("druby://:#{port}", ts)
  5. puts "Rinda listening on #{DRb.uri}..."
  6. DRb.thread.join

We start by setting the default port to 4000. We then create a new TupleSpace object. The call to start_service starts a local dRuby server listening on port 4000. Passing the TupleSpace as the second parameter sets the server’s front object to the TupleSpace. The URI of the server is output, and the program waits for a kill signal, and we have a Rinda server ready to be accessed by a client. To start the server:

$ ruby server.rb

Here’s a simple interactive client (client.rb). It allows input to be gathered on standard input, with the form .

  1. require "rinda/rinda"
  2. include Rinda
  3. port = 4000
  4. ts = DRbObject.new(nil, "druby://:#{port}")
  5. while message = gets
  6. begin
  7. args = message.split(" ")
  8. method = args.shift.to_sym
  9. topic = args.shift.to_sym
  10. message = args.shift
  11. message = nil if message == "nil"
  12. tuple = [topic, message]
  13. puts ts.send(method, tuple)
  14. rescue Exception => e
  15. puts e.message
  16. end
  17. end

We’re using the send method in order to invoke the method specified on standard input. There’s also a conversion between nil as a string into nil proper. This is necessary because of wildcards, which we’ll get to in a minute. With the server running, run the client to start a new interactive session:

$ ruby client.rb

Below is a sample session (output is indented for clarity):

write message hello Rinda::TupleEntry:0x4f998 write message world Rinda::TupleEntry:0x4cfa4 read message nil message hello take message nil message hello take message nil message world

The inputs above translate to these method calls:

ts.write([:message, "hello"]) => Rinda::TupleEntry:0x4f998 ts.write([:message, "world"]) => Rinda::TupleEntry:0x4cfa4 ts.read([:message, nil]) => "hello" ts.take([:message, nil]) => "hello" ts.take([:message, nil]) => "world"

Note that reads do not remove the tuple from the tuplespace. The tuple is left untouched such that other clients can access the same data. This is an important concept when dealing with synchronization that we’ll return to in a future post.

The structure of the patterns used in the read and take methods deserve some additional attention. When the tuplespace is queried through a read or a take, there are two criteria used to determine if a tuple matches. First, the length of the tuple must match the pattern’s length. In the example above, all patterns are pairs (2-tuples), so only tuples that are pairs will qualify. Second, the tuple must match the pattern specified. When specifying a pattern, nil is used as a wildcard, which will match anything.

Assume that our tuplespace has the following tuples:

1) [:message] 2) [:message, "hello"] 3) [:message, "hello", "world"]

If the pattern [nil] is specified, e.g. ts.read([nil]), only #1 will match.

If the pattern [:message, nil] is specified, only #2 will match. Of course, [:message, “hello”] would also result in #2 matching as well, while [:message, “world”] would not.

Using [:message, nil, nil], [:message, “hello”, nil], or [:message, nil, “world”] would all result in #3 matching. So it is important to remember that patterns are matched on both length and content of the tuple.

Conclusion

We haven’t even scratched the surface of what can be done with Rinda. This introduction only gets the simplest client and server talking to each other. Stay tuned for more.

###