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?