[HN Gopher] Neat Elixir trick: Libraries can start processes too
___________________________________________________________________
Neat Elixir trick: Libraries can start processes too
Author : sandbags
Score : 28 points
Date : 2021-08-01 10:36 UTC (1 days ago)
(HTM) web link (mattmower.com)
(TXT) w3m dump (mattmower.com)
| jolux wrote:
| This is a huge part of the power of Elixir/Erlang. Libraries can
| be written to use the same powers of isolation, resiliency, and
| concurrency that application code has, at no extra cost to the
| consumer and with total transparency.
|
| You quickly realize that every function you call could be working
| asynchronously underneath, and it's not scary because the whole
| system is designed to function like this.
| dceddia wrote:
| In earlier versions of Elixir where you had to explicitly add the
| libraries as additional processes to start, I remember being very
| confused why things like e.g. an HTTP client needed its own
| process. To be honest I still felt a little uneasy about it every
| time. Thinking of those processes as just "the library needs some
| internal state" makes so much more sense!
| dnautics wrote:
| Problem is, it's not entirely clear that an http client library
| needs external state. (And for example, mint does not).
| AlchemistCamp wrote:
| Many, many libraries do this! It's a key feature.
|
| (to clarify, these are light-weight _Elixir_ processes, not OS
| processes)
| dnautics wrote:
| I really wish that in elixir we drew a distinction between
| dependencies that do and don't spawn their own supervision trees.
| It would be nice if this were reflected in, say, hex.pm, possibly
| using different tags for them.
| QuinnWilton wrote:
| Agreed. I also wish fewer libraries started their own
| supervision tree, and instead gave you a child spec to drop
| into your supervision tree. There's definitely use-cases where
| shipping libraries as an application makes sense, but
| oftentimes that sort of design causes problems for me, because
| it means not being able to start multiple copies of the
| dependency with different configurations.
|
| I think Phoenix PubSub is a perfect example of how libraries
| should be structured, in that you just need to drop the module
| + options into your supervision tree, and you have the freedom
| of starting multiple independent copies of the tree, in
| different contexts, and with their own configurations:
| https://hexdocs.pm/phoenix_pubsub/Phoenix.PubSub.html#module...
| jolux wrote:
| You do sometimes have to be careful about how you handle
| configuration with embedding multiple copies of other
| supervision trees though:
| https://ninenines.eu/docs/en/ranch/2.0/guide/embedded/
| dnautics wrote:
| I'm 50-50 on that one. This may be an overly nitpicky detail,
| but I you sort of want your own sup tree to not _necessarily_
| have a different-ly scoped "microservice" tied to it in
| terms of failure domains. For the 90% use case (e.g. http
| process pools) an indepentent sup tree is correct, but to
| your points,
|
| 1. it would be nice to have a choice. The library-writer
| should think about their users and choose which case is more
| correct. And make it opt-out and easy (let's say 2-3 loc) to
| implement the "other case", and spelled out explicitly in the
| readme/docs landing page.
|
| 2. PubSub indeed made (IMO) the correct choice when it
| migrated over from being its own sup tree to moving into the
| app's sup tree.
|
| Thank you for listening to my TED talk.
| lostcolony wrote:
| It's not obvious from use? It's been a long time since I was in
| this world (well, Erlang), but it was
| application:ensure_started(App) that let me know "this
| dependency has its own supervision tree".
| dnautics wrote:
| Httpoison, for example, starts its own supervision tree to
| manage client process pools (not obvious that an http client
| should do that, definitely usecases where it shouldn't) and
| there is no indication either in mix.exs, or
| `MyApplication.Application.start/2` that Httpoison needs the
| tree. For most deployments, it's probably not a big deal.
| Some process will try to read http content, fail if httpoison
| isn't quite ready, and be restarted by its supervisor.
|
| However, if you try to use it early in compilation or test,
| say, in your test_helper.exs file, speaking from experience,
| you could wind up with a very difficult to understand race
| condition where the httpoison process tree hasn't fully
| booted and you're trying to fetch something off the internet,
| and you don't have the same level of supervision protection
| -- if test_helper fails the whole test suite gives up and
| doesn't restart -- for obvious reason.
|
| For the http case thankfully the elixir ecosystem is getting
| Mint as a base http library, which doesn't require a process
| tree out the gate, and several interesting explicit process-
| pool-libaries (finch, mojito) which are tuned for their own
| use cases that derive from mint.
| OskarS wrote:
| I think Erlang and Elixir are incredible, and this particular
| feature of libraries starting BEAM processes seems cool in the
| abstract. However, this PARTICULAR task, Erlang/Elixir might be
| the worst possible language you could choose:
|
| > _The Elixir approach to shared mutable state is wrapping it in
| a process. In this case, I needed a counter and the easiest way
| to implement it is to use an Agent which is a kind of process
| designed to handle simple state. In this case, the get_and_update
| function allows me to return the counter and increment it as an
| atomic operation._
|
| This is literally just an atomic counter. It's a single CPU
| instruction that is guaranteed to be safe. I don't care how
| lightweight a BEAM process is, it's not faster than updating an
| atomic counter. Doing it this way is also absurdly more
| complicated than using, say, a std::atomic in C++ (or the
| equivalent type in other languages).
|
| Again, I think Elixir is cool, but if you want to show off how
| cool it is, maybe don't use an example that is incredibly much
| slower and more complicated than it should be. It's not a great
| look for Elixir.
| enjo wrote:
| Of course you're right, but I think anyone who is capable of
| getting their head around Elixir is capable of understanding
| that this is a trivial example meant to showcase a powerful
| language feature. What you choose to use it for is up to you,
| not the article's author.
| OskarS wrote:
| I see that point, but it was pretty jarring reading this
| Elixir article as a curious outsider that they had to make
| these kinds of very advanced contortions to do something so
| simple in such a non-performant way. It's something that is
| only impressive if you were already totally sold in on the
| BEAM way of doing things, but looks insane to any
| C/C++/Rust/Java/whatever developer.
| whalesalad wrote:
| Processes on the BEAM may not be on the same machine or even in
| the same datacenter. You can't assume that everyone has access
| to the same physical memory.
___________________________________________________________________
(page generated 2021-08-02 23:01 UTC)