Thoughts, Tips and Tricks on what I'm currently do for a living. Currently most of my spare time is spent on contributing to Akka.NET.

Sunday, October 26, 2014

Handling messages and state with Akka.NET

In Getting Started with Akka.NET we created a calculator in Akka.NET that was able to add two numbers and return the response.

In this post we’ll add more functionality to the calculator.

Code for this post: https://github.com/HCanber/akka.net-blog-examples/tree/master/02-calculator

All posts in this series: Tutorials for Akka.NET

Note! This post was written using Akka.NET 0.7.0 and might not work for later versions

Receiving more messages

The calculator should be able to subtract two values as well, so create a copy of the Add class and rename it to Subtract

public class Subtract
{
    private readonly double _term1;
    private readonly double _term2;

    public Subtract(double term1, double term2)
    {
        _term1 = term1;
        _term2 = term2;
    }

    public double Term1 { get { return _term1; } }
    public double Term2 { get { return _term2; } }
}

We make the fields readonly to ensure the message is immutable.

Next, we need to tell CalculatorActor how to handle Subtract messages by adding another Receive statement:

public class CalculatorActor : ReceiveActor
{
    public CalculatorActor()
    {
        Receive<Add>(add => Sender.Tell(new Answer(add.Term1 + add.Term2)));
        Receive<Subtract>(sub=> Sender.Tell(new Answer(sub.Term1 - sub.Term2)));
    }
}

You can have as many Receive statements you like, but be aware of that the order is important. The upper takes precedence over the lower. In this case it doesn’t matter, but if we were to insert a Receive<object> first, it would handle ALL messages, and our handlers for Add and Subtract would never be called.

Testing subtraction

We add two more lines to test subtraction:

static void Main(string[] args)
{
    var system = ActorSystem.Create("calculator-system");
    var calculator = system.ActorOf<CalculatorActor>("calculator");
    var answer = calculator.Ask<Answer>(new Add(1, 2)).Result;
    Console.WriteLine("1 + 2 = " + answer.Value);

    var answerSubtract = calculator.Ask<Answer>(new Subtract(5, 3)).Result;
    Console.WriteLine("5 - 3 = " + answerSubtract.Value);

    Console.WriteLine("Press any key to exit");
    Console.ReadKey();
}

Adding state

Let’s add some state to our CalculatorActor. Every time it has performed a calculation it should store the answer. We modify the actor to look like this.

    public class CalculatorActor : ReceiveActor
    {
        public CalculatorActor()
        {
            var answer = 0d;

            Receive<Add>(add =>
            {
                answer = add.Term1 + add.Term2;
                Sender.Tell(new Answer(answer));
            });

            Receive<Subtract>(sub =>
            {
                answer = sub.Term1 - sub.Term2;
                Sender.Tell(new Answer(answer));
            });
        }
    }

Notice that we do not need to declare any fields, we can use locally declared variables.

Concurrency

Suppose we send Add(1,1) and Subtract(555,22) to the actor. The actor start processing Add(1, 1) assign answer=2 and then at the same time Subtract(555,111) calculates and assigns answer=444. When the handler of Add(1,1) creates the reply it will be Answer(444) which is clearly wrong.

How can we make sure this will not happen?

You don’t need to do anything. Akka.Net handles this for you. An actor can only process one message at the time. So if it has started processing Add(1,1) it will not start processing anything else until it has finished processing Add(1,1).

Retrieve state

So how can we get the latest answer from CalculatorActor now that answer is stored in a local variable inside the constructor? Can we make it a public field and then use that to access the value?

No, that will not help! Remember this line when we created the actor:

ActorRef calculator = system.ActorOf<CalculatorActor>("calculator");

ActorOf returns an ActorRef, not a CalculatorActor so we do not have access to public members on CalculatorActor. And that’s a good thing, as doing so would open up for sharing state.

Returning to one of the fundamentals of Actor based systems:

The only way to communicate with an actor is by messages.

So we need to send it a message GetLastAnswer and let it reply with an Answer message.

First the new message. Since it doesn’t store any values, we create it as a singleton:

public class GetLastAnswer
{
    private static readonly GetLastAnswer _instance=new GetLastAnswer();
    private GetLastAnswer() {}
    public static GetLastAnswer Instance { get { return _instance; } }
}

Next we declare what CalculatorActor should do when it receives a GetLastAnswer

Receive<GetLastAnswer>(m => Sender.Tell(new Answer(answer)));

And then to test it, from Main() we ask for the last answer after the subtraction:

var answerSubtract = calculator.Ask<Answer>(new Subtract(5, 3)).Result;
Console.WriteLine("5 - 3 = " + answerSubtract.Value);

var lastAnswer = calculator.Ask<Answer>(GetLastAnswer.Instance).Result;
Console.WriteLine("Last answer = " + lastAnswer.Value);

If you run this you should see:

1 + 2 = 3
5 - 3 = 2
Last answer = 2

Code

One file containing all code:
https://github.com/HCanber/akka.net-blog-examples/blob/master/02-calculator/CalculatorApp/Program.cs

Entire project: https://github.com/HCanber/akka.net-blog-examples/tree/master/02-calculator

All posts in this series: Tutorials for Akka.NET

No comments:

Post a Comment