7 ms·
I haven't given myself the opportunity to sit down and play with an actor model. But in the meantime, despite reading literature such as this, I have trouble en
by always_good 9y ago
I haven't given myself the opportunity to sit down and play with an actor model. But in the meantime, despite reading literature such as this, I have trouble envisioning what it actually entails day-to-day.
I think of my experience with channel-based communication in Clojure, Go, and even Rust's mpsc. And every time I feel an instant feeling of debt because I know I'm just one or two more channels away from misunderstanding the execution of my own application. Having to take out pencil and paper to reason about why messages are bottlenecking in this component or why I was wrong when I thought I knew how my application worked.
For example, we ripped out all of our channels in our Go application and replaced them with a couple mutexes.
When I read about actors, I get flashbacks to some of my worst struggles with channels. Where our application is split into disparate agents that everyone has to eventually map out on a whiteboard to understand even basic runtime behavior.
I owe it to myself to try it out, but I can't help but be discouraged by what I think they are. Can someone clarify? What does the actor model actually mean for your day-to-day code compared to futures, for example?
- eslaught 9y agoIt's worth mentioning that this isn't the only way to do distributed programming. Dataflow programming works quite well, and the newest state of the art systems are exploring ways to generate dataflow parallelism implicitly from sequential programs. See e.g. Legion from Stanford: http://legion.stanford.edu/ http://legion.stanford.edu/ In my personal opinion, message passing is going to be considered an evolutionary dead-end in the long term, and these sorts of more intuitive programming models based on generating dataflow graphs automatically will become more dominant. Disclaimer: I work on Legion.
- mmjaa 9y ago>It's worth mentioning that this isn't the only way to do distributed programming. Its not the only way, but it is one of the oldest that is still in production. If you take a train anywhere in the Western world, your life is being protected by best x-of-x systems based around the actor model. Just sayin' ..
- tree_of_item 9y agoI don't think that's true. Ericsson used it at one point. What makes you think every single train in the "Western world" uses it as well?
- mmjaa 9y agoI've worked extensively on these systems as a developer. ;) Siemens, Thales, Ericsson, Samsung - all have shipped SIL-4 systems based around the Actor/Voting model in the past, because it is an industrial standard. These systems are still out there, in some cases 20+ years later, in track-side systems as well as operations.
- eslaught 9y agoI absolutely agree that message passing and actor models are the way things are done now. I just don't agree that it'll be the best we can do in the future. As a programming languages person, message passing doesn't seem very satisfying. A lot of errors that you can get in parallel codes (e.g. deadlocks) aren't prevented by message passing. There are entirely new kinds of errors that are added (mismatched sends/receives). And there are errors that are nominally fixed, but really aren't: strictly speaking, in a shared-nothing distributed system, there can't be such a thing as a data race. And yet, two messages can absolutely race, and so you end up with the same problems just at a higher level of abstraction. Fundamentally, actor models are giant balls of mutable state interacting concurrently.... I'm sorry, yuck. The siren's call of implicit parallelism is the idea that you can generate a parallel execution automatically from sequential code. This was the objective of e.g. the auto-parallelizing compilers of the 80s and 90s. And that of course turns out to be an intractable problem. The difference today is that people are (a) more willing to work with new languages and abstractions, and (b) are willing to use dynamic analysis to cover the gaps where the static compile-time analyses fail. And at least in HPC people are willing to consider this because the next-generation machines are getting scary-complicated and it's not obvious how mere mortals are expected to program them under the traditional approach.
- amzans 9y ago
- polskibus 9y agoI checked the tutorials section on Legion's site, they don't seem to be very inviting for a stranger. Is there a better site that would help me grasp basic ideas about Legion and show its strengths in comparison to other paradigms?
- eslaught 9y agoFirst, I'll say: if you have any suggestions for how to improve the tutorials and documentation in general, we'd be happy to hear them. Even years into the project, we're still learning how to teach Legion. We're an open source project and would be happy to accept contributions, but even a set of first impressions would help. As for what resources exist today: If a video format would help, the bootcamp [1] has a more gentle on-ramp (despite the name). If you'd prefer text, there is also a language called Regent. The ideas are all the same, but the code does a much better job of expressing those core concepts. You could study Regent, but write all your code in C++ if you want. Even if you never write code in it, we've found Regent can be a better way to learn. And Regent has a tutorial [2]. [1]: http://legion.stanford.edu/bootcamp2017/ http://legion.stanford.edu/bootcamp2017/ [2]: http://regent-lang.org/tutorial/ http://regent-lang.org/tutorial/
- mcguire 9y agoOn the other hand, you're also one or two more mutexes from misunderstanding the execution of your application. In my experience, mutexes are very hard to reason about, particularly when you push them down to a fine grain, and drawing them out on a whiteboard doesn't help. One suggestion for actors is to look at the actors as elements of serialization, rather than as elements of concurrency.
- bitwalker 9y agoI would spend some time experimenting with Erlang or Elixir. The thing that really made the actor model "click" for me was how each actor in Erlang is just a process (green thread basically). The only way to communicate between processes/actors is message passing, and the way you end up constructing software is by modeling individual tasks and responsibilities of your application in terms of processes. The way processes are constructed in Erlang/Elixir allow you to pattern match on messages to selectively receive or prioritize messages, and discard others. A process can be written in such a way that it changes from one role to another just by being sent a message, or from one state to another (in the case of finite state machines and the like). A typical example of how it gets applied might be a web server, where each HTTP request is handled by its own process, and potentially delegating multiple tasks concurrently to other processes in the background which handle queuing and executing work associated with that request. You might have pipelines of processes, each responsible for some specific task/transformation before handing off work to the next stage in the pipeline. On top of that Erlang/Elixir provide supervision, where components of the system are started/monitored by a special process type called a supervisor, which will restart its children when failure occurs, and if a set of conditions fail, will itself be restarted by its parent supervisor. So your application ends up structured as a tree of supervisors and worker processes, where components of the application are branches of the tree, and can be isolated from the failure of other components. Hopefully that helps give you an idea of how the model plays out in practice. I would definitely recommend playing around with either Erlang or Elixir, they are a lot of fun, and it really changes the way you think - at least it did for me.
- napsterbr 9y agoHey, off topic but I have to say this, we use a bunch of libraries authored / maintained by you. Thanks!
- fpoling 9y agoWhy it is necessary to discard messages? Isn't it a sign of bad design when resources have to be spend to create and pass a message that is ultimately discarded?
- 9y ago
- egnehots 9y agoIndeed an actor only model will easily lead you to a mess. At first it's simpler with actors to think concurrently mainly because of the strict message passing rule for communication. But as your logic become more complex it will become hard to understand as the code flow is artificially separated between different entities, that doesn't scale nor compose easily. That's being said, it can be powerful to have just some top level actor abstractions that encapsulate more common idioms. Actors are great to build distributed micro services, you can have a supervisor to monitor and restart services, a registry to discover and subscribe. An actor can represent a service of course. Very useful as high level concurrency abstractions, not so much as low level primitives.
- toast0 9y agoThe actor model may not help much with overall system comprehensibility. But it can help a lot with comprehensibility of the pieces. A HTTP request handler may send messages (and wait for responses) to get all the data it needs, instead of taking mutexes and getting the data directly. The actors responding to the data fetch requests will generally be pretty simple -- get a request, grab the data and return it (and handle writes as necessary); if they bottleneck, it's either too many requests or the underlying storage is too slow. If your request handler bottlenecks, you probably have a slow data fetcher, the handler's data processing is too slow, or you're making a lot of data requests sequentially instead of concurrently. You absolutely need to have good instrumentation to understand your system -- at a minimum you need to know the queue lengths for at least important actors; having queue processing rate is pretty useful too.
- lastofus 9y agoI too ended up replacing all channels with a mutex or two in a small web socket relay server in Go, and things became 10x easier to reason about. I think part of the problem is the lack of usage/support for channels in the Go stdlib itself (seems that the stdlib authors also prefer mutexs). The other issue with sending a close msg on a channel to let the consumer know there will be no more messages, and that an error is thrown if you write to a closed channel.
- civility 9y agoIf you can reduce everything to _one_ mutex, that's simple. As soon as you have two mutexes, you need to start worrying whether one thread grabs A then B and another grabs B then A. In larger projects with multiple people contributing, the mutex and condvar approach does not scale well. It's very easy to get difficult to reproduce race conditions.
- Jtsummers 9y agoActors are computers and computers are actors. The mapping from actor to physical computer is sometimes one-to-one, sometimes many-to-one. An actor is an entity capable of handing input, changing its internal state, sending output, and potentially creating new actors. Whether we use it explicitly or not, the reality is that many of our systems can be modeled as actors in this sense. Whether you're using erlang (very explicitly based on the actor model), or C with pthreads and mutexes, you can model your system as actors. The specific mechanism of communication is irrelevant. And this is where I find it useful. My day job is in embedded systems. We have clear actors (multiple radios communicating over a protocol) which we treat as actors from a design perspective (of that protocol). Internal to each radio we have a number of physical computers running concurrently and communicating over a bus which acts like a shared memory. For this internal part, actors are not how it's coded. However they offer an effective model for how things work, as we can isolate the communication parts to a handful of modules (per application running). So from the internal part of our application it is as if we are sending messages (though the reality is we're writing them to a shared memory and setting some flags) and it is as if we are receiving messages (though the reality is we're reading from a shared memory when some flag goes high).
- Joeri 9y agoI just finished implementing an akka system. I was tasked to build an event-driven auto-scaling self-healing and self-balancing clustered rule processing engine, and I knew intuitively something like akka was needed. The learning curve in the beginning was frustratingly steep, and imho the only way to really grok akka is to implement something significant with it. I still feel I haven’t fully grasped its nuances, but eventually it became almost as routine to model everything as actors and messages as it was to model rest api’s with models and controllers. It’s a different way of thinking. Akka actually uses futures, when you “ask” an actor (send a message and receive the promise of a reply). I would say the primary benefit akka offers is changing the level of abstraction and forcing a proper modelling of the logic as message pipelines.
- Latty 9y agoI've played with this a bit, professionally and personally (Scala/Akka specifically). The way it makes you think about your model and the boundaries it introduces seem good. I really love the way it forces you to model the communication between parts of your code as a data structure - it feels like a much less costly version of the "everything as a micro-service" dream. Scala seems like the ideal language for it - you need really simple, powerful data types for it to feel good, otherwise those messages become painful to make or get overloaded in bad ways. Akka was still struggling with type loss in the system the last time I used it, which really felt like a pain, and where I feel it adds the boundary that makes it harder to understand.
- the_af 9y ago> Akka was still struggling with type loss in the system the last time I used it, which really felt like a pain, and where I feel it adds the boundary that makes it harder to understand. I don't know if this is what you are referring to, but: I don't understand the mix of Akka actors and Scala. Scala is a statically typed language -- in fact, that's the whole point of it -- and using Akka actors feels a bit like programming in Smalltalk. So you turn a statically typed language into a dynamically typed language with message passing. Honest question: why would you use Scala for that? Back when I took Coursera's "Reactive Programming in Scala" course, some years ago, I asked Ronald Kuhn & Martin Odersky precisely this question, and they didn't have a convincing answer. Kuhn's answer was "good point, we're also considering Typed Actors". It's my impression Scala never did go with Typed Actors (I might be mistaken; I certainly don't use them), so I wonder what's their current answer to this question.
- Latty 9y agoYes, this is precisely the problem I had - Scala is great, Akka's model seems good, but the mix feels like you loose something along the way. The typed actors stuff does exist, but it's experimental, and the last time I checked they had just thrown away the old version and were working on a new implementation. It seems like it would be better, but it's clearly not done yet.
- nickpsecurity 9y ago
- noelwelsh 9y agoHaving been involved in some systems that make heavy use of actors I 100% agree. We call actors "concurrent gotos" as they bring all the "advantages" of gotos with the added "benefit" of concurrency. It's a very primitive model of concurrency, only slightly better than mutexes. There are much better models in the research community but few of the ideas have yet made it into usable systems.
- uryga 9y agoCould you give some references to those better models? Sounds interesting!
- noelwelsh 9y agoVery briefly: Process calculus, such as the pi-calculus and join calculus. Prototype implementation in Scala here: https://github.com/Chymyst/chymyst-core https://github.com/Chymyst/chymyst-core Lasp and Bloom: http://christophermeiklejohn.com/lasp/2018/03/02/lasp-vs-bloom.html http://christophermeiklejohn.com/lasp/2018/03/02/lasp-vs-blo...
- lliamander 9y agoHaving worked on fairly concurrent systems in Erlang specifically, I would say one of the most useful tools to understanding the behavior of the system is the tracing facilities. Even something as limited (but user friendly) as ErlyBerly[0] helps a lot. As you scale up the number of concurrent processes who need to engage in ad-hoc concurrency, message passing becomes (in my experience) the only feasible way to manage those interactions. Being able to insert yourself in-between the communicating processes becomes necessary to truly understand what your system is doing. This is true even outside of Erlang itself, such as deploying independent OS processes that use HTTP for communication. Wireshark has definitely been my friend. State charts, interaction diagrams, and sequence diagrams are also invaluable. [0]https://github.com/andytill/erlyberly https://github.com/andytill/erlyberly
- deleted 9y ago[deleted]
- whalesalad 9y agocore.async != actors A lot of folks here in the comments are confusing a channel system with actors. Channels can help with actor communication, but they are not the same thing. A problem with core.async in Clojure is that the scenario you describe is very simple to create. You’re managing channels and buffers (communication mechanisms) instead of managing logical modules that do work for you (actors). If you want to succeed with actors, they need to really be a thing and you ideally want to abstract away the communication mechanism. This is what Erlang/Elixir have done and it’s beautiful. You don’t really spend a lot of time thinking about channels and buffers... you just talk to processes that are living things.