Archive for the tag 'Introduction'

Access Blackborad With Timeout

Operations that access a blackboard to retrieve information, i.e. operations read and take, have a second parameter that defines how long should this operation wait until there will be a matching n-tuple or, simply, timeout. A value of the parameter specifies number of second.

Example:

  • To read a tuple and if there is no such tuple, to wait 4 seconds for it:
    ts.read [nil, nil], 4
    
  • Or to take a tuple and if there is no such tuple, to wait 1 minute for it:
    ts.take [nil, nil], 60
    

There are two special values that you may use as a timeout value:

  • 0 (zero) - an operation would not wait at all. It is useful for checking, if a n-tuple is stored in the blackboard or not.
  • nil - an operation would wait endlessly. Of course, there is not difference if you would call an operation without a specified timeout value or with nil.
    Actually, to be more precise, an operation would wait 231 - 1 seconds, that is approximately 30 years. ;)

But what would happen, if we specify a timeout value and an n-tuple would not appear on the blackboard in the specified time(out)?
Rinda throws the Rinda::RequestExpiredError exception.

The handling exceptions in Ruby is very comfortable (esp. with the command retry). An example follows, where a tuple is taken with a timeout 5 seconds. This allows to inform an user about the state, that the application is still waiting for a message:

puts "Waiting for a message."
begin
  t = ts.take [:message, nil], 5
rescue Rinda::RequestExpiredError
  puts “Still waiting for a message.”
  retry
end

The timeout functionality allows to:

  • create a responsiveness application - to inform other components or users about its state (e.g. waiting for a message - processing a message).
  • end an application normally - to break waiting for a n-tuple every so often and check if the application should end.
  • check for different n-tuples - it is possible to wait in deferent intervals for different n-tuples. Of course, a better solution is to create for each n-tuple an own thread that performs a blackboard access operation for this particular n-tuple.
  • create an application that does not access a blackboard all the time (e.g. only every hour for 1 minute).

Message and Tuple

In the last posts a term message was used. In the Rinda documentation you would not probably find this term; it is called more accurately: tuple.


What is a tuple?

  • [1, 2] is a tuple
  • [:message, 'Hi there!'] is a tuple
  • ['Hi there!', :message] is also a tuple
  • [:result, 42, 'John', 'Susanne', 'London'] is a tuple
  • [:clock, Time.new] is a tuple too
  • [:service, CalculatorService.new(1, true) is a tuple

Tuple is an ordered list of elements [wikipedia].


A blackboard is a place to share tuples; agents are writing, reading and taking tuples.

If you have a simple system, where agents are using only one kind of messages, it is enough to use a single, e.g. [1], ['Hello']. But what to do, if your system requires to differentiate more types of messages? Well, there is no problem with writing different types of messages, but there is a problem how to find the correct one to read or take it.

Basically there are two possibilities:

  • Every type of a message has different number of elements. The number of elements is the distinguishing sign.

    Example: [1] is similar to [2] or ['Hello'], but it is different then [3, 'Hello'].

  • One of the parameters is an identifier. The number of elements and an identifier are the distinguishing signs.

    Example: [:request, 1, 2] is similar to [:request, 24, 'John'], but it is different to [:result, 42, 'London'].


How are the blackboard operations using tuples?

  • The write operation writes a tuple (specified as a parameter).
    ts.write [:message, 'Hi there!']
    
  • Reading operations read and take use tuples in a slightly different way. It is possible to use the nil value as a wild card for the position, where it is used.
    • To read any single:
      ts.take [nil], 0
      
    • To read any pair:
      ts.take [nil, nil], 0
      
    • To read any :message type:
      ts.take [:message, nil], 0
      
    • To read any pair, where the second element has value 4 (not very practical example :):
      ts.take [nil, 4], 0
      
    • To read exactly the tuple [:result, 4]:
      ts.take [:result, 4], 0
      

    The second parameter says, how long (in seconds) should a read operation wait until a tuple appears on the blackboard. Zero means to not to wait at all.

Examples and Howtos

To follow the nice Ruby on Rails motto Show, don’t tell, the majority of future posts will be examples and howtos: how to start environment, simple agent example, an unique ID for an agent, stopping agents with a message, etc

Blackboard Operations

A blackboard system should support three operations: write, take, read.

  • write — adds a message to the blackboard.
  • take — returns a content of a message from the blackboard, but it also removes it from the blackboard. An agent, that performed the take operation, is the only one in the system, that has a content of this message.

    This operations is usually used for messages that start a task. There is no need to let other agents to start the same task.

  • read — returns a content of a message from the blackboard. The message stays there, so other agents can also read a content of this message.

    This is used for messages that describe a state of the whole system — a flag. For example, a flag stop processing should be visible to all agents, so all of them will stop processing.

Multi-agent System in Ruby

To build a (simple) multi-agent system, we need at least three functionalities: communication between processes, discovery service and blackboard.

Let’s explain each functionality and show how it is provided in Ruby:

  • Communication between processes. Agents need to communicate with the rest of the world. Otherwise, it will not be possible to say them, what they should do and we will not know, what they did, because they did not present their results. ;) In the previous post, we said that agents are, from the operating system point of view, processes; technically we need a communication between processes.

    This functionality is provided by the Distributed Ruby library. You can find two abbreviations: DRb, dRuby. Both of them refer to the same library..

  • Discovery service. An agent should be able to automatically find other agents or a blackboard.

    This functionality is provided by the Rinda library, by its class Ring.

  • Blackboard. A blackboard is a shared place for exchanging data. It has to comply with the following important requirement: atomic access. What does it mean? If one agent takes a message, another agent should not be able to take the same message, so it will not happen that two (or more) agents received the same message.

    It is provided by the Rinda library, by its class TupleSpace.

Good news: distributed Ruby and Rinda are included in Ruby 1.8. :)

Ruby on Blackboard

The most usual way how to build an application is to create a monolithic system.

The code inside may vary in complexity - from straightforward, a few conditions, to complex with threads.

The next level of complexity is to spread an application into more processes. Communication between processes is more complicated then between threads and usually there is a special layer dedicated to this, e.g. DRb in Ruby or RMI in Java.
And we are only a short remove from a possibility to communicate between processes on different computers. Often the dedicated layers handle this complexity level too.

But on both sides, there are still monolithic sytems, that are only deployed on different computers.

The dialog between processes looks like this:

A: I want to load data with Process 1 on the computer 192.168.0.45.
B1: No response. It seems that the process is busy.
A: I want to load data with Process 1 on the computer 192.168.0.73.
B2: John, 34 years old, London. Susanne, 25 year old, Stockholm.

What would you say, if a situation would look like this?

A: Writes a message “Please, can you send me data?” on a blackboard.
B1: Is busy, so it does not have time to check the blackboard.
B2: Is available, so it erases the messages (to not to bother others with it) and writes the data on the blackboard - John, 34 years old,

What is the difference?

  • In the first situation the communication is pointed and forced. If the answering process is busy, the requesting process has to wait or find another answering process. It has to act actively until a request is transfered.
  • In the second situation, processing are taking tasks, if they are available. The communication is not pointed and forced. The request process does not have to take care about the posted message.

You probably noticed a special word in the second situation - blackboard. It is a shared place for exchanging messages. It allows to post, check for and delete a message.

Applications A, B1 and B2 are autonomous applications performing a task; usually it is a specialised task, so the application is small and simple. These applications are called agents.

An environment that allows to execute agents and provides various functionality such as communication between agents or coordination is called a multi-agent system. (Sorry for a simplification.)

On this blog, you will find how to create agents, coordinate them, how to solve their problems and how to solve problems with them. Oh, I would forget: in Ruby! :)

Welcome to the Ruby Agent a.k.a. Ruby on Blackboard blog :)