James Cowhen
James Cowhen

Reputation: 2965

When to use Actors vs Futures?

I am currently working on a Play! project that has the following architecture:

Controllers -> Services (actors) -> Models (Regular case classes)

For each request that comes in, we will issue a call to the service layers like so:

Service ? DoSomething(request, context)

We have a set number of these service actors behind an akka router that are created during app initialization, and is expandable on demand.

And in the service we mostly do modest data manipulation or database calls:

receive = {
    case DoSomething(x, y) => {
           ...
           Model.doSometing(...)
           sender ! result
    }
}

I am having second thoughts on whether we should be using actors for our services or just use Futures only.

  1. We do not have any internal state that needs to be modified in the service actors, whatever message comes in goes to a function and spits out the result. Isn't this the big strength of the actor model?

  2. We are doing a lot of tasks which seem to take a lot away from the actor model

  3. We aren't doing heavy computation and remoting doesn't make sense because most of the work is for the database and roundtriping to a remote actor to make some db call is unnecessary

  4. We do use reactivemongo, so every db call is non blocking. We can make a lot of these calls

It seems to me that removing akka and just use Futures makes our life a lot easier, and we don't really lose anything.

Upvotes: 39

Views: 13335

Answers (4)

Igor Yudnikov
Igor Yudnikov

Reputation: 464

Actor should be crated when you need some really long living stuff with modifying state. In other cases there are no any benefits from actors, especially from non-typed ones. - do pattern matching every time - control actor's lifecycle - remember the things which should not be passed between threads Why do all of this when you may have simple Future? There are tasks where actors fit very well, but not everywhere

Upvotes: 1

David Puleri
David Puleri

Reputation: 154

I was wondering the same and what we decide to do was to use Akka for our data access and it works very well, it's very testable (and tested), and very portable.

We created repositories, long living actors, that we bootstrapped in our App : (FYI, we are using slick for our DB Access, but also have a similar design for our MongoDB needs)

val subscriptionRepo = context.actorOf(Props(new SubscriptionRepository(appConfig.db)), "repository-subscription")

Now we are able to send a "Request" Message for data, ex:

case class SubscriptionsRequested(atDate: ZeroMillisDateTime)

that the actor will respond with

case class SubscriptionsFound(users: Seq[UserSubscription])

or Failure(exception)

In our situation (spray apps but also CLI), we wrapped those calls in a short living actor that take the context and complete on reception and closes itself. (You could handle domain specific logic in those actor, have it to extend another actor that manages its lifecycle and exception so you would only have to specify a partial function for your needs and leave the abstract actor to deal with timeouts, common exceptions etc.

We also have situations where we needed more work to be done in the initiating actor, and it very convenient to fire x messages to your repositories and have your actor storing those messages as they arrive, doing something once they are all there, firing back for completion to the sender( for instance) and close itself.

Thanks to this design, we have a very reactive repository living outside our app, completely tested with Akka TestKit and H2, completely DB agnostic, and it's dead easy, to access data from our DBs (and we never do any ASK, only Tell : Tell to repo, tell to sender, complete or x Tells to repos, pattern match on expected results until completion, tell to sender).

Upvotes: 0

akauppi
akauppi

Reputation: 18076

I think you're on right tracks.

We do not have any internal state that needs to be modified in the service actors, whatever message comes in goes to a function and spits out the result. Isn't this the big strength of the actor model?

I found Chris Stucchio's blog (referred to by @cmbaxter above) instantly delightful. My case was so simple that architectural considerations were not a valid point. Just Spray routing and lots of database access, like you have. No state. Thus Future. So much simpler code.

Upvotes: 8

cmbaxter
cmbaxter

Reputation: 35463

There certainly is no shortage of opinion on the topic of what should and shouldn't be an actor. Like these two posts:

http://noelwelsh.com/programming/2013/03/04/why-i-dont-like-akka-actors/

http://www.chrisstucchio.com/blog/2013/actors_vs_futures.html

I don't think you're going to find an absolute answer to this question other then that it's situational and it's really up to your preferences and your problem. What I can do for you is to offer my opinion that is based on us implementing Akka for about 2 years now.

For me, I like to think of Akka really as a platform. We come for the Actor Model but we stay for all of the other goodness that the platform provides like Clustering/Remoting, FSM, Routing, Circuit Breaker, Throttling and the like. We are trying to build an SOA like architecture with our actors acting as services. We are deploying these services across a cluster, so we are taking advantage of things like Location Transparency and Routing to provide the ability for a service consumer (which itself could be another service) to find and use a service no matter where it is deployed, and in a highly available manner. Akka makes this whole process pretty simple based on the platform tools they offer.

Within our system, we have the concept of what I call Foundation Services. These are really simple services (like basic lookup/management services for a particular entity). These services generally don't call any other services, and in some cases, just perform DB lookups. These services are pooled (router) and don't usually have any state. They are pretty similar to what you are describing some of your services to be like. We then start to build more and more complex services on top of these foundation services. Most of these services are short lived (to avoid asking), sometimes FSM based, that collect data from the foundation services and then crunch and do something as a result. Even though these foundation services are themselves pretty simple, and some would say don't require an actor, I like the flexibility in that when I compose them into a higher level service, I can look them up and they can be anywhere (location transparent) in my cluster with any number of instances available (routing) for using.

So for us, it was really a design decision to baseline around an actor as a sort of micro-like service that is available within our cluster for consumption by any other service, no matter how simple that service is. I like communicating with these services, where ever they are, through a coarse grained interface in an async manner. A lot of those principles are aspects of building out a good SOA. If that's your goal, then I think Akka can be very helpful in achieving that goal. If you are not looking to do something like that, then maybe your are right in questioning your decision to use Akka for your services. Like I said earlier, it's really up to you to figure out what you are trying to do from an architecture perspective and then design your services layer to meet those goals.

Upvotes: 37

Related Questions