Devreal

Scale By The Bay 2020: Salar Rahmanian, Acting Lessons for Scala Engineers with Akka and ZIO

Scale By The Bay 2020: Salar Rahmanian, Acting Lessons for Scala Engineers with Akka and ZIO

Recording: Scale By The Bay 2020: Salar Rahmanian, Acting Lessons for Scala Engineers with Akka and ZIO

Hi everyone. Thank you for coming to my talk acting lessons for Scala engineers with Akka and ZIO. The agenda for my talk today is it's going to I'm going to start by introducing myself briefly. Then do a quick introduction to the actor model. And then I'm going to talk about two implementations of the actor model, namely Akka and ZIO actors. And lastly, I'm going to wrap up with why the future is bright for the actor model on the JVM with Scala. I'm Solar Ramanian. The picture you see on the screen is my family, my wonderful wife Layla, my two sons Valentino and Caspian, and my daughter Persephone

Online, I'm known as Softinio in most places. My blog blog is at softinio.com. On Twitter, I'm known by my real name, Solar Ramanian. I occasionally stream on Twitch with the same username of Softinio. I'm a Scala engineer who's trying to learn and get better at functional programming. My first computer was an IBM PC clone with Intel 486 processor inside it. It had a button called turbo that when pushed would double its clock speed from 16 MHz to 32 MHz. Watching how software and hardware have evolved since then has been an interesting journey

When new faster hardware comes out, software quickly evolves to make use of all the new performance. CPUs have evolved from it all being about their clock speed and cache to being more about their number of cores and hyperthreading. With Moore's Law and this evolution, we need to fully utilize the multiple cores and threads. We need to leverage concurrency. There are many methods and patterns for concurrency and in this talk, our focus is on the actor model. So, what is an actor? An actor consists of an isolated internal state, an address, a mailbox, and a behavior. The address is a unique reference to locate the actor so that you can send a message to it. Once the actor receives a message, the message is added to the mailbox

The actor's mailbox is kind of like a queue. The order of the items in the mailbox is not guaranteed though. It It's not guaranteed. Though some implementations do give you a choice of different kinds of mailboxes which do. Actors typically pick up one message at a time to process. The behavior determines what the actor does with each message. Once the actor has finished processing the message, it will do one or more of the following. It will either do nothing

It will send one or more messages to other actors. It could create a new actor and send a message to it. And it could respond to the sender zero or more times. And it may or may not update the internal state. So, what are the characteristics of an actor? Actors persist. Actors persisting means once they are started, they will keep running processing messages from its mailbox. It maintains its internal state throughout. Only the actor has access to this internal state

This is unlike normal async programming like futures in Scala where once the call is finished, it stops and its state between calls is not maintained. These characteristic These characteristics gives us superpowers for building concurrent applications. So, how do actors handle failure? Each actor has a supervisor whose sole job is to know how to handle a failure. The supervisor itself is an actor. Different libraries and frameworks provide you with a choice of supervisors that you can use to handle failures. You will always have the opportunity to create your own custom supervisors. So, let's start with taking a look at Akka. Taken straight from the Akka's documentation, Akka is a toolkit for building highly concurrent, distributed, and resilient message-driven applications for Java and Scala

In other words, it basically it implements the actor model. Um it it it's inspiration came from Erlang. And recently introduced typed actors, which is the now the default way to create Akka actors. So, what I would like to do is go through an example application using Akka and Scala, a contrived example, a simple mailing list app where we can add a person's name and email address to a data store, remove a person from a data store, or retrieve a person from a data store. As this is a contrived example, the data store is contrived also. We will actually just log a message for that action. Looking at my slide, all the boxes represent the different actors that we are going to have with this application. So, let's go through what each actor is doing

The root actor creates the actor system and spawns all the actors. The validate email address actor validates if the new message received has a valid email address. The data store actor decides whether we are doing an add, get, or a remove and calls the relevant actor with the message. The add action actor uses the message received to add the received subscriber to the database. The get subscriber actor retrieves a subscriber from the database. The remove subscriber actor removes a subscriber from the database. And last but not least, the reply to actor is responsible for letting the original caller know we accepted a new customer to add to the data data store. data store

Let's now look at the types of messages our actors are going to be communicating with. Starting with the Hey Solar. Yes. Um we're having a little bit of uh breaking in in your voice. Um if you could talk a little louder so we can hear you a little better. Sure. Sure. Sure

I'll do that. I'll do that. Uh is this better? Is this better? Yeah. Okay. Great. Great. Great. So, let's now look at the types of messages our actors are going to be communicating with

Let's start with the command type. Our actors will have Our actors will be sending three types of commands to determine whether we are adding, removing, or fetching a person. So, we have So, we have a subtype which caters for each of the possible commands, add, remove, or get. The customer type is is The customer type is is the message that is received by our root actor for adding a person. It simply contains the first name, last name, and email address of the customer that we want to add. The message type is the message that is sent by the root actor to add a new subscriber. It basically contains the all the information that our customer type had. But in addition, it has a command field to determine whether we are adding, removing, or fetching

It has an actor ref for the data store actor so it knows which actor that was spawned it should send the message to for storing. It has an actor ref for for for the reply to actor to send the message to once the received message has been accepted to be saved. Also, it has a helper method to make sure the received message has a valid email address. Note that the actor ref I'm referring to is the actor's address that I mentioned earlier. The subscribe message The subscribe message type is the message that that is sent to the reply to actor to confirm we are storing the customer. It has a unique ID of the item being added and an actor ref of the actor that sent the request. Let's look at how we would implement our first actor now using Akka. When creating When creating an actor, we need to define how the actor reacts to messages and how they are processed

Taking a look at the apply method of my code snippet, you can see that to do that, we create a behavior of type message, which is the type of the message the actor will receive. Behaviors.receive provides us with a context and the received message. We use the context to log messages. Message.is valid is the helper method that I mentioned when we that the message type has is called to determine if the received message has a valid email address. If not a valid email address, an error message is logged using the context. If the message is valid, we continue. A message is sent to the reply to actor to confirm acceptance of the received message for adding. And a message is sent to the data store actor that will save the message

Last, we need to signal whether our actors' internal state will change before the next message is processed. In this example, its internal state does not change so we return a Behaviors.same to show this. Now, let's take a look at the data store actor. Similar to the earlier actor, we receive the context and the message. The context is used for logging. We pattern match on the message's command field this time to determine the relevant data store operation of add, remove, or get and call the relevant actor to carry out the operation. As this is a contrived example, we're just going to We are just logging what what what action is being taken here. Again, as this actor doesn't need to change its internal state between calls, a Behaviors.same is returned

Let's take a look at the root actor for our Akka application. The root actor spawns all new actors and as a result has the actor ref for all the actors. Going through the code snippets, you can see we are creating a behavior of type customer as that is the type of messages we will be receiving. As this is the outermost actor in our actor hierarchy, behaviors does set up is used to spawn all the actors we have. When a new message is received, a message is sent to the subscriber actor, which is our validate email address actor that we went through earlier that that that we went through earlier. Great. In your application's main, you create the actor system and start sending customer messages for this contrived example. The code snippet is is self-explanatory for that

So, now let's take a look at ZIO Actors, another implementation of the actor model. ZIO Actors is a high-performance purely functional library for building, composing, and subscribing, and supervising typed actors. It's backed by ZIO and Scala. It models actors communications without any side effects. And it's and it's and every everything is typed. Everything is typed. We're going to go through the same contrived example that we went through with Akka, but this time using ZIO Actors for comparison. So, starting with a review of the message types our actors are going to be communicating with, we have the same command type to know whether we are adding, removing, or fetching a person

We define the protocol type whose child types will be the different types our actors will be using to run behaviors on and do any processing on. The customer type is the same as what we had with the Akka application, except it is now a child type of the protocol type we have. The message type is the same same what what we used for the Akka application also with one exception. Notice that the is valid helper now returns a UIO Boolean instead of a uh Boolean. UIO is an effect type from ZIO. We will talk about UIO later. The subscribe message type is the same again again it's it's a child of the protocol type that we defined. With ZIO Actors, we define we also define a specific type for failures

We'll talk more about this in a later slide. I don't have time in this presentation to go over ZIO in detail, but let's just do a quick summary of what a ZIO effect type looks like. ZIO has three type parameters R, E, and A. The R is is the environment type. The E is the failure type. And the A is the success type. ZIO provides us with some type aliases. I'm going to go through two of them as I will be using them in this presentation

UIO, as you saw in in the earlier slide, is a ZIO effect that can have any environment and will never fail. RIO is a ZIO effect that will require an environment. It can fail with a throwable or it can succeed with an A. When creating actors, we need to define how the actor reacts to messages and how it processes them. With ZIO Actors, to create a behavior, we need to create a stateful. Looking at the stateful, stateful has three type parameters R, S, and F. The R is the environment type, similar to what ZIO provides. The S represents the state of the actor uh the state of the actor that gets updated after every message

And then of course, F represents the message the actor will receive that that that that needs to be processed that needs to be processed. So, let's implement our first actor behavior, but this time with ZIO Actors. We start by creating a new stateful. For the environment type, we are passing a console. A console is an environment type provided by ZIO. As this is a contrived example, we use this environment to log messages. The state of the actor does not change between messages processed just like with the Akka applications. Hence, the state type hence hence the state, which is the S, is unit

Protocol, which is the F, is the type of messages our actor is able to process. To implement a behavior, we pattern match on the protocol to make sure we have a message of the correct type. For a valid message, we print to the console that we are validating. We check if the email address is valid by using the helper method our message type provided. If everything is valid, we continue. We get the actor ref of the current actor from the context. We use this actor ref to create a subscribe message type and send send it to the reply to actor. We then send the received message to the data store actor to save the message

And of course, if the message is received if the message received is of an invalid type, then we fail by by doing an io.fail. io.fail is provided by ZIO for us. Interesting interesting thing to note here is that as everything here are all ZIO effects, we can use a for comprehension to compose our flow. This actor returns an RIO, which is an alias to a ZIO effect as as discussed earlier. It has a console environment type. And it returns a tuple for success. It returns a tuple for success. And for a failure, it it it it it it it it it does an io.fail as mentioned earlier, which we which we which is the failure of type of the ZIO ZIO signature ZIO signature

This illustrates one of the one of the magical powers of functional programming, where the error channel in ZIO expects a throwable. So, this constraint forces you as a developer to create the correct type to return on failure. This is why we created invalid email exception type. Now, we implement the data store actor. Similar similarly to the earlier actor, we define a stateful. We pattern match to make sure we have a valid message. If not, we return an io.fail to indicate a failure. If the message is valid, we do a we do another pattern match, this time on the command received to determine whether we are doing an add, remove, or a get

And depending on which the message is sent to the relevant actor. Now, let's look at how we create our actor system. With ZIO Actors, everything is a ZIO effect. Hence, we can use a for comprehension. And we start by creating the actor system. Using the actor system that we created, we use its dot make property to create all our actors. From my slide, you can see the signature of the dot make file. It's telling you basically to give your actor a name

For our contrived example, we're actually not going to have any supervisors, so hence the supervisor.none. But as to my earlier message, ZIO Actors provides with multiple kinds of supervisors. So, you could have chosen a particular kind that you want to have, but for the purpose of this contrived example, we we're not we're not going to have any supervisors. And then and then the last parameter, of course, is the is the actor is the actor that's we we we we want to create the actor we want to create. Then we send our message to add a customer to then we then then we create our message to add a to add a customer to our subscriber actor. We send it we send it um and then um included in that, of course, is the actor ref for the reply actor and the actor ref for the data store actor that that that that that our system will will need to know. Now that we have looked at both Akka and ZIO Actors, let's do a quick comparison of the two. This comparison is not an exhaustive list

Of course, Akka being a more mature project does currently offer more choices in some of the some of these features and more features. Such as for persistence, Akka gives you options for multiple types of data stores to use, whereas ZIO Actors today only supports JDBC. For monitoring, Akka has Lightbend Telemetry, which is excellent, but to get it you will need a Lightbend subscription. For ZIO Actors, you have ZIO's ZMX, which is available as an open source project as part of the ZIO ecosystem. Both Akka and ZIO Actors support JDK 8 and 11. They will probably work with newer JDKs. I just have not tried it. They both support Scala versions 2.11, 2.12, and 2.13

ZIO Actors has been tested and works with Dotty, so it will support Scala 3. I'm pretty sure Akka will be supporting Scala 3 also, but I couldn't find any information on it, hence the question mark for now. Scala.js, as far as I know, Akka does not support it, but ZIO Actors will be supporting it in the next release. The PR adding this functionality is being reviewed as we speak. So, the future is bright for the actor model of Scala. We have two great implementations to choose from, and the icing on the cake is ZIO Actors have interrupt with Akka. That means it has the ability to send and receive messages between ZIO Actors and Akka. We have This means that we have the ability to send and receive messages between ZIO Actors and Akka typed actors

And and I'm pretty excited about this feature about this feature. Now, to wrap up, uh some resources. My sample app that contains the snippets from this presentation is will be available on GitHub by the end of the week. Think of it as the Hello World for the actor model using Scala. Over the next few months, my goal is to blog about Akka and ZIO Actors in more details. So, please follow my blog and give me some feedback. On my slide, you'll see links to the Akka's documentation as well as ZIO Actors documentation and the the and ZIO itself's documentation as I wasn't able to cover it in today's talk. I would love to see more people contribute to ZIO Actors, so visit the repo on GitHub, take a look at the issues and see if there is one you want to work on

Do open new issues for any feature you would love to see added to ZIO Actors. Don't hesitate to join the ZIO Discord and visit the ZIO Actors channel if you need any help, whether as a user or a contributor. I went through my presentation a lot faster than I than I did during my presentation. Uh so, I so sorry if I spoke too fast, but I wanted to thank everyone for attending my talk, and I wanted to thank Alexey Grabovoy for inviting me to do a talk at this fabulous conference. Thank you all.