3

Suppose I have an application that uses actors for processing User. So there is one UserActor per user. Also every user Actor is mapped to user via id, e.g. to process actions with concrete user you should get Actor like that:

 ActorSelection actor = actorSystem.actorSelection("/user/1");

where 1 is user id.

So the problem is - how generate unique id inside cluster effectively? First it needs to check that new id will not duplicate an existent one. I can create one actor for generating id's which will live in one node, and before creating any new UserActor Generator is asked for id, but this leads to additional request inside cluster whenever user is created. Is there a way to do this more effective? Are there build-in akka techniques to do that?

P.S. May this architecture for using Actor is not effective any suggestion/best practice is welcome.

Cherry
  • 31,309
  • 66
  • 224
  • 364
  • 3
    If you need an explicit name: `UUID.randomUUID.toString` – Rafael Winterhalter Aug 22 '14 at 09:26
  • No. I have updated the question to make it clearer. Could you please read it again. – Cherry Aug 22 '14 at 09:32
  • 2
    @Cherry UUID's [are guaranteed to be unique](http://stackoverflow.com/q/1155008/298389). No additional requests has to be done. – om-nom-nom Aug 22 '14 at 09:33
  • Guaranteed is a strong word :) They're just incredibly unlikely to clash :) – sksamuel Aug 22 '14 at 10:05
  • @om-nom-nom, I disagree that this question is a duplicate of the one you linked it to. What I believe the OP is really asking about is how to ensure within an Akka cluster that there will be a single instance of an actor for each user that is currently using the system, using the user's id as part of the name of the actor so that it can consistently be looked up. – cmbaxter Aug 22 '14 at 12:39
  • @cmbaxter unless akka has special mechanisms to ensure unique membership (AFAIK, you're avid akka user, can you elaborate on this?), this question boils down to unique id (e.g. UUID), or more specifically to unique sequence id, both approaches were described in answer to that question – om-nom-nom Aug 22 '14 at 12:41
  • 1
    @om-nom-nom, my answer was going to center around using a consistent hashing router (hashing based on the user id of the request) sitting in front of an actor deployed to all nodes of the cluster that is responsible for looking up/creating the user based instance for that node and then forwarding onto it. – cmbaxter Aug 22 '14 at 12:43
  • @cmbaxter thank you for advocating this question :-) reopened – om-nom-nom Aug 22 '14 at 12:44

3 Answers3

4

I won't say whether or not your approach is a good idea. That's going to be up to you to decide. If I do understand your problem correctly though, then I can suggest a high level approach to making it work for you. If I understand correctly, you have a cluster, and for any given userId, there should be an actor in the system that handles requests for it, and it should only be on one node and consistently reachable based on the user id of the user. If that's correct, then consider the following approach.

Let's start first with a simple actor, let's call it UserRequestForwarder. This actors job is to find an actor instance for a request for a particular user id and forward on to it. If that actor instance does not yet exist, then this actor will create it before forwarding onto it. A very rough sketch could look like this:

class UserRequestForwarder extends Actor{
  def receive = {
    case req @ DoSomethingForUser(userId) =>
      val childName = s"user-request-handler-$userId"
      val child = context.child(childName).getOrElse(context.actorOf(Props[UserRequestHandler]))
      child forward req
  }
}

Now this actor would be deployed onto every node in the cluster via a ConsistentHashingPool router configured in such a way that there would be one instance per node. You just need to make sure that there is something in every request that needs to travel through this router that allows it to be consistently hashed to the node that handles requests for that user (hopefully using the user id)

So if you pass all requests through this router, they will always land on the node that is responsible for that user, ending up in the UserRequestForwarder which will then find the correct user actor on that node and pass the request on to it.

I have not tried this approach myself, but it might work for what you are trying to do provided I understood your problem correctly.

cmbaxter
  • 35,283
  • 4
  • 86
  • 95
1

Not an akka expert, so I can't offer code, but shouldn't the following approach work:

Have a single actor being responsible for creating the actors. And have it keep a Hashset of actor names, for actors that it created, and that didn't die already.

If you have to spread the load between multiple actors you can dispatch the task based on the first n digits of the hashcode of the actor name that has to be created.

Jens Schauder
  • 77,657
  • 34
  • 181
  • 348
1

It seems like you have your answer on how to generate the unique ID. In terms of your larger question, this is what Akka cluster sharding is designed to solve. It will handle distributing shards among your cluster, finding or starting your actors within the cluster and even rebalancing.

http://doc.akka.io/docs/akka/2.3.5/contrib/cluster-sharding.html

There's also an activator with a really nice example.

http://typesafe.com/activator/template/akka-cluster-sharding-scala

Mike
  • 164
  • 1
  • 4