Keep an Eye on Your Memory Consumption When using Akka.Net

I was recently working on a system that essentially involved using Akka to repeatedly call an Api over HTTP, transform the data and store the result. Pretty simple stuff. Using Akka to do this seemed ideal as some of the data transforms were a little complex and I was dealing with a lot of data and a huge amounts of requests. I had setup the system to start its process every 5 minutes, using the built-in scheduler – like so:


system
.Scheduler
.Schedule(
TimeSpan.FromSeconds(0),
TimeSpan.FromMinutes(5),
someActor, someMessage);

What I did wrong

Now, to keep it simple, lets say I had three actor types; CollectDataActor, TransformDataActor and StoreDataActor – and the process simply involved the CollectDataActor calling the Api then telling the TransformDataActor to do its thing and in turn this tells the StoreDataActor to, well, save the data.

What I was doing to achieve this was calling:


var actor = Context.ActorOf(Context.Create<CollectDataActor>());
actor.Tell(MyMessage);

And this would then collect the data which came back as a List<> if data. And then essentially created a TransformDataActor for every item in this list, and told them to process it.

Why this is a problem

As I mentioned at the top of the post, I was scheduling this process to run every 5 minutes. This meant that every single time the process was scheduled to run new actors were created at each stage, a new actor to collect, a huge amount to transform and subsequently store. This resulted in memory consumption just increasing and increasing over time. Not good.

Fix

At first my solution to this was to kill all the actors I had after I was done with them. However, this became hard to manage as the system grew and it didn’t work at all well for re-sending dead messages – it was a hack, to be honest.

The solution I used was to create in all of the actors I’d need before I start running. I knew I’d need only one CollectDataActor per process run, I’d need a shed load of TransformDataActors and the same for SaveDataActors (1 for each data item I transform).

What I did was create a router with a round robin pool of the TransformDataActors and SaveDataActors. This is a feature of Akka.Net that will essentially keep a pool of any given actor type, and when you message the router it send to an Actor within the said pool which is not busy (or has the fewest numbers of letters in its mailbox). Then, rather than creating an actor each time from the CollectDataActor, I can just select the router by its path and send it.

Code:


//setup code.
var transformPops = Props.Create<TransformDataActors>().WithRouter(new RoundRobinPool(50));
var transformRouter = system.ActorOf(transformPops , "transformDataRouter");

var saveProps = Props.Create<SaveDataActors>().WithRouter(new RoundRobinPool(50));
var saveRouter = system.ActorOf(saveProps , "saveDataRouter");

With these set up, the CollectDataActor code can look a little something like:


public class CollectDataActor : ReceiveActor
{
    public CollectDataActor()
    {
         Receive<object>(message => Handle(message));
    }

    private void Handle(object message)
    {
        List<TypeMcType> data = CollectData();

        foreach (var item in data)
        {
           var actorPath = "akka://path/to/your/system/transformRouter";
           var actor = Context.ActorSelection(actorPath);
           actor.Tell(item);
           //NOTE: here we use "ActorSelection", we do not create a new actor
           // -  this is the key difference!
        }
    }
}

Leave a Reply

Please log in using one of these methods to post your comment:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google+ photo

You are commenting using your Google+ account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s