Akka anti-patterns: stateless actors

Actors are object-orientation done right (as opposed to say, objects in Java): their state is not visible from the outside and they communicate via messages. There’s no way to break encapsulation because you can’t peek into an actor’s state while it is running. That’s the entire point of actors: they provide you with the illusion of a safe space in which things are executed sequentially, one message after the other, allowing you to use mutable state inside of your actor without having to worry about race conditions (well, almost: don’t leak the state).

Which is why using actors that don’t have state is somewhat odd, to say the least. With the exception of actors that handle supervision for larger parts of your actor system hierarchy (setting up back-off supervisors, for example), actors are really meant to deal with long-lasting computation that holds state. With long-lasting I mean to say that the actor will have multiple interactions in its lifetime, producing different results depending on its state, as opposed to one-shot computations. For those, Futures are an excellent abstraction: they allow asynchronous code exection, ideal for cases of network and disk related computation (or really intensive CPU tasks), are composable and have a failure-handling mechanism. They also integrate well with Akka actors (using the pipe pattern).

So: don’t use actors if you don’t have state – that’s not what they’re meant for.

Thanks to Heiko for the discussion on this topic in the cab to that fancy vegan restaurant during the last Scala Days in Berlin

Comments 9

  1. Hi Manuel, thanks for your posting! I was wondering whether for example the pipe and filter pattern is an anti-pattern or not, since it does not really hold any state, I mean, mutable variables or so, it usually receives something, do some work and pipe it to the next actor.
    I would like to see some code examples because after this post, I was wondering how many anti-patterns I’ve been writing the last years using Akka 🙂

    1. Post

      I’d say that if none of the filter actors have any mutable state (i.e. the ability to alter the filter criterion) then I’m not sure of the advantage of using actors over simply chaining futures with a for comprehension – unless there’s the danger of one step failing, in which case error handling in actors is easier to handle than with Futures (with a for-comprehension you don’t necessarily know which step has failed, unless you explicitly make this a part of the data being processed).

      1. Hmm, I see and makes sense. I have one big system here written using the pipe and filter pattern and they have some logical inside for success or failure, so yes, I agree it’s easier to handle than using future with for comprehension. Thank you.

  2. Hello Manuel,
    I am somewhat confused with your assertion of stateless actors, being agents of Anti-Patterns. I understand why think so, and what you suggest as alternatives, but while trying to implement a small Rule Engine using Actors, I am facing situations where an Actor simply takes a decision based on the input data and possibly, the Context data, both of which are passed to it. In order to generalize such Actors, I have to allow for situations where a (rule-executing) Actor may or may not have its own state. If it is so, then I am adopting an anti-pattern.
    Is my line of thinking correct? Perhaps, I am missing something. Please help me in catching that.

    1. Post

      Hi! I can see how in this case you’d have a need for stateless actors in order not to have to mix two paradigms and keep doing everything with actors. Though I do have to wonder why you’d implement the rule engine with actors in the first place? Is it a rule engine that spans across several machines because the data to operate on is large? If the computation takes place locally, on one computer, my gut feeling would otherwise be that there wouldn’t necessarily be that much performance gain from performing each step of the rule computation in its own actor, as that would potentially result in having quite a few actors who’d have to interact, leading to context switching. Or have you found a way to split the computation across a small number of actors that doesn’t exceed the amount of CPU cores? That could work well with e.g. the thread affinity dispatcher.

  3. My points for having stateless actors:
    1. Monitoring. You have built-in measurement for actor performance into the tools like Kamon. Nothing similar for futures.
    2. Scalability. You can place an actor into a separate node in the cluster. But how about the service implemented through futures?
    3. Testability. Akka testKit is very powerfull while futures based service got to have it’s own mocks, etc.

    1. Post

      I think this might be a bit like comparing apples and oranges. As I see it you don’t use actors for doing the same things as futures. Futures are much shorter-lived than actors are and carry just one piece of state – i.e. you don’t re-run the same Future. Regarding your points:

      • regarding monitoring I’m not sure how much value there’d be to monitor individual Futures, as they should be rather short-lived executions? It’d probably make more sense to monitor thread pool utilization and I think most profiling tools / JMX offer you this. You can always use metrics to monitor the time a future took to be executed by adding an onComplete handler – this can easily be done via AOP as well if it is a requirement
      • in terms of scalability, if there’s no state, then I don’t see the added value of using Akka Cluster (which is stateful). A stateless deployment with simple API entry points is suffiscient in this case I’d say
      • testing Futures is something that tools such as ScalaTest have really good support for
  4. Thanks for the article, these articles have been really helpful in trying to figure out how to properly use Akka.

    “Futures are an excellent abstraction: they allow asynchronous code exection, ideal for cases of network and disk related computation (or really intensive CPU tasks), are composable and have a failure-handling mechanism”
    One thing that occurs to me about this – AFAIK Futures don’t have a way to handle failures of the actual process, like someone stopping / restarting it or it crashing. They do have failure-handling mechanism but not to the extent that something like AbstractPersistentActorWithAtLeastOnceDelivery does by using persistence and event sourcing.

    If I have an Akka app where I need to make sure that any request we accept will eventually be processed to completion, might it make sense to create an actor for things like network / disk computation, even if that actor has no state other than what is already there in AbstractPersistentActorWithAtLeastOnceDelivery (delivery confirmation stuff)?

    For example, I could create an actor whose sole responsibility is issuing requests / getting a response from a downstream REST API. It doesn’t really have any state, all it does is get a message telling this actor to issue a query and then sending the response body back to the sender once the query completes. This article seems to suggest I should just use a Future for this rather than creating an actor. But if I used a future, then the app crashed while the query was being issued, I would potentially have a situation where the downstream query was incomplete and there wouldn’t be a way for me to know that this happened. Restarting the app becomes a scary proposition because we would have no idea what in-process operations we might be ending in an incomplete state. If I wanted to avoid this I could use event sourcing / persistence and have some recovery logic, etc…and eventually I would end up with something that’s probably about the same as what’s already provided in AbstractPersistentActorWithAtLeastOnceDelivery (where I use the deliver / confirmDeliver to ensure that the http request is completed and I log an event when the request is sent and received).

    That same logic could be applied to any process that we want to guarantee is completed at least once (basically, the things you said we should use futures for). Sure, I could use a future, but I can’t envision many situation where I’d be okay with just letting a process go unfinished. The event sourcing and persistence stuff built into AbstractPersistentActorWithAtLeastOnceDelivery helps me avoid that. Any time I want to be able to track incomplete operations, it seems like I’m better off creating an actor to handle those operations even if all its doing is just performing the operation and returning some sort of result (i.e. not producing different results based on state).

    Maybe I’m misusing AbstractPersistentActorWithAtLeastOnceDelivery or missing something?

    1. Post

      So you have a HTTP API that triggers a process and are waiting for it to be done? If you want to make sure it eventually gets done for certain, then I’d say that you need to change your API to being asynchronous: you persist the fact that the work needs to get done and then return the HTTP call (maybe with an identifier). This could be done using a persistent actor. Then the persistent actor makes sure to call the downstream process which might crash – and it will be the actor’s job to resume the process. You need to make sure that your downstream service is idempotent in that case.
      Note that this is a statefull case – the state being “this item of work needs to be done” contains the state “this item of work”. If you were to implement this in a stateless fashion then in case of crash, it would be the upstream client that’d remember that a particular job needs to be done. It really depends on the architecture of the overall system.

Leave a Reply

Your email address will not be published.