When you look at how the composibility of Futures/Promises make both sideffecting and transformation tasks like monitoring completely trivial, it becomes really hard to accept the actor model as a reasonable choice.
There is little-to-no actor composition in this post, except that which is hidden by the framework that they've implementated... instead, it describes using the methods of an interface to communicate between your implementation, and their framework.
Composition of actors would allow for "wrapping" the actions of one actor in another and an implementation of RPC within your service itself (separate actors for both requests and responses), but that's not generally a thing you would do directly. Why? Because it's nontrivial, and you'd likely want a framework like theirs to orchestrate it.
Apparently you misunderstand all three of the original blog post, the actor model, and erlang's specific implementation.
Composition of actors in the actor model is done by message proxying, just like this blog post does. The original actor's API is preserved and undergoes no semantic change, just like composition in any other functional concurrency paradigm.
This works in erlang where it wouldn't work in most other languages because actors are very lightweight and message passing is a fast, optimized language primitive.
Having written many hundreds of promises and awaits, and many thousands of actors, I'd have to say that the actor paradigm is far easier to scale and reason about than any promise or async/await implementation. That's partly because almost every other language is pretty terrible at concurrency. But your mileage may vary and everyone has their preferences.
This looks like the 'gateway service' architectural pattern, in which you put a thin layer in front of your API service in order to perform activities common to all calls, like logging, metrics, authentication, session management, etc.
It's a little unusual to put that in as a proxy in front of a gen_server, because normally intra-erlang messaging doesn't need those facilities, which are normally handled at the border. But for metrics I can see it.
Yeah, I'm a little surprised this didn't illustrate the metrics gathering part, because aside from that, there isn't a lot to be gained from using this pattern.
You could also use it for resource management, couldn't you? the csi server could handle calls differently based on reaction to the overview that it has. Not that you couldn't write something like this yourself without too much difficulty, but standardizing could mean not having to reinvent the wheel and might have some benefits for tooling?
11 comments
[ 7.5 ms ] story [ 143 ms ] threadComposition of actors would allow for "wrapping" the actions of one actor in another and an implementation of RPC within your service itself (separate actors for both requests and responses), but that's not generally a thing you would do directly. Why? Because it's nontrivial, and you'd likely want a framework like theirs to orchestrate it.
Composition of actors in the actor model is done by message proxying, just like this blog post does. The original actor's API is preserved and undergoes no semantic change, just like composition in any other functional concurrency paradigm.
This works in erlang where it wouldn't work in most other languages because actors are very lightweight and message passing is a fast, optimized language primitive.
Having written many hundreds of promises and awaits, and many thousands of actors, I'd have to say that the actor paradigm is far easier to scale and reason about than any promise or async/await implementation. That's partly because almost every other language is pretty terrible at concurrency. But your mileage may vary and everyone has their preferences.
It's a little unusual to put that in as a proxy in front of a gen_server, because normally intra-erlang messaging doesn't need those facilities, which are normally handled at the border. But for metrics I can see it.