4 ms·
>> With that being said, the actor model has a lot of inherent complexity. If you don’t need it, don’t use it. But if you really do need lots of concurrency and
by lostcolony 4y ago
>> With that being said, the actor model has a lot of inherent complexity. If you don’t need it, don’t use it. But if you really do need lots of concurrency and lots of mutable state, it’s often a great choice.
I kind of disagree with this. I mean, it's kind of correct, if you literally have a sequential problem with immutable state you're gaining nothing from it, but you're also losing nothing (you...will have one actor, no messages being passed, so the only cost is the syntax of the language; the actor model isn't adding any complexity because you aren't using it).
But, a lot of problems we've historically learned to view as sequential are in fact concurrent. Deeply concurrent. We've created entire concepts specifically to impose sequential processing upon innately concurrent activities.
As an example, almost everywhere we use queues we could instead model as unrelated processes able to be run concurrently. You still may have a bit of queueing/scheduling for unbounded processes, but I know from production experience that that is far, far simpler to do and get right using actors than a traditional queue/priority queue and worker(s).
And that's where it shines. It encourages you to start thinking about what in fact -should- be done concurrently, by making that easy rather than a chore as it is in most other languages, and that leads to -less- complexity.
- bcrosby95 4y agoI'm curious what you see as so different in an actor vs a queue with workers. An actor's mailbox effectively acts as a queue. Is it the supporting structure surrounding an actor focused library/language? Lots of person-hours have gone into making them to work well. Some home grown queue + pool of threads working off that queue... not so much.
- ackfoobar 4y agoYou're exactly right. Some examples: - Some common patterns. E.g. actor hierachy and supervision, circuit breaker. - Persistence. The actor persists its state (changes) and can wake up from a sleep. - Clustering. The actor can live in another node, and you interact with it using the same API.
- ackfoobar 4y agoWell I misread and couldn't edit now. What I meant is that you can launch a co/go-routine and have a channel that only it can read. That is like 70% the power of having an actor system. --- > an actor vs a queue with workers See the sibling comment. TLDR: Actors each has their own queue. The orderings in each queue are independent. This can help with parallelization if it fits the shape of the problem.
- imtringued 4y ago>What I meant is that you can launch a co/go-routine and have a channel that only it can read. That is like 70% the power of having an actor system. You can also iterate over an array in parallel and only modify the current element and that is like 70% of the power of having an actor system. "Structured concurrency is a programming paradigm aimed at improving the clarity, quality, and development time of a computer program by using a structured approach to concurrent programming." - Wikipedia
- lostcolony 4y agoSo pulling from the alluded to real life example, we wrote a job scheduling system. Now, each job could be defined by a lifecycle. It wasn't just a single fire and forget command, but multiple in time (a prep start, start, stop, clear, commands), and also tracking status of an external resource (looking for error states or ambiguities, etc), and could also be modified (change of start and stop times, including "now" for both). Now, using actors, this was trivial to do. Each actor was basically a glorified state machine, with each command, status update, etc, a message coming in that updated the internal state, and caused it to change state, and, where appropriate, update anything it needed to (such as the internal timer for when it would stop the job). We loaded up everything that had to happen in the next 12 hours, via an actor that would wake up and load more periodically (with an actor registry to find and confirm what existed already, as well as used to route incoming commands from users to change jobs). There is no complexity in managing a global state of priority between the actors; that's a 'solved problem' by the time slicing the underlying actor engine is doing for us; it's well tested, well proven, and invisible to us. Our implementation allows us to treat each job as existing in isolation, and write our code as though it and it alone is the job that needs to be executed. There is no global synchronization state to manage (well, with the caveat of taking the infinite list of future jobs, and ensuring we have actual processes equating to those scheduled to start in the next 12 hours, but that's a comparatively simple problem, and a proper use of sequential ordering. We want to execute on these jobs first), nor should their be based on the problem we're trying to solve. Using a queue and traditional threading model though? Well, we'd need a synchronized priority queue, as items could have their priority changed at any time, from multiple sources. We'd also still need a registry. We'd need a pool of workers, that are mostly just blocking...hope we don't have > (number of workers) things needing to execute concurrently. Our complexity to manage all of this is high; the level of testing needed to validate that there aren't emergent bugs, also high. Our semi-realtime requirements are probably out the window (high contention on the queue, or high enough concurrency to exhaust the thread pool could lead to very measurable delays in updates). And ultimately, it all comes to the fact that the queue exists to pretend that the state of the world is sequential, and that we then had to then contort ourselves around to bring back to a state that allows for suitable concurrency. Updates to a job can end up killing a worker and inserting something back onto the queue, can remove something from the queue and reinsert it at a different position, etc, all while other workers are trying to access the queue simultaneously. Actors are the simpler solution. The comment about an actor's mailbox being a queue is missing the point (though, amusingly, demonstrating it). Queues are sequential. Actors are capable of doing only one thing at a time, and so when they are requested to do multiple things, they will do them sequentially. A mailbox filling with things that don't demand sequential processing is a code smell in an actor system. This is useful if you have limited resource access (queries on a DB connection, say), or need things to be done sequentially, and are very much akin to threads. But if you actually want multiple things concurrently...you should have multiple actors, not send them into the mailbox of one actor (which is very much the queue situation mentioned above; you imposed a sequential ordering on things you want done concurrently. Why?). That's the key difference; in most other languages, the queue is taking multiple things you actually want concurrently, and imposing a sequence to them. In the actor model, your queue is in fact things you DO want done sequentially; the things you want done concurrently should get federated out to multiple actors.
- helge9210 4y ago> so different in an actor vs a queue with workers In short: different computational model -- Actor model vs. Communicating sequential processes. This really helped me: https://youtu.be/7erJ1DV_Tlo https://youtu.be/7erJ1DV_Tlo Edit: added link to video