3 ms·
My experience using channels in Go, was that I replicated state across "actors", similar to microservices (which is one of many problems that microservices have
by klabb3 4y ago
My experience using channels in Go, was that I replicated state across "actors", similar to microservices (which is one of many problems that microservices have vs a monolith). Worse, this state is not in sync, so you end up worrying a lot about logical race conditions (if you're paranoid like me). I ended up still using channels, but a single message bus with a single threaded dispatch loop.
I am curious about if redundant state and state synchronization tends to be an issue in medium-to-large Elixir apps? If not, how does one design the component/actor hierarchy to avoid such issues?
- sieabah 4y agoWell each process has its own state, so if you needed to sync that you'd be storing it in mnesia, ets, or an external DB. Processes can watch and get messages about other processes if something were to succeed/fail.
- sb8244 4y agoI've worked on a variety of different size apps, but I haven't run into many issues with state synchronization yet. Generally, I try to keep data as close to its source of truth as I can. So if I need to synchronize state between actors, it would be to achieve a task and not to have duplicated data. This could be an artifact of how I build systems. I have heard of some people that build really small actors and do a lot of data exchange. Without knowing details, I'd lean towards that being an anti-pattern.
- throwawaymaths 4y agoYou tend to not write concurrency in elixir. You tend to borrow concurrency from your libraries and frameworks, which usually are fairly buttoned down. Most websites are basically stateless data fetch/transformation agents, and even the stateful ones are usually one process. Why muck around with all that when your framework does the right thing for you out of the box?
- bostonvaulter2 4y agoI'm not super familiar with channels in Go, but one way to avoid the need to synchronize state in Elixir is to use ETS (Erlang Term Storage) which is a way to store data (terms) in memory. If the state is in ETS than (optionally) any process on the node can read the state. Often this is paired with a single process that is allowed to update the state so that the state is always updated atomically. Often this pattern means that you don't need to distribute the same state to multiple processes, because instead they can just read the state they need from ETS (which is pretty fast/cheap).
- toast0 4y agoSo in Erlang (and I assume Elixir), the idiom is that the Process (Actor) holds the state. If you want to change the state, you send the Process a message, if you want to find out something about the state, you send the Process a message. You shouldn't be keeping the same state in multiple processes, because of all the problems you mention. Sometimes you have requirements where it's hard to assign any given piece of state to a single actor though, that's going to be challenging in any language. An ETS table (the OTP in-memory key/value database, more or less) is isomorphic with a Process that holds the data, although the reality is different. Mnesia if everything is done with transactions might be as well, although where I used mnesia, we did not use transactions and it was helpful to understand how the data flows between mnesia nodes, and when you might read outdated data.