 Cool. All right, so I've got I've got this buddy and he is super into synthesizers right now and we're both kids of the 80s, so I'm like right there with them He used to have this this cute little one here. This is called the OP one and It's it's really easy to do just fun stuff with this So this is this has been now he fiddles with it and I've played with it some and it's super cool But the other day he sent me a picture of another synthesizer he saw and it looked like this That thing first off that looks awesome. That is one cool looking synthesizer, you know, you can do some rad stuff with that but number two How do you do this stuff with that? I mean there it looks like there's like a hundred knobs on that thing and Several different like equalizers and there's some keys and there's just so much going on to that I think that's a real good example of what OTP looked like to me when I first tried to learn it It's just I knew the power was there, but I wouldn't even know where to start with it So what is OTP anyway? So there's a lot of things to OTP But basically it's a collection of libraries and a hierarchy of supervisors and workers also called the supervision tree So there's of the libraries. There's a lot of libraries, but kind of the main ones are the supervisor Gen server and Then agent and task which are abstractions of gen server And there's a lot of other other stuff like gen stage gen event gen FSM Actually, gen FSM is gone and gen events on the way out but we do have gen stage and then there's other stuff like application ETS and MNESA NESA, I'm not really sure how to pronounce that it seems to be a debate on that And then dialyzer gen TCP observer and that's probably like just half the stuff There's a lot to OTP, but the main thing when people say you should learn OTP They're talking about supervisor and gen server for the most part at least to get into it and agent and task So to be able to use these you need the the basic building blocks, which are processes so Processes any looks or as you may know are not like an OS process. They're super lightweight You can just spin up like a thousand on your laptop or more than that Like they're they're really lightweight. You can spin them up really easily. So let's go ahead and see what that looks like So we're in IEX here and we're gonna spawn up just a quick print out hello elixir days So we've got our spawn function and then we're giving it The function that we want the process to run so the prod so spawns we're gonna enter it And it's gonna kick up a process and that process is going to run the function We gave it which is print hello elixir days So I run that there we got our hello elixir days and then we have this if you haven't seen it before weird-looking Hashtag PID with some numbers after it and that's the process ID So we actually want to track that so we can see what's going on with this process So we'll run that again and this time we'll capture it in a PID variable We run that prints elixir days and then we see a different process ID right there And now we check on the process. We say process alive. What's going on with the PID and False it is no longer live and that's because process is they run the function You give it and then they gracefully die so that they're real simple They just give me the work. I do the work and then I'm done and So that's great so now we can take this newfound knowledge of processes and start using them our apps and then Eventually you're gonna give it a big task and it's gonna fail halfway through that task And it's not gonna die gracefully and it's not gonna give you the answer you need So now it's time to figure out some error handling for these processes So something you'll hear a lot is Let it crash which is a common thing said by people that you know use Erlang and elixir 2 as well Which to me Sounded terrifying like let it crash. I don't want to crash my app. That does not seem like a good solution But that's because I wasn't thinking of my app as a bunch of different processes. I was thinking of like the app And when you think about like if you think outside of that you think about like what's the best way to fix something? Whether it's an app like you know Photoshop or something or it's your entire operating system or your phone or your Xbox PlayStation Electronic toy your TV like what is the IT crew IT crowd approved way to fix that problem anybody Have you tried turning it off and on again and and this works like this fixes like I don't know 50% or more of your problems like it's like this thing won't work. Well, did you try restarting it? Well, no, I know that'll work and you restart it and it does work Why does this work? So the reason that letting it crash or turning it off and on again works is because we're giving it a Fresh state so whatever happened there is some transient bug that happened that messed up the state and then Excuse me, and now it doesn't work, but when you restart it that weird state is gone and now it works So the supervision tree is based on this principle We have the cluster of different our processes and we can turn them off and on again at an individual level so Supervision trees basically these are all an individual process We have the worker processes, and then we have our supervisor processes And the supervisor processes all they do they just have two really simple jobs So they start up a new process They start up the workers underneath it and then they keep an eye on it and so that if it crashes it restarts that worker So like let's do an example real quick this worker down here on the bottom right say that's it's doing some work for you for your app And it hits a bug and it crashes not gracefully. It just dies halfway through its work The supervisor above it will see that it's crashed and Automatically restart it and restart it isn't actually what it's doing. It's just starting a brand new process with that same function and So it'll do that and it'll try it a couple of times And if it doesn't fix it then that'll kind of cascade up and the supervisor above it will then try and So on and so forth so then I'll cascade up and if that that supervisor will restart the one below it And then that one will start the workers underneath it and that's that's kind of how it works If the top supervisor crashes well, then maybe it's time for a rewrite So the the thing that I thought when I was first learning about this is Like okay, like that that makes sense. That seems really cool. But what if you just have bad code like Restarting and rerunning my badly written code with the bug in it isn't going to make the bug go away and You're right. I was right. It wouldn't do that But so if OTP doesn't magically fix your broken code, what does it do that's so magical? So there's this great article called the Zen of Erlang by Fred Abert And this is one of the articles that really made it start to click for me In in his article. He talks about the ease of finding bugs in production He talks about how there's basically four different types of bugs in your app You've got your core features and you have your secondary features and Of those the bugs that they have are either easily repeatable bugs or transient bugs So with the core feature you have a transient or you with the core feature you have an easily repeatable bug Those are really easy to find. It's really easy to find those kind of bugs Those are the kind you find your basic testing like no problem and the secondary feature same Those are also easy to find when they're repeatable bugs, but sometimes you miss it because it's just some oh Forgot our app did that we should probably fix that But the transient bugs these are the ones where you get a bug report and you read what the user says and you're like I have I have no idea how they made the app do this I don't know and these are the ones that take most of the time trying to fix You have to figure out like what weird state did they get the app in to be able to have for this bug to kind of creep up and The thing with these kind of bugs the core feature Repeatable bugs should never go into production like that just shouldn't happen the secondary feature ones They shouldn't happen, but they do because we forget about our apps get big and we miss them sometimes But the transient bugs whether it's a core feature or a secondary feature Those happen all the time in production because they're hard to catch because they don't have a really obvious like Steps that make it happen. It's some weird Element of state that makes it that makes it have the bug But the really great thing about these transient bugs is these are the ones that are handled by resarts so the The repeatable bugs Restarting isn't going to fix that but those you should be able to catch before they go to production anyway But the transient ones restarting it which the supervision tree will do automatically for you that will fix the bug so One thing that makes the supervision tree work is that all the processes are completely isolated and memory independent So when you say restarting you really are just restarting that one process not having to restart the whole app But because they're completely isolated like that They still have to be able to work together and they do that by sending messages to each other Excuse me So earlier we are passing around or we are tracking that that PID or that process ID of that first process we spun up So that is actually so every process has a night has a process ID and that is basically like a mailing address to that process So every every process has built in the ability to receive messages It doesn't automatically do something with the message But every process like when you spin it up is ready to receive a message So let's go ahead and see how how this message sending between processes works So I'm a big fan of fantasy football and it's never too early to get started for the next season So I made this past summer. I was I made an elixir API wrapper for fantasy football nerd Which has got like just kind of different stats and we're going to use that to get We're going to make an app called fantasy team real quick that Stores that the players that are in our team and then use the API to grab their stats So we have that all to track and then we can do some fun stuff with that later So the first thing we need to do is like, okay, so how are we going to hold that team? How we're gonna hold the state? And we could create a database. There's lots of great ways to do that And maybe if we went to a production with this we would use ecto to create a database or we'd use Erlang term storage or ETS or Menesia, however you pronounce that and there's some other options too But for now, we're just going to store it in this process and that's going to be totally fine for now So here is a really basic Process that stores state and lets us add and remove players and then send a message to the process to say Hey, who's on the team? So we're gonna if this looks weird to you for some of you I'm sure this is like really basic but for me. I saw that like this didn't make sense to me So we're gonna go through it line for line so that there's any other means out there. This will make sense to you, too So we started off with a with a simple function called start link and this is just a common naming pattern And that's how we're gonna kick up the process You would run fantasy team basic start link and then that's gonna run spawn link and What we're giving spawn link so it takes three arguments, which is a module a function And the argument we're basically giving instructions to the process of what to do So first we've got the double underscore on both sides all cast a module All that's gonna do is take our module name fantasy team basic and then plug it in right there And then our second our second one there is the atom loop That's gonna say run the loop function from this module and then it's gonna this is our list of arguments We only have one argument. We still put it in a list But that is an empty map because what we're gonna start with is there's no players on the team It's just an empty map waiting to receive data So here you can see basically the process is gonna start up and then it's gonna run fantasy team basic loop With an empty map argument So that's gonna go down to our loop function So loop takes that state of the empty map and then we have received you and so what that does before we had A process it printed out elixir days and then it gracely died So this isn't gonna die because it's gonna sit and wait to receive message Messages so now it's watching its mailbox waiting to receive stuff So when it gets a message at first checks does it match this and then if it doesn't match the tuple It starts with the add atom then it's gonna move down the line So we'll go ahead and go through the first one say it matches So it's a tuple with the add atom at the beginning and then the name would be a player name So like Russell Wilson and you know, it's Russell Wilson string So then we would take that name and then we would search it and this is a function We're not gonna look at this is the one that's using the API behind the scenes to go ahead and pull all the data So we run fantasy team dot player dot find Russell Wilson and then it's gonna return and like all this information like College, Wisconsin position quarterback team Seattle Etc. So we do that and we find a sign it to the variable player And then we're gonna take that and we're gonna add that player with his stats to our empty map So now we're defining new state with the map that now has Russell Wilson as the key and his all his stats is the value And then if we just stopped right here, it would be like great We did that and then the process would just die So we actually need to jump back to the loop function to kind of recursively run it again So that now it goes so it takes a message does the stuff with it And now it sits again waiting to receive a message with state now being the map with Russell Wilson So our second message that it can receive here is remove and it's similar to the ad We don't need to look up the player We can just Redefine the state as whatever the current state is minus the player that we're that we're trying to remove and then we loop it And then our third message that we receive is our team Adam and this is asking to return the state So the first thing it does oh and it takes the second one there is the PID So that's like where to send the state to so the calling process So if you're using like IEX and you you're sending in a message you would put self right there And then that would give give it the process ID to send a message back to So we'll do that right here send a message back to the calling process with the state and then we loop back up to the top again So here's our process module. Let's go ahead and see in action real quick Now I don't want to embarrass myself like Dave Thomas just did so I'm just gonna do it through the slides All right, so we start off and we assign PID to Run our function fantasy team basic dot start link and there's our return of the PID and So now our process is up and waiting to receive a message. So we're gonna go ahead and send in a message Send to PID the message Tuple with ad and then Russell Wilson. So we get the return at Russell Wilson Yep, and we'll do it again. We'll add Doug Baldwin and then extra we're gonna remove Doug Baldwin So we do the same thing but with the remove Adam in there And then finally we send the team the tuple with team in it to get the team back So we don't get anything back as I said before IEX or any process isn't Originally ready to do something with messages or it sees but it still has the mailbox in there So we can just flush it out and then there so we run flush and it's gonna spit out our message and up there It is Russell Wilson position quarterback, etc so this right here is a really common pattern in elixir and It's so common that they made a generic library to emulate it. So This works really great, but it's actually missing lots of little edge cases Like just compatibilities deadlocks message ordering dynamic tracing some tail call stuff and half of that I don't even understand But I don't have to because people have been working on this generic server that's gonna do this for me for 30 plus years So now we're gonna learn about gen server, which is the right way to do what we just what I just showed you Hmm. Alright, so on the right we have the one we just wrote our basic and then on the left We have a gen server version and so it looks like a lot more code But really the top part is mostly convenience functions So it's really not that much more code than the basic one. We just went through So to start off we just run we put use gen server and that's gonna call up that library So now we have all the power of the library kind of in here And then we're gonna jump down to the parts that are kind of more the same between the two So we start off with our start link function and that's again the common function name to start one of these and Now instead of doing our spawn spawn link function on the right We're gonna do gen server dot start link and we're gonna provide that again the double underscore module Which is gonna replace that with our module name, which is fantasy team dot my gen server And then we don't have to give it the loop function because gen server kind of has its own functions Like you don't need to tell me what to run. I know how I know how to run a loop And so instead our second argument there is actually the argument We're just gonna put an okay to pull there and the third option is Or the third argument is options and we're not gonna do any options. So we just do an empty list So this is gonna start up that process And the first thing it's gonna do because we did use your gen server It knows to jump back into our code and say alright Where's the in it function because as a gen server I know I got to run in it first And so we're gonna define in it and it takes our our okay to pull and then it returns and An okay to or sorry. Okay, Adam is the argument It's gonna return an okay to pull with the second argument is our state So that's where we put the empty map where before we put it in that initial call Here's where we put our initial state, which for us is just gonna be an empty map And now we're gonna go through and see okay, so this is a little bit more interesting This is a bit different than it was before so instead of having like one master loop receive block Jen server actually handles each message as its own function So Jen server has like two types of messages that it receives which are cast or calls And a cast is kind of like sending a postcard like it doesn't have a return address like it doesn't need a reply It's just saying hey here you go Here's a message do this thing and a call is like mailing a self-addressed stamped envelope like here's my return address And I want you to send me something back So for our add and remove messages, we don't need anything back We're just telling it like hey add this player remove this player and then for our team That'll be a call because we're asking tell me who's in the team so for this one here, we've got handle cast and We've got our message right there as the first argument before we had it as kind of that Part of the receive block so we have our add and name and then we have state defined in that function So rather as an argument in that function since we don't have the wrapper loop state We actually list the state as an argument in each function that we go through So these two lines are exactly the same. We're doing just like we did before We're grabbing the players stats from our API in the background And then we're defining the new state as adding that player to the existing state and Then before we re-ran the loop function with that state this time Jensen is expecting a Return of a tuple that starts with no reply because this is that this is a cast and there is no reply message It needs to be sent out and the new state which now is the map that has Russell Wilson in it. Yeah, new state So same thing here for the remove message. It's basically the same as before we have our message right there We have our state which before was again defined once in the loop We have it as the argument for each one of these functions And then this line is exactly the same and then we have another no reply to pull with the new state in it So for team instead of handle cast. This is a handle call because we're receiving a call message and This one we're not actually doing anything to the team. So we don't need to do any other lines we're just gonna have that return tuple and This starts with reply because it's it is a reply. There's gonna be a message sent back The second argument is state and that's actually the message that's being sent back The third one is also state and that's the mess. That's the state that's being saved So if if we had another function here that was like handle call a quarterbacks this second state That would be the quarterbacks and the third one would still be the entire team because we're still saving the entire team And so that that's how that works. So how do we actually make these casts and calls? That's back up at our convenience functions So we've we're gonna fill out our API here and we've got Add so we have a basic add function and this is what people are gonna. This is how people will use This module and the first thing is the process ID And this is the process ID of the gen server that you started up. So you're telling which process to add these players So we got the PID there and then the name and then we do gen server dot cast With that PID again and then the message add and name And so now that's going to be handled by the stuff. We just define further down and Then team is the same sort of thing. There's no argument other than just there's just a single argument of the PID We don't need anything else and then we'll do the gen server call with the PID and the team and That's it. So now we have our gen server ready to go But the one thing that's kind of annoying about this I'm not planning on Releasing this to have a bunch of different people track a bunch of different teams right now And so I'm just gonna make this a named gen server because it makes it a lot easier to deal with so To do that so on the right. We had the one we were just looking through and on the left We're gonna name it now So first thing we do a module attribute with the module name So now our name is fantasy team single server And we're gonna start plugging that in here. So we now have an option which is giving the Gen server a name when we started up And now we can replace the PID In these convenience functions with the with that module attribute And then because of that we can now take the process ID out of the function argument list So now all you have to do is run and just have the names you don't have to track that process ID So we're gonna go ahead and see this in action real quick Fantasy team single server dot start link So there's our return tuple but we with the PID in it But we don't need to capture it because it's a name server And it knows its own name So we don't even have to worry about that and then we'll run fantasy team single server dot add Russell Wilson We'll do it again. We'll add Doug Baldwin and then we'll remove Doug Baldwin and now we'll say fantasy team single server Dot team and then we get our return back, which is our map with Russell Wilson in it So that is awesome. This is working great for me. I'm really excited. There's one more thing We have agents and tasks. So the thing about gen server, we're mostly just using it to store state But gen server can also do a lot of stuff with kind of used for like parallel programming kind of kind of stuff Concurrent tasks that sort of thing and so what agents and tasks do they kind of They're kind of like a more focused abstraction of that where agent is just For doing this just for storing the state and tasks are for more doing the concurrent stuff And we're not really doing any of that but agents we could actually use an agent in place of the Gen server we just did and It's a lot less code. This is this is the full thing So it doesn't have to have the convenience functions because they are the functions So we just have like right there to find add with the name I'm going to look up the player and then we use this agent dot update To be able to place the player in that map. So these are can be really convenient personally I really like gen server because I feel like I have a better grasp of everything that's going on And then I don't have to change the abstraction later if I if I decide that oh, I need this to do other stuff But the nice thing if you're working with other people When you jump in here and you see agent, you don't even have to question. You're like, okay Well, this stores state. I don't have to worry about doing anything else. It's really obvious What this does where is gen server? Maybe if it's a more complicated app there might be more to figure out So let's build a supervisor real quick because right now it's still gonna crash and nothing's watching it so we'll build a supervisor really quick and That's it. That's all the code we need to supervise. So this is a module-based supervisor We're gonna start off with you supervisor to use that library and Then there is our familiar start link function again And then we do supervisor start link and we provide it the module again and this one doesn't need function or Or argument so it just has a Spot for for options and we're not going to give any options. So just empty list again And then when that when that process starts up, it looks back to our code say, alright My process just started. I need the init function. So there's our init function And the first thing we define in here is The children so before we had that tree and some of the supervisors had like multiple Processes multiple workers underneath it In this case, we're just gonna have the single supervisor with a single worker underneath it So we define one worker and worker just basically means not a supervisor It's just a regular old kind of gen server. And so we have our worker We provide at the module name again, no options needed and then we just supervise So run supervise and we give it our list of children, which is just the one and then we have our strategy One for one. There's several different strategies one for one Just means if you get if a supervisor has like four processes for workers underneath one of them dies It's just gonna restart the one and there's other other strategies in there But for us, we only have one processor anyway or one process anyway, so we're just gonna have it restart that one process and That's it. So now it's ready to go if our gender server dies Supervisors gonna automatically restart it and we're good to go. So this gives us the ability to kind of hold state and a lot of functionality inside of just a process and It has all the benefits of the supervision tree and there's a lot we could do to kind of go to the next step If we look back at this guy and we consider this kind of the world of OTP we we learned like that Which is not much but? You can play music with that like you can play music with that little section right there and now you can start to say like well Maybe I want to learn. I want to learn ETS and figure out how that can plug in well You're playing music already. So now you can just find that section and see how that works with the part You know how to do That's it there. I'm on Twitter. I've added some different resources that really helped me to learn OTP There's some great videos and there's some great books out there I'm Dave Thomas's book Sasha Eurek. I think might be here and he has a great book And the one that really really did it for me There's the literal little elixir and OTP guidebook by Benjamin 10 way how and I'm probably pronouncing that right? Incorrectly, but those are some great resources and that's it. Thanks