Live data from Hacker News

A Brief Guide to OTP in Elixir

serokell.io

81–90 of 100 posts

Re: A Brief Guide to OTP in Elixir

#81

Earlier quoted context omitted.

I finished reading Elixir in Action a few days ago. This was probably the best part of that book. It takes you through building a primitive GenServer with basic processes before moving onto actually using GenServer. It specifically goes over tail recursion optimization before it introduces the receive do loop, which seems like a very important part of the whole thing.

Would you recommend the book?

Also not the OP, but unequivocally yes (though for full disclosure, I am biased as was one of the technical reviewers for the second edition). It was hugely important to me personally learning the language; the approach is excellent and it's very well written. You take a simple to-do application and write a number of versions of it, each one progressively more complex and featureful, and each one using progressively higher language/OTP abstractions.

Re: A Brief Guide to OTP in Elixir

#82
post #35
post #3

OTP = Open Telecom Platform [1], though I think the full name is avoided nowadays. On an unrelated note, I've always been fascinated with the Erlang VM. The idea that you could hot-load modules and have multiple versions of the same module running at once seems really useful. I wonder why other runtimes haven't adopted these features? [1] https://en.wikipedia.org/wiki/Open_Telecom_Platform

Because why would you do that? Now we have tool and pipeline where testing / deploying ect .. is easy, so I'm not sure why would I risk to do hot load things. I don't think it's useful and I think it's pretty dangerous is the first place. You have code that can run two different things, wcgw.

Someone correct me if I'm wrong here, but afaics dropping the ability to do it would remove a complication from the platform and wouldn't affect the vast majority of users in any way. But [afaik] it has to exist because Ericsson require that feature. Outside of their specific usecases it's doesn't seem a particularly useful feature, or one that is used in the vast majority of contemporary codebases

Re: A Brief Guide to OTP in Elixir

#83
post #47

Earlier quoted context omitted.

A very erlang demo showed upgrading code running on a telephone switch without dropping the call. That's why you want hot code loading.

Because control plane does not handle anything phone related most likely? It's like upgrading Kubernates masters, your API servers still works and are not affected. AFAIK Erlang code does not handle any network traffic, all of that is done in C/C++.

This is the original video in its 90s glory: https://www.youtube.com/watch?v=uKfKtXYLG78

Regardless of where the call is handled, you can't just deploy a new switch in a telecom network, but you still want to upgrade them. This is where you really want hot code loading.

Re: A Brief Guide to OTP in Elixir

#84
post #80

Earlier quoted context omitted.

> the basic notion of the spawn -> receive -> loop cycle I'm working on a video series about these concurrency patterns; even recorded the first one but the audio is bad so I'm going to re-record it before I publish it when some "real" audio equipment comes in (hopefully this weekend) - let me know what you think: https://www.youtube.com/watch?v=eoEcpcLVumc

Pretty good! I knew most of that stuff already so I can't speak to the pure education value, but it made sense, and I think you hit the key points. I bet some visuals would help it make sense for total nooberz. AND! I didn't know that about "v" - thanks! :)

another protip: you can also call v(n) and it will give you the item at iex(n)>

Re: A Brief Guide to OTP in Elixir

#85
post #75

Earlier quoted context omitted.

At WhatsApp we had a bunch of mnesia-backed services. I can't say that it was the best experience. Pretty much anything non-trivial, such as healing a cluster after a partition, or expanding a cluster, or cross-DC replication was a complete PITA to do properly. There's also very few people that have operational experience with it and very little documentation about it compared to pretty much any other storage engine.…

I think we would have found any database to be a PITA at write volumes and data volumes we were using, with our expectations of availability. I don't think we would have been able to get that performance out of MySQL, or Postgres; and anyway, we didn't have a dedicated team for database management to make sure nobody wrote bad queries if we did. Sync, status and reg ran on MySQL and php when I started, and it was a l…

Mnesia is first and foremost and in-memory key-value configuration storage. Relying on it for anything else will damage you in the long run.

Re: A Brief Guide to OTP in Elixir

#86
post #9

> Forget using a million different technologies for things like background jobs, OTP can supply you with everything. It's true but in practice this usually doesn't pan out. For example with just background jobs alone there's the idea of queues, tracking failures / successes, exponential back-off retries, guaranteeing uniqueness, draining, periodic tasks and everything else you'd likely want in a production ready app.…

Phoenix ships with Ecto ORM-style-binding to Postgres underneath (usually) for a good reason! :) And if you need that in memory magic where the serial nature of a genserver won't suffice for your load, you basically have built-in Redis with Erlang's ets. And if you really need insane performance on longer-cached items than ets is optimal for, Discord released https://github.com/discord/fastglobal

And if you don't want to put all those pieces together, like you said there's a rich set of modules people have built, like oban. Long rise and live the elixir monoliths ;) (although admittedly even Discord had to swap out some of their most intense functions with Rust, but how many people are writing systems that need to handle millions of concurrents?)

Re: A Brief Guide to OTP in Elixir

#87
post #17

OTP is definitely more of an expert's toolset than a framework for throwing together features quickly. This is a good thing because it's not trying to be more of an abstraction than necessary. With OTP you can make fault-tolerant, concurrent, self-healing applications that handle stateful operations with ease, but you'll still need a fair bit of need-to-know about the callbacks, how to manage process lifecycles, etc.…

Elixir already started the process of extending OTP: Task is a hugely important addition to the family of genserver, genstatem, etc.

Isn't Task an entirely different beast to the gen_* behaviours?

To me it looks like a convenience wrapper around Erlang's primitives. I usually use them directly if I need the flexibility to "await" later. If I don't, I have a small function "run_in_subprocess" (mostly to not run into binary gc problems) that spawns and receives within one function.

The only real advantage I see is that it is separately supervised. When avoiding trap_exit, "spawn_link"ing within a process that is itself supervised has a similar effect, though.

Re: A Brief Guide to OTP in Elixir

#88
post #78
post #61

Earlier quoted context omitted.

I agree. The reason we stick with OTP, is that OTP behaviors like gen_server handle two system-level concerns that "raw" Erlang code doesn't: 1. OTP behaviors integrate with the OTP supervisor lifecycle management system (i.e. the OTP framework offers the supervisors standardized hooks to start up and shut down your process, guaranteeing that the errors generated during such steps will be in a format the supervisor c…

Well, the sys behavior is easy to implement. I also am not a fan of the complexity and overhead of gen_server so instead I use metal ( http://github.com/lpgauth/metal ). It's a simple receive loop with an optional init and terminate callback. It implement sys and can be supervised.

Does gen_server have a measurable overhead compared to this? The main event loop of gen_server looks remarkably similar to what you are doing but handles calls, casts, hibernation, debugging, provides stacktraces and doesn't "force" exit trapping. In the "happy-path", I don't see any additional overhead to what you are doing.

I would also like to see "handle_call" and "handle_cast" (maybe even "handle_info" with a blanket noreply-implementation) optional, but until then I can deal with the additional two lines to exit on cast or call if I don't require those.

Re: A Brief Guide to OTP in Elixir

#89
post #4

> Every time the loop runs, it will check the top of the mailbox (mailbox is last-in-first-out) for messages that match what we need and process them. Nope, erlang mailbox is fifo with filtering. When a process sends messages, they are delivered in the same order they were sent.

That sentence was fixed in the article and now reads:

> Every time the loop runs, it will check from the bottom of the mailbox (in order they were received) for messages that match what we need and process them.

Re: A Brief Guide to OTP in Elixir

#90
post #47

Earlier quoted context omitted.

Because control plane does not handle anything phone related most likely? It's like upgrading Kubernates masters, your API servers still works and are not affected. AFAIK Erlang code does not handle any network traffic, all of that is done in C/C++.

This is the original video in its 90s glory: https://www.youtube.com/watch?v=uKfKtXYLG78 Regardless of where the call is handled, you can't just deploy a new switch in a telecom network, but you still want to upgrade them. This is where you really want hot code loading.

Real world usage case - I have a server which handles several thousand telematic devices (telemetry+actuation), split into about 20 different device types. It's small device base so doesn't require multiple servers and we can be really cheap. But requiring to disconnect all devices just to make one small change to one device driver would be painful. Now we can have uptime of over half a year and in that time we had changes/fixes to several drivers. Normally that would require scheduled downtime every week and slow us down considerably.
Post reply on HN