During @chrismccord’s keynote at ElixirConf.EU he briefly touched on the subject of different ways to set up systems that need to be scalable but still be able to track data accurately.
Now, there are multiple ways to approach this problem, each with their own set of trade-offs, but one of the possibilities would be to use an approach similar to ‘sticky sessions’:
When a user connects to a (web)application and interacts with it, the application can make sure that the user only can do things it is allowed to (like make only a certain amount of requests per hour, or will only spend as much money as they have in their account). But when we try to scale this up and we need multiple nodes, then this suddenly breaks down, because now a user might connect to multiple nodes at the same time, and perform requests faster than these nodes can synchronize.
So a possibility is to set up something that is known as a ‘sticky session’: Whenever a user signs in (or possibly already earlier), it is decided to what node the traffic of this user will always be redirected. This is of course to be built in such a way that the amount of users that interact with the application are somewhat evenly spread across the nodes.
This is a really nice idea, but usually it is implemented in a load-balancer set-up before we enter the application (that is written in Elixir or anything else). There is a nice article that goes into detail about setting up such a kind of system using HAproxy as Load Balancer.
However, there are two drawbacks to this approach:
- You introduce an external dependency on the Load Balancer to the system that might not be required. (do note that It might very well be the case that when your application grows so large you might want a load balancing layer in front of your cluster of outward-facing nodes anyway, to make sure traffic is always properly re-routed when nodes fail, since the DNS doesn’t do that for you.)
- You’re only able to balance requests based on sessions, but maybe your application logic has its distributable stateful thing at a different logical layer. An example: In a system where users play together in a game (say: poker), it would make sense to distribute these games, rather than the individual users.
So that’s why I’m currently looking for a way to properly and reliably do this type of thing right inside the BEAM. For proper nomenclature, since we might distribute different (stateful) things than just sessions, I think that ‘Sticky Process’ might be a good name for this.
So what I’ve thought about so far:
- We have wonderful tools now like Swarm to specify what node we want to run what type of process on, and how to redistribute it (potentially keeping its state!) when its node goes down. I think we can build a ‘which one of this group of processes do I want to call based on whatever you pass in’ wrapper on top of this although I am not entirely sure yet how it would look/work. The other main thing I am unsure about here is how the supervision would look/work (Because we want a ‘pool’ of processes where members of these pool all live at different nodes).
- When a node goes down, we could either choose to redistribute their sticky processes’s state over the other nodes’ sticky processes, but maybe it is a lot less complex to just move the node’s sticky process as a whole to a different node until it comes back. And if we want to make sure workload is distributed evenly, we might run multiple sticky processes of the same type on each node: Say we have nodes A, B and C and D and each have three sticky processes, then when A goes down, A1 will move to B, A2 to C and A3 to D.
- Obviously we want to use the Quorum-option (i.e. ‘majority wins’) of Swarm so we have strong consistency; if eventual consistency is good enough for your application, then using e.g. CRDTs might be a better option than something like sticky processes.
Now, I’m also wondering if there are edge cases that have not been considered yet, and also would love to hear if there are people already doing something like this in their applications .
~Wiebe-Marten/Qqwy