Akka Typed: Stateful to Stateless

Reading Time: 4 minutes

This blog is for people getting started with Akka typed actors. We’ll look at how we can keep state inside an actor in a few different ways. Cover the code from stateful to stateless.

Introduction

Typed Actors consist of 2 “parts”, a public interface and an implementation. If you’ve done any work in “enterprise” Java, this will be very familiar to you. As with normal Actors you have an external API that will delegate method calls asynchronously to a private instance of the implementation.

The advantage of Typed Actors vs. Actors is that TypedActors you have a static contract, and don’t need to define your own messages, the downside is that it places some limitations on what you can do and what you can’t.

Setup

This blog will assume you have the Akka Typed library set up in your project.

val akkaVersion = "2.6.10"

libraryDependencies += "com.typesafe.akka" %% "akka-actor-typed" % akkaVersion

Background

This blog assumes you know the principles of Akka actors. In short:

  • Standard multithreading/parallel applications are a pain to write because of concurrency issues
  • In Akka, we design applications in terms of actors
  • An actor is an object whose state we cannot access directly. But we can only interact with it via asynchronous messages
  • Message passing and handling eliminates the need for us to manage threads & concurrency, while making it easy to write massively distributed systems

An actor is described by its behavior, which (among other things) is responsible for handling the messages that the actor can receive. After each message, the actor’s behavior can change. Given new information, the actor might change the way it handles future messages – much like us humans in real life.

An important part of an actor is the potential data it might hold – we call that its state. As a reaction to an incoming message, the data held inside the actor might change.

As an aside, this actor model embodies the real encapsulation principle: you can never access the internal state of the actor. We can never call some getState() method on it, but we can only interact with it via asynchronous messages. This is what object-oriented programming was supposed to be (search for “messages”). For some reason, OOP took a different turn… but I digress.

A Stateful Emotional Actor

For this example, we’ll write an actor that reacts to external messages from the world by changing its happiness level, originally starting at zero. Let’s assume we have a few message types to send to this actor:

trait DemoExample
case object LearnScala extends DemoEXample
case object LearnJava extends DemoExample
case object LearnAkka extends DemoExample

We can then define an actor with a mutable piece of data (state) as follows:

import akka.actor.typed.Behavior
import akka.actor.typed.scaladsl.Behaviors

val emotionalMutableActor: Behavior[SimpleThing] = Behaviors.setup { context =>
    // define internal state
    var happiness = 0

    Behaviors.receiveMessage {
      case LearnScala =>
        context.log.info(s"($happiness) Learning Scala, getting a amazing!")
        // change internal state
        happiness += 1
        // new behavior for future messages
        Behaviors.same
      // similar cases for the other messages
      case LearnJava =>
        context.log.info(s"($happiness) Doing Coding...")
        happiness -= 2
        Behaviors.same
      case LearnAkka =>
        context.log.info(s"($happiness) Learning Akka, looking good!")
        happiness += 100
        Behaviors.same
      case _ =>
        context.log.warn(s"($happiness) Received something i don't know")
        Behaviors.same
    }
  }

In order to use mutable state, we create this behavior using Behaviors.setup, which allows you to allocate resources at the moment of instantiation, before any messages can arrive to this actor.

If we want to test this actor, all we have to do is back it up by an ActorSystem and then fire a few messages to see how it does:

  def demoActorWithState(): Unit = {
    val emotionalActorSystem = ActorSystem(emotionalMutableActor, "EmotionalSystem")

    emotionalActorSystem ! LearnScala
    emotionalActorSystem ! LearnScala
    emotionalActorSystem ! LearnJava
    emotionalActorSystem ! LearnAkka

    Thread.sleep(1000)
    emotionalActorSystem.terminate()
  }

So we see that with every new message, our actor modified its internal state.

Mutable variables are generally fine inside an actor, because handling a message is thread-safe*. So we can safely change our variable without worrying that some other thread might race to change or read that variable at the same time.

* except Future callbacks inside actors, which will be a discussion for another time…

A Stateless Emotional Actor

But in pure Scala we hate variables and anything mutable. So in this part, I’ll show you how we can write the same actor without needing a variable. Instead of a variable, we’ll use a method taking an Int argument and returning a Behavior instance:

def emotionalFunctionalActor(happiness: Int = 0): Behavior[LearnScala] = Behaviors.receive { (context, message) =>
  // handle message here
}

Notice that we moved our mutable variable as a method argument, and because we don’t have a variable to initialize, we don’t need Behaviors.setup anymore, so we can directly use Behaviors.receive. Inside the block, all we have to do is run a pattern match on the message and do something similar to what we did earlier. However, this time, we aren’t returning Behaviors.same on every branch, but rather a new behavior obtained by calling emotionalFunctionalActor with a new value for happiness:

  def emotionalFunctionalActor(happiness: Int = 0): Behavior[SimpleThing] = Behaviors.receive { (context, message) =>
    message match {
      case LearnScala =>
        context.log.info(s"($happiness)Learning Scala, getting a amazing!")
        // change internal state
        emotionalFunctionalActor(happiness + 1)
      case LearnJava =>
        context.log.info(s"($happiness)Doing Coding...")
        emotionalFunctionalActor(happiness - 2)
      case LearnAkka =>
        context.log.info(s"($happiness) Learning Akka, yes!!")
        emotionalFunctionalActor(happiness + 100)
      case _ =>
        context.log.warn("Received something i don't know")
        Behaviors.same
    }
  }

Sure enough, if we change our test method to use this new emotionalFunctionalActor instead, the logged output will look the same:

How to Turn a Stateful Actor into Stateless

Here are some steps to turn a stateful actor — with variables or mutable pieces of data — into a “stateless” actor:

  1. Create your actor behavior as a method. The arguments of the method will be immutable versions of the pieces of data you used to hold.
  2. If you created your stateful actor with Behaviors.setup, you’ll probably no longer need it — use Behaviors.receive or Behaviors.receiveMessage.
  3. Most of the time, stateful actors keep the same behavior after the reception of a message — see earlier case where we returned Behaviors.same every time. This time, with every message reception, you’ll change the behavior to a new method call with new arguments and depending on the data you need to change.

Conclusion

We’ve seen how we can create stateful actors in Akka Typed. How a “stateless” actor looks like, and how to turn mutable state into method arguments in a stateless actor version. I hope this is useful, and that you’ll create more functional-style/”stateless” actors after this blog.

References

https://doc.akka.io/docs/akka/2.5/typed-actors.html

Written by 

Meenakshi Goyal is a Software Consultant and started her career in an environment and organization where her skills are challenged each day, resulting in ample learning and growth opportunities. Proficient in Scala, Akka, Akka HTTP , JAVA. Passionate about implementing and launching new projects. Ability to translate business requirements into technical solutions. Her hobbies are traveling and dancing.

Discover more from Knoldus Blogs

Subscribe now to keep reading and get access to the full archive.

Continue reading