I am looking at an Akka example built with the Play Framework for a web based chatroom. The example in this case, only has one room and gets instantiated with the following:
val room = Akka.system.actorOf(Props[ChatRoom])
I want to expand this example and have multiple chat rooms available instead of just one. A user can provide a string, which can be a chatroom "name", and that would create a new chatroom. Anyone that tries to join this chatroom would share a broadcast with each other, but not with people in another chatroom. Very similar to IRC.
My questions are the following:
1: How do I create a ChatRoom with a unique name if one does not already exist?
2: How can I check if the existing ChatRoom exists and get a reference to it?
The chatroom name will come via either the URL or a query parameter, that part will be trivial. I'm just not entirely sure how to uniquely identify the Akka ChatRoom and later retrieve that Actor by name.
You should find your answer in this document: http://doc.akka.io/docs/akka/snapshot/general/addressing.html
Every actor in the system has a path associated with it, which is very similar to paths in operating systems.
Example : akka://my-sys/user/service-a/worker1
This is a purely local path and worker1 is the name that you would give to the actor when you create it.
You can name your actor when you create it like this:
context.actorOf(Props[ChatRoom], name = "chatroom1")
If you know the path to an actor you can get a reference to it using, context.actorSelection("akka://my-sys/user/service-a/chatroom1")
However when you do a context.actorOf it will return an ActorRef which is a reference to the actor. So another solution would be to store these ActorRef's and look them up by name when you need them.
That said it's always better to work with actorRef's rather than actorSelection since an actorRef is tied to the lifecycle of an actor whereas actorSelection is associated with it just a reference path. ActorSelection's are useful in cases where you are not the one creating the actor and so you do not have it's reference all you know is it's name, then you can lookup them up.
Although once you have an actorSelection you can get an ActorRef by querying the actor using ActorIdentity message.
Related
I'm fairly new to both Scala and Akka and I'm trying to figure out how you would create a proper domain model, which also is an Actor.
Let's imagine we have a simple business case where you can open a new Bank Account. Let's say that one of the rules is that you can only create one bank account per last name (not realistic, but just for the sake of simplicity). My first approach, without applying any business rules, would look something like this:
object Main {
def main(args: Array[String]): Unit = {
implicit val system = ActorSystem("accout")
implicit val materializer = ActorMaterializer()
implicit val executionContext = system.dispatcher
val account = system.actorOf(Props[Account])
account ! CreateAccount("Doe")
}
}
case class CreateAccount(lastName: String)
class Account extends Actor {
var lastName: String = null
override def receive: Receive = {
case createAccount: CreateAccount =>
this.lastName = lastName
}
}
Eventually you would persist this data somewhere. However, when adding the rule that there can only be one Bank Account per last name, a query to some data storage needs to be done. Let's say we put that logic inside a repository and the repository eventually returns an Account, we get to the problem where Account isn't an Actor anymore, since the repository won't be able to create Actors.
This is definitely a wrong implementation and not how Actors should be used. My question is, what are ways to solve these kind of problems? I am aware that my knowledge of Akka is not on a decent level yet, so it might be a weird/stupid formulated question.
This might be a long answer and I am sorry there isn't a TLDR version. :)
Ok, so you want to "Actorize" your domain model? Bad idea. Domain models are not necessarily actors. Sometimes they are but often they are not. It would be an anti-pattern to deploy one actor per domain model because if you do that you are simply offloading the method calling to message calling but losing all of the single threaded paradigm of the method calling. You cannot guarantee the timing of the messages hitting your actor and programming based upon ASK patterns is a good way to introduce a system that is not scalable, eventually you have too many threads and too many futures and cant proceed further, the system bogs and chokes. So what does that mean for your particular problem?
First you have to stop thinking of the domain model as a single thing and definitely stop using POJO entities. I entirely agree with Martin Fowler when he discusses the anemic domain model. In a well built actor system there will often be three domain models. One is the persisted model which has entities that model your database. The second is the immutable model. This is the model that the actors use to communicate with each other. All the entities are immutable from the bottom up, all collections unmodifiable, all objects only have getters, all constructors copy the collections to new immutable collections. The immutable model means your actors never have to copy anything, they just pass around references to data. Lastly you will have the API model, this is usually the set of entities that model the JSON for the clients to consume. The API model is there to insulate the back end from client code changes and vice versa, its the contract between the systems.
To create your actors stop thinking about your persistent model and what you will do with it but instead start thinking of the use cases. What does your system have to do? Model your actors based on the use cases and that will change the implementation of the actors and their deployment strategies.
For example, consider a server that delivers inventory information to users including current stock levels, reviews by users and so on for products by a single vendor. The users hammer this information and it changes quickly as stock levels change. This information is likely stored in half a dozen different tables. We don't model an actor for each table but rather a single actor to serve this use case. In this case this information is accessed by a large group of people in heavy load environment. So we are best creating an actor to aggregate all of this data and replicating the actor to each node and whenever the data changes we inform all replicants on all nodes of the changes. This means the user getting the overview doesn't even touch the database. They hit the actors, get the immutable model, convert that to the API model and then return the data.
On the other hand if a user wants to change the stock levels, we need to make sure that two users don't do it concurrently yet large DB transactions slows down the system massively. So instead we pick one node that will hold the stock management actor for that vendor and we cluster shard the actor. Any requests are routed to that actor and handled serially. The company user logs in and notes the receipt of a delivery of 20 new items. The message goes from whatever node they hit to the node holding the actor for that vendor, the vendor then makes the appropriate database changes and the broadcasts the change which is picked up by all the replicated inventory view actors to change their data.
Now this is simplistic because you have to deal with lost messages (read the articles on why reliable messaging is not necessary). However once you start to go down that road you soon realize that simply making your domain model an actor system is an anti-pattern and there are better ways to do things.
Anyway that is my 2 cents :)
General Design
Actors should generally be simple dispatchers to business logic and contain as little functionality as possible. Think of Actors as similar to a Future; when you want concurrency in scala you don't extend the Future class, you just use Future functionality around your existing logic.
Limiting your Actors to bare-bones responsibility has several advantages:
Testing the code can be done without having to construct ActorSystems, probes, ActorRefs, etc...
The business logic can easily be transplanted to other asynchronous libraries, e.g. Futures and akka streams.
It's easier to create a "proper domain model" with plain old classes and functions than it is with Actors.
Placing business logic in Actors naturally emphasizes a more object oriented code/system design rather than a functional approach (we picked scala for a reason).
Business Logic (No Akka)
Here we will setup all of the domain specific logic without using any akka related "stuff".
object BusinessLogicDomain {
type FirstName = String
type LastName = String
type Balance = Double
val defaultBalance : Balance = 0.0
case class Account(firstName : FirstName,
lastName : LastName,
balance : Balance = defaultBalance)
Lets model your account directory as a HashMap:
type AccountDirectory = HashMap[LastName, Account]
val emptyDirectory : AccountDirectory = HashMap.empty[LastName, Account]
We can now create a function that matches your requirements for distinct account per last name:
val addAccount : (AccountDirectory, Account) => AccountDirectory =
(accountDirectory, account) =>
if(accountDirectory contains account.lastName)
accountDirectory
else
accountDirectory + (account.lastName -> account)
}//end object BusinessLogicDomain
Repository (Akka)
Now that the unpolluted business code is complete, and isolated, we can add the concurrency layer on top of the foundational logic.
We can use the become functionality of Actors to store the state and respond to requests:
import BusinessLogicDomain.{Account, AccountDirectory, emptyDirectory, addAccount}
case object QueryAccountDirectory
class RepoActor(accountDirectory : AccountDirectory = emptyDirectory) extends Actor {
val statefulReceive : AccountDirectory => Receive =
currentDirectory => {
case account : Account =>
context become statefulReceive(addAccount(currentDirectory, account))
case QueryAccountDirectory =>
sender ! currentDirectory
}
override def receive : Receive = statefulReceive(accountDirectory)
}
I created two actors Actor1 and Actor2 in one project and registered them in Program.cs:
ActorRuntime.RegisterActorAsync<Actor1>();
ActorRuntime.RegisterActorAsync<Actor2>();
but in runtime I see that each of them hosted in separate Actor Services. When I save some data in Reliable Collections via StateManager in Actor1, this state is not available for Actor2.
Is it possible to share state between different actor types?
First of all, it looks like you are using actors in wrong way. You should register one actor type per host process. For example
ActorRuntime.RegisterActorAsync<Actor1>((context, actorType) =>
new ActorService(
context,
actorType,
() => new Actor1()))
.GetAwaiter()
.GetResult();
If you want communication between actors, you need to create actor instance and call a method on its interface to save data. Then, if you want to save the same data in another actor, you need to create another instance. Actros by design are unique, separate entities. You can do it from one place and create two actors and save the data separately or you can create second actor from the first. For example
var actor1Proxy = ActorProxy.Create<IActor1>(actorId, new Uri("fabric:/MyApp/Actor1"));
var actor2Proxy = ActorProxy.Create<IActor2>(actorId, new Uri("fabric:/MyApp/Actor2"));
actor1Proxy.SaveData(data);
actor2Proxy.SaveData(data);
Probably, you need a reliable service to store your data. Please read this and this articles for more information about how to use actors.
I currently have a ReliableActor for every user in the system. This actor is appropriately named User, and for the sake of this question has a Location property. What would be the recommended approach for querying Users by Location?
My current thought is to create a ReliableService that contains a ReliableDictionary. The data in the dictionary would be a projection of the User data. If I did that, then I would need to:
Query the dictionary. After GA, this seems like the recommended approach.
Keep the dictionary in sync. Perhaps through Pub/Sub or IActorEvents.
Another alternative would be to have a persistent store outside Service Fabric, such as a database. This feels wrong, as it goes against some of the ideals of using the Service Fabric. If I did, I would assume something similar to the above but using a Stateless service?
Thank you very much.
I'm personally exploring the use of Actors as the main datastore (ie: source of truth) for my entities. As Actors are added, updated or deleted, I use MassTransit to publish events. I then have Reliable Statefull Services subscribed to these events. The services receive the events and update their internal IReliableDictionary's. The services can then be queried to find the entities required by the client. Each service only keeps the entity data that it requires to perform it's queries.
I'm also exploring the use of EventStore to publish the events as well. That way, if in the future I decide I need to query the entities in a new way, I could create a new service and replay all the events to it.
These Pub/Sub methods do mean the query services are only eventually consistent, but in a distributed system, this seems to be the norm.
While the standard recommendation is definitely as Vaclav's response, if querying is the exception then Actors could still be appropriate. For me whether they're suitable or not is defined by the normal way of accessing them, if it's by key (presumably for a user record it would be) then Actors work well.
It is possible to iterate over Actors, but it's quite a heavy task, so like I say is only appropriate if it's the exceptional case. The following code will build up a set of Actor references, you then iterate over this set to fetch the actors and then can use Linq or similar on the collection that you've built up.
ContinuationToken continuationToken = null;
var actorServiceProxy = ActorServiceProxy.Create("fabric:/MyActorApp/MyActorService", partitionKey);
var queriedActorCount = 0;
do
{
var queryResult = actorServiceProxy.GetActorsAsync(continuationToken, cancellationToken).GetAwaiter().GetResult();
queriedActorCount += queryResult.Items.Count();
continuationToken = queryResult.ContinuationToken;
} while (continuationToken != null);
TLDR: It's not always advisable to query over actors, but it can be achieved if required. Code above will get you started.
if you find yourself needing to query across a data set by some data property, like User.Location, then Reliable Collections are the right answer. Reliable Actors are not meant to be queried over this way.
In your case, a user could simply be a row in a Reliable Dictionary.
I'm creating WebSocket actors in Play (Scala).
Actors are being created somewhere else in the system, and I just need to keep them in one place, grouped by some variables.
What is the best practice to store them, and which one takes up the smallest amount of memory:
Seq[Actor]
Seq[ActorRef]
Something else?
You should NEVER store actors - the only way to access actor should be through the ActorRef
There are few patterns/practices that you could use to find your actors.
First is ActorSelection, and it would require building right actor hierarchy. For instance, you have users split by geographical location, then you might want to have actor selections like
/user/..../US/PA/18900/user1
/user/..../US/PA/18900/user2
/user/..../US/NJ/07000/user3
This way you could find all actors using selection with wildcard, although you will stick with just one property to filter them
The other way is to have data structure that would store all your flags/properties, for instance.
case class UserRef(ref: ActorRef, name: String, country: String, zip: Integer, active: Boolean)
Then, your 'directory' will store them as a users = List[UserRef] and you will be able to query this structure with one pass using users.filter(_.active = true) or users.find(_.name = "superuser")
I'm trying to apply CQRS in a pet project: in my example there is Food and Course.
A Course is composed by many item of different Food.
In my design I have those commands:
AddFoodToCourse ChangeFoodQuantityInCourse etc...
If I create a CourseCommandHandler I will get a ICourseRepository as dependency in order to modify courses but keeping commands simple I'll have CourseId and FoodId properties instead of Food.
In the handler I suppose to manage only Ids and get all the relevant data from a repository or not?
If I have understood correctly my app must follow this workflow:
Use ReadModel => Create a command and send to bus => CommandHandler consume command, operate with domain object and save objects with repositories => Domain objects raise events and publish into bus => EventHandler consume event and modify ReadModel object
In the handler I suppose to manage only Ids and get all the relevant
data from a repository or not?
Correct. The command handler is the place to inject repositories and other dependencies required to handle commands. As you stated, commands should reference entities using IDs not the entity itself. This is because the commands are meant to be serialized and passed across application boundaries. Your workflow is also correct. Take a look here for a more in-depth treatment on the subject.