Live data from Hacker News

Message Passing and the Actor Model

dist-prog-book.com

81–88 of 88 posts

Re: Message Passing and the Actor Model

#81
post #61
post #58

Earlier quoted context omitted.

There’s a lot of literature on this in the Erlang world. This post is a very thorough summary of the various solutions available: https://ferd.ca/handling-overload.html I don’t find that kind of fully async processing pipeline very idiomatic TBH, at least in Erlang - more typically, every request would be its own actor, and do blocking requests to other actors that own shared resources.

Thank you for the insightful comments. That link looks perfect (down to the point of having a diagram with a->b->c->d just like mine!). The idea of each request being its own actor being more idiomatic is intriguing. Naively though does it really solve the problem? Don't you just end up with an overflow of actors now instead of an overflow of one actor's inbox?

You can put a bound on the number of actors under a given supervisor.

That being said the main reason an Erlang program will use an actor per request is not to prevent overload, it’s for fault tolerance; in your a -> b -> c -> d example a bug (e.g. uncaught exception) triggered by one request that happens in process c can cause all requests in flight to fail, while with a process per request there’s only one affected.

It does mean you may have thousands of actors rather than four or so, but that’s not a problem since Erlang is built and optimized for that kind of workload. On a platform where actors are OS threads, it may make sense to use a different approach.

Re: Message Passing and the Actor Model

#82
post #78
post #73

Earlier quoted context omitted.

Why 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?

Processes discard messages that they aren't interested in, that doesn't mean another process isn't.

Erlang has one message queue per prosess. So if a process discards the message, the message cannot be available for other processes. Moreover, as Erlang does not have message broadcast and all messages the process receives were explicitly sent to it, the process can not receive messages accidentally.

Re: Message Passing and the Actor Model

#83
post #75
post #73

Earlier quoted context omitted.

Why 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?

I think it’s just a bit badly worded. You don’t normally throw them away, you leave them in the queue for later. For instance if you have a process that gets requests and writes stuff to DB, while processing a request you can send a message to the DB, then use a selective receive to match on the response from the DB while ignoring all other messages (you’ll deal with them later i.e. when you fetch the next request to…

So "discard" means to post messages to own message queue? If so it seems like an awkward pattern that essentially implements a priority queue.

Re: Message Passing and the Actor Model

#84
post #65

Earlier quoted context omitted.

In Erlang the actors are lightweight processes are much smaller than OS threads. I wouldn't worry.

Right, but in my scenario (and I appreciate perhaps this means I'm "doing it wrong"), the messages themselves have a fairly heavy payload (around 800 bytes). Given that, whether I'm spinning up an actor or a message, if it can't be processed quickly it's consuming a non trivial chunk of memory.

I wouldn't say you are doing anything wrong (or, at least I couldn't say that for certain without knowing more about your problem domain :) ).

However, I also don't think you have to worry about the message payloads being too large. In Erlang, normally objects are copied between processes. However, for large objects (>64 bytes) they are put in a shared heap, and only the pointers are copied between processes[0].

[0] https://hamidreza-s.github.io/erlang%20garbage%20collection%...

Re: Message Passing and the Actor Model

#85
post #80

Earlier quoted context omitted.

I have come to realize that breakpoint style debugging should be avoided. Atleast for people working on Services. Prefer logging, or other variants like Event History, State Change history, etc.

Why?

Makes it easier to jump to distributed systems that span multiple processes or machines, where you can’t just set a breakpoint. Also makes it easier to debug production systems, where you may have logs but can’t jump back in time to attach a debugger.

Re: Message Passing and the Actor Model

#86
post #28

Earlier quoted context omitted.

>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' ..

I 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/recei…

I agree, we haven't found a general solution to building concurrent programs without the downsides that most of the existing models incur. The Actor model doesn't remove the burden to handle edge cases from the programmer, and depending on the problem domain it might be the wrong choice, but it does solve certain kinds of problems really well.

It's about using the right tool for the job. For example, I find that Futures / Promises great for composing calls to consume various backend services. But when it comes down to concurrent processes coordinating work and being able to handle failure transparently, Actors are my first choice most of the time. And sometimes the pragmatic solution is to simply use Threads and Locks.

I'm just curious about what you mean with the following statement:

  > And yet, two messages can absolutely race.
The queuing and dequeuing of messages in an Actor's mailbox are atomic operations, so there cannot be a race condition within an Actor's state. An actor handles messages in sequential fashion. But yes there could be surprises by certain patterns on the incoming messages if that's what you meant.

Consider the following example:

Imagine three actors: a Bank Account actor and two ATM actors. One of the ATM actors sends the message 'GetBalance' to the Bank Account actor, which in turn replies with a 'CurrentBalance' message. Now that the ATM knows that there are enough funds it sends a message to set the balance to a different value, but in the mean time the other ATM had already set the balance to something else. This of course is a problem since the getting and setting the balance are not atomic (two independent messages).

But this could be solved by removing the 'SetBalance' message and instead having a 'Withdraw' message which the Bank Account actor can reject and reply with the proper 'NotEnoughFunds' message.

Basically:

  ATM-1 sends GetBalance to BankAccount
  
  BankAccount replies Balance(500 EUR) to ATM-1
  
  ATM-2 sends Withdraw(400 EUR) to BankAccount
  
  ATM-1 sends Withdraw(200 EUR) to BankAccount
  
  BankAccount replies NotEnoughFunds to ATM-1
  
  ATM-1 sends GetBalance to BankAccount
  
  BankAccount replies Balance(100 EUR) to ATM-1
ATM-1 would now display the problem to the user and show the new balance (and he should worry since somebody withdrew his money from another ATM).

In the Actor model, each actor is expected to manage its own state, not "leaking" the control to the outside world. Allowing a 'SetBalance' message is giving control over its state to the outside world, and instead the actor should expose behaviour while retaining the possibility to decide what to do given the intention behind the message.

The nice part about this model of concurrency, is that it follows quite closely how real life processes work, so it can become quite intuitive to come up with and understand many of the patterns used.

Re: Message Passing and the Actor Model

#87
post #83
post #75

Earlier quoted context omitted.

I think it’s just a bit badly worded. You don’t normally throw them away, you leave them in the queue for later. For instance if you have a process that gets requests and writes stuff to DB, while processing a request you can send a message to the DB, then use a selective receive to match on the response from the DB while ignoring all other messages (you’ll deal with them later i.e. when you fetch the next request to…

So "discard" means to post messages to own message queue? If so it seems like an awkward pattern that essentially implements a priority queue.

No, messages that don’t match are not dequeued in the first place. I don’t know why the other poster used the word discard.

Re: Message Passing and the Actor Model

#88
post #13
post #2

I was hoping to see a mention of Pony which looks like a promising language based on the actor model.

Relating Pony to some of the languages mentioned, two that seem immediately comparable are Akka and Erlang. " Akka’s receive operation defines a global message handler which doesn’t block on the receipt of no matching messages, and is instead only triggered when a matching message can be processed. It also will not leave a message in an actor’s mailbox if there is no matching pattern to handle the message. The messag…

Pony is a bit different to a typed Erlang though:

* Pony has two message passing options: copy or share (by ref). Both are safe, Erlang does only copy.

* Pony is non-blocking only, there are no blocking calls in any stdlib function.

* Erlang supports no native threads, only green threads. So it favors IO over CPU, distributed computing on different cores is only via distributed nodes.

* Pony supports native threads, but no distributed actors on different hosts yet.

* Pony is massively faster, uses much less memory, and has better cleanup via a clever GC protocol.

Post reply on HN