Akka Actors: Best Practices

Actors are components of message-passing systems that are particularly popular these days. They make concurrency a lot easier to understand compared to the traditional thread-monitor-lock model.

An actor is a thread-like program that run independently from other actors. It has a mailbox to receive messages from other actors, where each message represents a task to complete. Although actors are much easier understand, we still need to pay attention on how to use them. Following is a list of golden rules when using actors:

Never block

Actors should never be blocked to wait for some result to come back. Instead of waiting, the time could be used to process other messages. Moreover, when an actor is blocked, it will lose requests/messages from other actors and the system could risk to collapse due to deadlocks. If blocking cannot be avoided, delegate to special actors that are dedicated just to the specific blocking tasks.

Communicate only via messages

The idea behind the message-passing system is to keep all the shared data within the message. If the communication doesn’t happen through messages, we risk to introduce some extra shared data that need to be protected by locks. A part from the performance loss, what’s the point of using the actor model if we still have to deal with locks?

Messages should be immutable

As mentioned before, the messages contain all the shared data between the actors. Having immutable messages guarantees that the shared data is thread-safe. This can be easily achieved by using case classes for your messages: not only the immutability is guaranteed, but pattern matching can be easily used to identify the type of your message.

Messages should be complete and self-contained

When receiving a message, an actor should have all the information needed to process it. This reduces dependencies between actors and it allows an actor not to have any memory/history of when/why a specific request was made. Don’t be scared of adding some redundancy to your messages if it helps to improve your system performance.

If you try to follow these simple rules, your concurrent challenges will be a lot easier to tackle — and the system more performant!

Type Parameterisation

The following article provides a list of all the different type parameterisations available in Scala and how to use them.

Variance

A class can depend on a type constructor. The following is an example trait that depends on a generic type T:

trait MyExample[T] {...}

The above declaration implies that the generic type T is the only valid parameter for MyExample: this is called nonvariance.

Let’s assume we would like our trait to be compatible with other types, as long as this types are subclasses of T. This can be expressed as following:

trait MyExample[+T] {...}

This concept is called covariance: Given a type U subtype of T, a class MyClass is called covariant if MyClass[U] is a subtype of MyClass[T].

What if we want to achieve the opposite? In other words, we would like our trait to be compatible with any type as long as they are superclass of type T:

trait MyExample[-T] {...}

This concept is called contravariance: Given a type S supertype of T, a class MyClass is called contravariant if MyClass[S] is a subtype of MyClass[T].

Bounds

Compatibility between classes is not the only thing we can achieve. For example, we would like our trait MyExample to accept all the types that are subclasses of T. This can be expressed using the lower bound annotation:

trait MyExample[U >: T] {...}

The upper bound annotation can be used in a similar way to let our MyExample trait accept all the types that are superclasses of T:

trait MyExample[S <: T] {...}

Last but not least, let’s assume we want our trait to accept any type as long as convertible to T. This can be achieved using the view bound annotation:

trait MyExample[V <%: T] {...}

Thank you M. Odersky for implicit conversions!

Implicits: Rules and Applications

Scala implicits are a great tool to remove code duplication and convert objects from different domains. However, we need to learn not to abuse them: by hiding code they can make the code more concise but more cryptic at the same time.

In order to use implicits efficiently, we need to know how the compiler tries to search and apply them:
Making rule: an implicit needs to be identified by the implicit keyword.
Scope rule: an implicit needs to be in scope as single identifier: it has to be accessible directly (i.e.: without accessing any container object).
One-at-a-time rule: the compiler will try to apply only one implicit at the time.
Explicits-First rule: if the expression compiles as it is, no implicits will be applied.
More-Specific-First rule: if there is more than one implicit conversion that could work, the one with the most restrictive type is chosen.

Implicits can be applied in three case scenarios only, for the sake of both the compiler and the poor developer that needs to understand what our code is doing.

Implicit conversion to an expected type

scala> case class Message(msg: String)
defined class Message

scala> implicit def intToMessage(n: Int) = Message(s"I am number $n")
intToMessage: (n: Int)Message

scala> val myMessage: Message = 1
myMessage: Message = Message(I am number 1)

Converting the receiver

scala> case class Message(msg: String) {
     | def talk() = println(msg)
     | }
defined class Message

scala> implicit def stringToMessage(text: String) = Message(text)
stringToMessage: (text: String)Message

scala> "hello".talk()
hello

Implicit parameters

scala> implicit val defaultMsg = "hi dude"
defaultMsg: String = hi dude

scala> def talk(implicit msg: String) = println(msg)
talk: (implicit msg: String)Unit

scala> talk
hi dude

scala> talk("this is an explicit message!")
this is an explicit message!

RESTful now more important than ever.

REST APIs are becoming more and more popular: they allow horizontal scalability, they are flexible to change and easy to use without the need of detailed documentation.
However, some APIs claim to be RESTful when they are not. This is mainly for two reasons:
– We don’t always have a clear idea of what RESTful means, we just associate it to the HTTP protocol standards.
– Building a pure RESTful API can be really challenging.

What are the features of a REST API? What are the benefits of developing a pure REST API?

Uniformity

Not only it needs to use the standard HTTP methods, but it needs to use them with their standard meaning.
By definition, an operation is idempotent if it always have the same effect whether applied once or multiple times. The following HTTP methods should always be idempotent:
GET: when retrieving existing information without altering the status of our system.
PUT: when updating/adding to an existing resource. Note that by applying this operation more than once, we shouldn’t create any side effect.
DELETE: when deleting a resource. If we try to remove a resource that doesn’t exist or that it has already been deleted, this shouldn’t cause a different behaviour of the API.
HEAD: same as GET, but with an empty body. This is usually used to retrieve metadata about large data without actually retrieving it.
OPTIONS: when retrieving information about what the methods are allowed by the URI.

There is only one HTTP method that is non-idempotent:
– POST: when creating a new resource. This is not idempotent as we shouldn’t be able to create a resource that already exists.

Also, our return codes should respect the HTTP standards. The most used ones:
200 means “OK”
204 means: “OK” with empty body
400 means “Bad Request”
404 means “Resource not found”
500 means “Internal Error”

For a list of all HTTP code status see  http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html.

Adopting HTTP standards allows us to safely depend on and use the API: we will know that when using an idempotent method, we can safely repeat the operation if needed. On the other side, when POSTing, we are aware that this can cause side effects that we need to take care of.

Addressability

Each resource should be uniquely identified by a URI: every time a resource is created it needs to have an address that identifies it.
Addressability is also about readability and predictability: not only the URI should be informative on what the resource is, but it should also be easy to remember and consistent with the other URIs we have created so far.
There are a lot of different opinions on how to build efficient URIs (e.g.: http://www.restapitutorial.com/lessons/restfulresourcenaming.html), chose one convention and stick to it! This will make your API a lot easier to use and our clients a lot happier!

Connectedness

Resources need to be linked together…literally!
While a SOAP API uses a WSDL to define the communication between client and service, a REST API doesn’t have such a technical precise definition of what it can do: as a client we should learn how to use the API by using it, we shouldn’t necessarily rely on any official documentation. One way of achieving this is by using hypermedia to link resources together. For example, if we return the information about a customer we could provide the URI to retrieve all his accounts. We could also prove URIs that provide and explain specific values (e.g.: enums, query params, etc) accepted/returned by that specific operation.

Statelessness

Statuses should never be maintained by the system: they should either be provided by the client or stored in a database. Although this seems to be a small detail, this allows our service horizontally: when our service in under lots of pressure due to high demand, we just need to run a new node of the service to increase our computational power. This operation is completely seamless to our clients because API calls are not influenced by previous calls (i.e.: they are stateless). Another advantage is that stateless calls are also independent between each other: it will allow us to parallelize some calls without worrying too much about side effects.

Stateful objects: use them to lie!

Sometimes we want to hide/protect our variables — the classic concept of encapsulation in object oriented programming.
We’d like to write a Scala class to manage someone’s age. In particular, we’d like to:
– lie on our age if we are not teens anymore (you never know!)
– put some validation to avoid negative age values

Our first try is following:

package girlcoding.tutorial

class MyAge {

  var age: Int = _

  override def toString = s"I am $age years old"
}

This compiles, but it doesn’t respect the given requirements!

    val myAge = new MyAge
    myAge.age = 30
    println(myAge); // it prints: "I am 30 years old"
    // it should be lying here!

    myAge.age = -110
    println(myAge); // it prints: "I am -110 years old"
    // it should not be possible!

After some research, we discover that our so needed setters and getters are actually just hidden! In fact, the compiler transforms MyAge code into the following:

package girlcoding.tutorial

class MyAge {

  private[this] var a = _
  
  def age: Int = a // getter
  
  def age_= (x: Int) { a = x } // setter

  override def toString = s"I am $a years old"
}

Let’s use this information to our advantage and deliver our requirements:

package girlcoding.tutorial

class MyAge {
  private[this] var realAge: Int = _

  def age = {
    if (realAge > 19) realAge - 5
    else realAge
  }

  def age_= (a: Int) {
    require(a <= 0, "Age cannot be negative!")
    realAge = a
  }

  override def toString = s"I am $age years old"
}

Note that the variable realAge is completely hidden and it cannot be accessed from outside, so no one will know our little secret! 😉
We can now play with our MyAge class — and lie when needed.

    val myAge = new MyAge
    myAge.age = 10
    println(myAge) // it prints "I am 10 years old"
    // still young, no need to lie...

    myAge.age = 30
    println(myAge); // it prints: "I am 25 years old"
    // lier! myAge.age was actually set to 30

    myAge.age = -110
    // throws java.lang.IllegalArgumentException: 
    // requirement failed: Age cannot be negative!

Super Powers to Qualified Access Modifiers

Scala uses access modifiers quite differently from Java. Let’s see what the differences are and why qualified access modifiers are so powerful in Scala.

default access modifier

Scala >> public
Java >> protected

While in Scala the public keyword doesn’t exist, Java allows you to explicitly use the keyword protected as you please.

private

Scala >> accessible from a class and its companion object
Java >> accessible from a class and its inner classes

 

Scala makes a special link between a class and its companion object, but not with inner classes as they are not accessible with a private modifier.

protected

Scala >> accessible from a class and its subclasses
Java >> accessible from all the classes in the same package

Scala is quite restrictive here 😐 (but don’t worry too much, keep reading…)

public

Scala and Java >> accessible from everywhere

Finally something that works in the same way! 😀

So far, we could think that the access modifier system in Scala is more limited than the Java one. Actually, it is a lot more powerful than the Java one, thanks to the super powers of qualified access modifiers!

private[X] and protected[X]

By adding qualifiers to an access modifier we can redefine its visibility. In the following definitions, X can be a package, an object or a class:

private[X] >> private and accessible from X
protected[X] >> protected and accessible from X

We can use this technique to obtain exactly the same meaning of the Java access modifiers:

– Java private member in class MyClass becomes Scala private[MyClass] member
– Java protected member in package girlcoding becomes Scala protected[girlcoding] member

private[this]

We can also declare a member as object-private:

private[this] >> accessible only for the instance that contains the definition/field

This makes sure that the member will not be visible to other objects of the same class…it’s like a super private modifier! 😀

Shared package code? Fear no more!

Sometimes code is common to all the classes of a specific package (e.g.: conversions between objects, custom logs, shared operations, etc). Our lovely Java doesn’t seem to have a convenient way of declaring code as part of a package, rather than just a class….but FEAR NO MORE! Let’s see how we can this in Scala using package objects.

First step is to define the shared package code.

// in file girlcoding/tutorial/package.scala
package girlcoding

package object tutorial {
  def customLog(msg: String) {
    println(s"Customised package log: $msg")
  }
}

The compiler will extract and compile the package code into a package.class file: the convention is to be consistent with it and to put its source code in a package.scala file as well — but you are not forced to!

We are now ready to use this shared code in all the girlcoding.tutorial classes:

// in file girlcoding/tutorial/MyClassA.scala
package girlcoding.tutorial

class MyClassA {
  def saySomething(msg: String) = customLog(msg)
}

What if we what to use it in sub packages as well?

// in file girlcoding/tutorial/whatever/MyClassB.scala
package girlcoding.tutorial
package whatever
// package girlcoding.tutorial.whatever doesn’t compile!

class MyClassB {
  def saySomethingElse(msg: String) = customLog(msg)
}

Note that if we use the declaration package girlcoding.tutorial.whatever (see line 4), it doesn’t work: to fix it, we need to declare a dependency on package girlcoding.tutorial as well (see lines 2-3).

This simple main class is checking that everything is working together as expected:

import girlcoding.tutorial.MyClassA
import girlcoding.tutorial.whatever.MyClassB

object MyMain {
  def main(args: Array[String]) {
    val myClassA = new MyClassA
    myClassA.saySomething("Hello from class A")
    // it prints: "Customised package message: Hello from class A"

    val myClassB = new MyClassB
    myClassB.saySomethingElse("Yo, I am way cooler than class A!")
     // it prints: "Customised package message: Yo, I am way cooler than class A!"
  }
}