Scala actors design patterns - part 1

 This post is one of (hopefully) a series of design patterns for actor based programming. If you don’t know what an actor based programming is, see http://en.wikipedia.org/wiki/Actor_model. This post and the next will be focused on developing in Scala, using its standard actors library.

Many times, working with actors involves a request-response cycle. For the context of this post, imagine a UniversityRegistry actor. The actor can register students, assign them to classes, whatever. One of the things we may want in such a system is to query for the state of a student. To do that, we can send a message Query(id) and expect a response with the student details.

 

What should this response look like? A naïve approach is to send a response of the form Response(student) to the sender, where the student object is an instance of the Student class used internally. This is wrong. The actors model is based on a share-nothing paradigm. Once you send an internal object outside, it might be access by different threads simultaneously, resulting in errors.

 

Another approach is to clone the object before sending it. This may have performance issues. Think of this student referencing to different course objects, which themselves reference other courses.

 

We can create a transfer object that represents the internal objects in a way good for passing outside. A student transfer object (StudentTO) may not point directly to other objects, but instead contain their ids. If a client wants, he should query the system for any referenced object. This means a much more complex system to design, and also can lead to many queries. One can design a query language to treat actors like databases, but this is even more complex.

 

Another problem with all these issues is the need for a client to wait on a message specified by receiver. Imagine a worker interacting with several actor subsystems, needing to wait on each Response type of message. Not very nice.

 

I want to propose a solution that is lightweight and avoids the above problems.

 

The approach is simple. Instead of replying in a way defined by the actor, allow the client to pass in a function, that is executed in the context of the actor on the reply, extracts any information it requires and constructs a message to be sent to the client.

In pseudo code this looks like:

 

case class Student(...)
case class Query
(id: String, f: Student => Any)  

class StudentsRegistry extends Actor 
{
  override def act 
= {
    loop 
{
      react 
{
        case Query
(id, f) => 
          val r 
= getResults(id)
          sender 
! f(r)
      
}
    
}
  
}
}

 

There are two variations to this idea:

  • Optional messages – Sometimes the work the actor does is not a pure query. E.g. registering a user produces an ID which the client might not care about. The passed in function can then be required to return Option[Any]. If it returns None, then no message is sent back
  • Callbacks – a subsystem can provide an observers mechanism. Clients can pass in partial functions (pattern matches) which are called with the different events. If the function is not defined for an event, then no message is sent. This can be combined with the above variation.

 

Developer