9

I am new to Akka and actor framework - I am sure I am missing something obvious, please accept my apologies in advance.

I keep reading that one of the main points to choose Akka is the way it manages concurrency.

It is not clear to me why Akka is so special; I understand that there are many little actors which are very light and fast; however, how can this help me when two users save a form at the same time?

Wouldn't I still need some sort of concurrency lock (pessimistic/optimistic/etc..)?

Random42
  • 10,370
  • 10
  • 48
  • 65
abx78
  • 415
  • 4
  • 10
  • Are you asking why actors are good for concurrency or specifically Akka? – scriptin Oct 20 '15 at 19:27
  • `Wouldn't I still need some sort of concurrency lock (pessimistic/optimistic/etc..)?` -- Yes, but that's the responsibility of the underlying RDBMS, not the actor. Very likely the Akka actor is not talking to your user directly. Rather, the Akka actor and the user (another actor, essentially) both interact with the database, directly. – Robert Harvey Oct 20 '15 at 19:57

2 Answers2

10

I'm going to write about the Actor Model in general (not just Akka) in comparison with other concurrency models such as the classical lock-based concurrency and the neat transactional memory.

Advantages

  1. Easier concept to understand and use

    • Lock based concurrency is difficult; in particular is very difficult to get it right because there are many concepts to understand and use in order to be correct and efficient: locks, semaphores, threads, synchronization, mutual exclusion, memory barrier etc.

    • Actors, on the other hand are a more abstract concept; you have actors which send and receive messages. No need to grasp and use low-level concepts such as the memory barrier.

  2. Enforces immutability

    • Mutability is a source of many errors and bugs in programming, especially in multi-threaded applications. Actor model solves this issue by enforcing immutability.
  3. Less error prone

    • Because of the above two reasons
  4. Efficient

    • Not so efficient as good written lock-based but in general more efficient than software transactional memory.
  5. Easily scalable

    • Theoretically at least, the application should scale pretty well by adding more actors to perform your jobs. With lock-based is pretty difficult to scale.

Disadvantages

  1. Not all languages easily enforce immutability;

    • Erlang, the language that first popularized actors has immutability at its core but Java and Scala (actually the JVM) does not enforce immutability.
  2. Still pretty complex

    • Actors are based on an asynchronous model of programming which is not so straight forward and easy to model in all scenarios; it is particularly difficult to handle errors and failure scenarios.
  3. Does not prevent deadlock or starvation

    • Two actors can be in the state that wait message one from another; thus you have a deadlock just like with locks, although much easier to debug. With transactional memory however you are guaranteed deadlock free.
  4. Not so efficient

    • Because of enforced immutability and because many actors have to switch in using the same thread actors won't be as efficient as lock-based concurrency.

Conclusion

Lock-based concurrency is the most efficient but it's hard to program and error-prone; software transactional memory is the most clear, easy to program and less-error prone but it's also the least efficient. Actors are somewhere in between these two models with all aspects: easy of programming, efficiency and error proneness.

8bittree
  • 5,637
  • 3
  • 27
  • 37
Random42
  • 10,370
  • 10
  • 48
  • 65
  • For your number 2 advantage, shouldn't that be "**Mutability** is a source of many errors..." and "...solves this issue by forbidding **mutability**" rather than "*immutability*"? Also, the second sentence has two redundant mentions of solving the issue. – 8bittree Oct 21 '15 at 14:00
  • @8bittree Yep, you are correct; i misspelled. Just in case you don't know, you can edit the answers to correct such issues. – Random42 Oct 23 '15 at 08:31
  • I am aware of that, I'm just reluctant to make changes that invert or drastically change the meaning of something in case it's a misunderstanding on my part. – 8bittree Oct 23 '15 at 13:57
  • Can you elaborate on deadlocking? It seems like you'd have to construct a very awkward actor setup (bi-directional communication) to come across that. If you're using an ask-style pattern (A asks B for answer, B processes the request and replies to the request sender but never contacts A directly) I don't see how a deadlock is possible – Daenyth Oct 23 '15 at 15:56
  • 1
    @Daenyth I agree that you'd have to use a weird construct to achieve deadlock with actors; but from a similar perspective you'd have to use a weird construct to achieve a deadlock with lock-based concurrency (although I agree it's much more easier to create a deadlock with a mutex than with an actor). Anyway, the idea is that only transactional memory guarantees that you cannot have a deadlock no matter what weird construct you chose to implement. – Random42 Oct 23 '15 at 16:32
7

One of the advantages of message-processing models like actors and agents is that the traditional concurrency problems (primarily synchronization of shared state) are no longer a problem. The actor can keep private state and update it freely without locks. The actor framework ensures that only one message is processed at a time. With serialized processing, code can be written in a lock-free way.

In your example of users saving a form, assuming the actor was keeping a List of some data from each form, the actor can update the list without locks, because the framework guarantees that only one form will be processed at a time. Traditionally, you would have to lock around the List accesses or use a concurrent list.

Concurrency strategy is a slightly different matter and is still your responsibility (with no strategy being the most common strategy). To change your example slightly, let's say that both users try to update the SAME form instance at the same time. With no concurrency strategy, one's changes will overwrite the other (probably last one wins). That's fine, but at best this results in unexpected behavior for the user whose changes got overwritten. If they view the form they just changed, it will have unexpected values (from the other user). At worst (when we're not just talking about form updates, but things like shipping orders) it could result in losses of various kinds (time, revenue, etc.).

Using a concurrency strategy helps to identify these cases and be able to resolve them based on business rules. For instance Optimistic Concurrency has the user send the version of the form it is updating. When the actor goes to process the change, it notices that the 2nd user thinks it's updating Version 5 when the form is actually at Version 6 because of the first user's update. Now at least we can notify the 2nd user that the form has already changed since they started editing it. Or whatever rules the business wants to enforce there.

In the case of updating a form, you probably don't care as much about concurrency (depends, I guess). But in other cases, it may be a very important thing to at least be able to check and handle violations. You may even want to ignore the concurrency violation, like if the users changed different sections (to continue the form analogy). Or if the change has a large impact on the business (a big order), you want to accept it and resolve minor conflicts later (e.g. yearly contact info update hasn't been completed).

I believe Akka has a number of other dimensions like how it handles failures, supervisors, etc. that are important considerations for devops.

Kasey Speakman
  • 4,341
  • 19
  • 26