qwerescape
The concurrency model of Elixir is really fascinating, along side immutability, they are my favourite things about Elixir. Recently I’ve been thinking about how Elixir can help me manage state in a distributed web application. I am looking for people to enlighten me to agree/disagree with my thinking.
In my opinion, stateful applications are a lot easier to reason about than a “stateless” application, I am putting “stateless” in quotes because I haven’t really seen a useful application that doesn’t have any side effects/states. I think the reason why people choose stateless because there is no good technology stack that allows them to do stateful safely. In my past experience with web applications in other languages, there are really 3 ways to manage state:
-
Client side state: basically the entire state is passed from front to back in every request, the problem with this is if I have a web front end and a mobile front end, they will end up overwriting each other. To synchronize them is difficult, and I’d say any technique can only shrink the window where the race condition happen instead of preventing it.
-
Server side state: this is very problematic in a clustered environment, you might end up with multiple states on different servers that all represent the same user. You can kind of solve it by sophisticated routing based on cookie/request param to make sure the same user (even with different devices) always end up on the same server. On top of that you need to make sure you have some thread safe data structure on the server side that can load/save the user state.
-
Database state, stateless server/client side: the most used case, unfortunately also the slowest one because in most cases it means a network hop. If combined with a stateless frontend + server side, you can get into race conditions: the husband trying to checkout on the website, but the wife is deleting the same cart on the mobile app; customer tries to add the same product on both web and mobile at the same time, but the business rule is that 1 custom can only buy 1. To solve those issues, we often resolve to adding database constraints, unfortunately any logic that we put in the database layer is not unit testable or easily understandable.
So all those problems, I feel like I can solve them by Elixir!
In an Elixir app, my mental model is processes interacting with each other, processes are globally addressable, and they don’t all have to live in the same server. So if I have:
- one process per user to hold state
- that process will periodically/asynchronously persist the state to the database just in the case
- any front end request can hit any server in the cluster, but since processes are globally addressable, i can always route the message to the right process
- if processes crashes, the supervisor will restart process with the last known state persisted.
- since all the state is in memory, I’d imagine it will be very fast.
I am not very experienced with Elixir, please share what you think, am I missing something that will prevent this from working well?
Thanks
Trending in Questions
Other Trending Topics
Categories:
Sub Categories:
Forums
Popular Tags
- #ecto
- #liveview
- #troubleshooting
- #learning-elixir
- #deployment
- #library
- #erlang
- #testing
- #genserver
- #mix
- #absinthe
- #remote-other
- #otp
- #plug
- #how-to-question
- #macros
- #postgres
- #channels
- #elixirconf
- #exunit
- #discussion
- #code-sync
- #javascript
- #podcasts
- #onsite
- #dialyzer
- #docker
- #authentication
- #umbrella
- #full-time-contract
- #podcasts-by-brainlid
- #ecto-query
- #elixir-ls
- #blog-post
- #phoenix_html
- #iex
- #graphql
- #ai
- #genstage
- #elixirconf-us
- #websockets
- #supervisor
- #advent-of-code
- #distillery
- #processes
- #api
- #forms
- #metaprogramming
- #security
- #hex











Showing Posts 1 to 10- Show Best Posts
- Show All Posts (oldest first)
- Show All Posts (newest first)
kelvinst
In my opinion you’re totally right in the title!
Well, I do actually agree with this statement, because I think state itself is inevitable, there is no such fully “stateless” application. I guess what people might want to say when they say “stateless” is kind of a temporary stateful application, not relying deeply on a given state.
Yes, you’re right, but you also can solve with other things. Maybe Elixir makes it a little easier to solve because of the agent model, but let’s not forget there are other solutions for the same problems, and actually some of them are better in some aspects, like performance.
Still, if it’s a feature that one user can affect the other user state, you’ll have to make your own code to manage one user (process) updating the other user data. This can be very complex, and using a db you get it done quickly. I mean, it’s not a bad idea, I am just showing the trade-off’s, and maybe, for some applications, this is actually the best way to do it.
Again, more code for you. But maybe, it would be a good idea to make a PoC of it. I actually can be the birth of a new framework, why not?
This is actually a very nice idea!
Sure, but will be a little bit more expensive. It depends on the size of each user states and the simultaneous users of course.
Well, resuming it, I really like the idea, all I can do is to encourage you to prove the concept and turning it into a framework like plug somehow! I would also really like to be part of this if I have some time.
Thanks for sharing!
qwerescape
I agree that it’s a trade off. The “states between user” part is interesting, I didn’t think of that. I am thinking since each user is a process, the users can communicate among themselves using message passing as well.
I am hoping to use an existing library for a global process registry, the client side will have to identify itself through an ID, and the server side code will identify the process through that ID in that registry, so you are right, it is extra code.
thank you for the encouragement! I am thinking of building an ecommerce platform with this idea, I feel the ecommerce platform that I am working with right now has quite a few of those problems. I am open to suggestions.
Thanks
JEG2
I think you’ve got some great ideas. I gave a talk about some of this a while back that you may enjoy.
josevalim
Orleans is a framework for .NET that provides that out of the box. The Elixir ecosystem has all of the building blocks but nothing that provides the full experience (yet).
Here is another good talk from Caitie McCaffrey covering some of those patterns: https://www.youtube.com/watch?v=H0i_bXKwujQ
kelvinst
Is this yet another encouragement or you mean is there a work in progress on this?
josevalim
Haha, it is an encouragement.
peerreynders
This reminds me a bit of the argument that immutability is pointless because you have to change something somewhere to have an effect.
The way you get something that is “easier to reason about” is by pushing side effects or in this case state to the edge of the system.
So the issue with state isn’t that needs to be completely eliminated but it needs to be reduced to the essential minimum and it needs be located where it belongs.
Also there are many kinds of “state”. For example REST makes a distinction between application state which lives on the client and resource state which lives on the server (so domain state lives on the server while session state resides largely with the client (some concepts like transactions are modelled as resources)).
Somehow this smells like mapping OO onto processes, i.e. use processes as containers of state. Have you looked at To spawn or not to spawn yet?
“Goto” is a useful tool but people abused it because it was easy to do - so it was abolished and replaced by the more constrained concepts of
forandwhileloops andifconditional. When it comes to state we can’t abolish it because it is essential to what we need to accomplish - but we need to be very disciplined about how and where we use state.Ultimately I find a process “easier to reason about” when it’s behaviour does not depend on internal state and this applies even more so to a group of cooperating processes. State has to live somewhere but that is no excuse to let it appear everywhere.
One has to be careful to not confuse “this is easy to accomplish with state” with actually being “easier to reason about”. In my experience systems embracing immutability and statelessness for the most part are in fact “easier to reason about” even if they require a bit more code.
michalmuskala
We have GitHub - erleans/erleans: Erlang Orleans · GitHub which is very promising, I’m not sure, though, how ready for production it is.
qwerescape
@peerreynders thanks for your reply. I actually agree to almost everything you said about immutability, pushing side effects to the edge of the system, keep state logic small and contained… There are certain places I feel you generalized my statements in ways that I didn’t intend.
You touched on a great point of “easy vs simple” with
goto, and I do want to discuss why I think it’s simpler to reason a system in which processes hold state. I believe systems should handle states in the right context, and absolutely not let the state leak, an example is a pure function: it could introduces variables that hold state, but those states are local; and I think another good example of this isgen_serverI want to take an analogy to explain my original thinking of “stateful” vs “stateless”, hopefully it could explain my thinking better and spark more discussions.
Take the example of an analog booth that sells analog (paper) movie tickets. There is 1 agent sitting in the booth with 100 tickets in his drawer, and there is a line of people waiting outside the booth. The agent serves the customers 1 by 1, by taking the money and handing out the tickets. A while later the agent realizes that he is low on tickets, so he asks his supervisor for more, and his supervisor goes to the ticket vault, fetches 50 more tickets and gives it to the agent. As the booth gets busier, the supervisor calls another agent over, opens another window and hands the new agent 100 tickets to sell, since there are 2 windows now, customers are getting served faster. Eventually every agent sells out their tickets, and when they ask their supervisor for more tickets, the supervisor checks and vault and says “everything sold out! congrats, go home”, the agents all go home. (or maybe leave one agent behind to let future customers know that we are sold out)
I think the parallels that we can draw between that example and OTP is clear. Now the version of that example with stateless backend that only persist state in the database is something like this:
There is a ticket booth that doesn’t have any mechanism for people to line up, but the supervisor has a pool of agents, for every customer that shows up, the supervisor asks an agent in the pool to deal with that customer. The agent doesn’t know anything about the tickets (how many is left in the vault, how many he is allowed to sell), so he runs over to the ticket vault, grabs the tickets if available, runs back to the booth, hands the customer the tickets. Situation gets sticky when 2 agents arrive at the vault at the same time but only finds 1 ticket left; or when the tickets are sold out, the agents wouldn’t know because they are not allowed to remember anything about the ticket vault.
In my opinion, the first scenario is a lot simpler to reason about. Even though the agents hold state, it is a local state that is completely opaque from the rest of the system, and in a way I’d consider the agents to be the edge of the system because the ticket vault is another system.
Thanks
qwerescape
@JEG2 amazing talk, that is almost exactly what I had in mind and you put it so eloquently.