qwerescape

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:

  1. 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.

  2. 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.

  3. 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

Showing Posts 1 to 10

kelvinst

kelvinst

In my opinion you’re totally right in the title! :slight_smile:

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? :wink:

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

qwerescape OP

:grinning: thanks for the reply @kelvinst, all very good points, I will try to build on some of them.

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.

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.

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?

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.

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.

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

JEG2

Author of Designing Elixir Systems with OTP

I think you’ve got some great ideas. I gave a talk about some of this a while back that you may enjoy.

josevalim

josevalim

Creator of Elixir

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

kelvinst

Is this yet another encouragement or you mean is there a work in progress on this?

josevalim

josevalim

Creator of Elixir

Haha, it is an encouragement.

peerreynders

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)).

  • one process per user to hold state

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 for and while loops and if conditional. 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

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

qwerescape OP

@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 is gen_server

I 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

qwerescape OP

@JEG2 amazing talk, that is almost exactly what I had in mind and you put it so eloquently.

Where Next? Top

Trending in Questions Top

stjefim
Hello! Suppose you are building workflow (order / task / payment) processing system with the following requirements: Each workflow con...
New
jonnycharles
I’m in search of an Elixir library that offers PDF generation capabilities similar to Ruby’s Prawn. While there have been discussions abo...
New
spammy
I’m looking to build a personal workflow to quickly deploy web applications written in elixir/phoenix, for local consumption (ie not on t...
New
dli
Before I dive in myself, did anyone successfully sprinkle Hologram into their existing LiveView app? Looking for hints regarding: Addi...
New
roeland
Kia ora, We have been using elixir-google-api to connect to Google Drive. However, with the updates to Tesla due to CVEs this is now bro...
New
bottlenecked
Hi all, I wanted to ask how the community is dealing with post-release steps. Today we have Ecto migrations, which make sure that the db...
New
rahultumpala
Hello, I have an Elixir backend that implements a custom protocol over TCP. I want to load test the backend and assess the performance o...
New

Other Trending Topics Top

JesseHerrick
Hey, I’m Jesse and I’m the main contributor behind Dexter, a full-featured, lightning-fast Elixir LSP optimized for large codebases. It s...
New
jimsynz
Beam Bots (or just BB for short) is a framework for building fault-tolerant robotics applications in Elixir using familiar OTP patterns. ...
New
mcass19
ExRatatui lets you cook up rich terminal UIs in Elixir, powered by Rust’s ratatui via Rustler NIFs. Build interactive terminal applicatio...
New
Damirados
Hello everyone. After busy few months I am happy to announce v0.1.0 of Emerge & Solve. They are GUI (Emerge) and State management (S...
New
netoum
Corex is an accessible, unstyled UI component library for Phoenix that integrates Zag.js state machines using Vanilla JavaScript and Live...
New
ausimian
Emily is an Elixir library that runs Nx computations on Apple’s MLX. Install it as the default Nx backend and Nx, defn, Axon, Nx.Serving,...
New

We're in Beta

About us Mission Statement

Options

Thread Display Mode




Thread Preview

Skip Thread Previews