Akka.Net – real scenarios of usage

actor-modelakkanet

Now I'm trying to understand Actor model. I know main concepts like everything is an actor etc. I see a lot of words like "it's very good from performance point of view", "no locks because there is no shared resources and messages are immutable". But I still don't understand some points:

  1. In case of old school CRUD application – does it make sense to replace simple processing of request UI->BL->DL with actor system? If not what real scenarios of usage actor model? I'm asking not about

Transaction processing (Online Gaming, Finance/Banking, Trading, Statistics, Betting, Social Media, Telecom), Service backend (any industry, any app)…

but about real use cases. Because for me it's still unclear why I should prefer Akka.NET (I'm .net developer) instead of "traditional" way.

  1. Do actor model and database live in one world or in parallel? How they works together?
  2. Last thought: actor model avoids disadvantages of thread synchronization because it works using messages. But if I need to have access to shared resource like some static cache or something like this I still need synchronization. I really don't understand this point.

Best Answer

I think, your question is not specific to Akka.NET exclusively. So I'll answer it being Akka/Scala developer.

0 Not everything is an actor. Functions are still functions with all that functional composition and purity.

  1. Actor model becomes very useful when you extend beyond CRUD in the land of Domain-Driven-Design and Hexagonal Architecture.
  2. I can't tell for .NET, but in the JVM world most database calls (JDBC) are blocking. Meanwhile, actor system usually works on a limited thread pool compared in size to the number of your CPU cores. So, if your actor system works with blocking calls, Akka allows to configure separate thread pool for that subsystem, i.e. subtree of the actor system's supervision tree. So you can leave your nonblocking business core with 4 threads and give 200 threads to storage subsystem.
  3. Immutable (Persistent, Functional) data structures are the key. You don't need synchronisation to use them. Though sometimes you may need to pass a reference to e.g. ByteStream. Well, you should check that sender doesn't try to work with the instance after it had been sent.

In general, what is the main difference between Erlang and Akka implementations? In Erlang each actor has its own heap and garbage collector. Hence, no shared mutable state, no stop-the-world gc. This gives us soft realtime qualities. But you pay on copying data from actor to actor. In JVM (and .NET?) heap and gc are shared. You don't have overhead like in Erlang, but you should be disciplined not to shoot yourself in your own foot.

Now a couple of use cases I myself have encountered. You know RabbitMQ: exchanges, queues, bindings... If connection fails - some drivers can automatically redeclare them all. I've implemented this logic with Akka, because the driver didn't suit my needs. It was first hard to figure out that logic, but after a managed to separate, what was ephemeral, and what should persist failure - things clicked. The former went to worker actors, while the letter resided in actor's props (which is effectively an actor instance config) and supervisors. This is what is meant when you hear of actor resiliency and self-healing. It is not out-of-the-box, but actors help greatly to implement this logic as straightforward as possible, right after you figure it out yourself.

Hexagonal architecture and concurrency. I've implemented CAS 3.0 Protocol Specification for internal needs using Akka.

First of all, business core, http server, http client, db interaction - all were separated in different actor subsystems and had clear interaction surface. You can test them separately. Business core doesn't know, what an http protocol is. It just known how to handle business commands. It doesn't know anything about db including interfaces. It just knows what messages to send and what to receive. And db's ActorRef, of course.

Second, in CAS protocol there are quite a few things that you need to check for a single user request. So, I implemented every check in a separate worker actors. They are configured with necessary ActorRef-s and a timeout. Either they finish processing before timeout and commit suicide, or the timeout is reached, and the produce error response and, again, commit suicide. Meanwhile, there are other actors, which I called services. They act as supervisors for mentioned workers and existed as long as the whole runtime. Their ActorRef-s are static addresses, which you can use to configure different runtime components during application startup.

Hope this helps

Related Topic