Most of the Akka material I found on the internet including many SO questions point out that we should maintain relevant data inside an actor which deals with them. Usage of locking mechanisms are discouraged
I'm working on a Java service based on Akka actors which maintains a huge amount of dynamic data. Previously I used copy on write mechanisms for data updates, but this may lead to performance issues (specially high GC activity) in the next version due to expected update rates.
Let's say I have an actor (StockManagerActor) which manages Stock information. Stock prices are read and updated frequently. (I prefer to have a separate actor receiving updates and committing them, and another actor reading the stock prices when it's needed. But I cannot do this since it will make mutable Stock data shared in between different types of actors) So StockManagerActor processes two type of messages. UpdateStockMessage and GetStockMessage. When we think of a single instance of this actor running, everything seems to be fine since no data is shared across actors.
I'm worried that if I just have one StockManagerActor running in the system, inbox of it may grow rapidly when the stock markets are highly active. So I'd like to have a pool of StockManagerActors to process the messages concurrently. But in this case, there will be concurrent update/get operations done by different actor instances. What are the good designs for this kind of a scenario, when updateStock() is blocking (in a separate dispatcher) and when it is non-blocking?
StockManagerActor extends UntypedActor{
StockStroe stockStore;
// Only StockManagerActors use the StockStore which is
// Initially populated from outside of actor system. Methods are not
// thread safe
public StockManagerActor(){
stockStore = StockStore.getInstanceFor(this);
}
@Override
public void onReceive(Object message) throws Exception {
if(message instanceof UpdateStockMessage){
UpdateStockMessage updateMessage = (UpdateStockMessage)message;
stockStore.updateStock(updateMessage)
}else if(message instanceof GetStockMessage){
GetStockMessage getMessage = (GetStockMessage)message;
Stock stock = stockStore.getStock(getMessage.getSymbol());
// stock here is immutable
generateStockMessageAndSend(stock, getSender());
}else{
unhandled(message);
}
}
//... More code
}
Solution : One actor per stock - created on the need.
StockActor - Responsible for maintaining/updating data of one stock. - For each stock one StockActor should be created when needed and killed when not needed/done.
StockManagerActor The parent actor which creates the StockActor when needed and keep a watch of them. Provide other stats like number of child actors running at a time.
Akka sharding Akka sharding along with Clustering will let you seamlessly create one actor per stock and passivate them when not needed. Akka takes care of the sharding functionality. So we need not worry about the unexpected death of the running stock actor.
If you dont want to loose the actors state during a crash just use Akka Persistence as well.
Some links here. Persistence Sharding