namelos

namelos

Currently I’m following the “to spawn or not to spawn” approach – use functional and module deal with thought concerns, and processes deal with runtime concerns.

And try to use registry with name or id, instead of direct linking or storing pids. It’s pretty similar to the service discovery pattern over configuring ip addresses.

I believe it’s the safest and cleanest way of doing things currently. If you are trying to play your business on BEAM, this is absolutely recommended approach.

But from time to time I’m also wondering use wrap processes to an object-oriented language. Though it’s not applausable across the community, it’s might suitable to make something interesting – for example an entity system with runtime concerns. Maybe like when you define an object use the wrapper, you’ll get: supervision tree, default recover behaviour, type, struct, methods, and possibly graphQL type resolver for free. Or you can write custom view or read model on this entity. And it’s might quite suitable for things like fast prototyping.

2
Post #22
jwarlander

jwarlander

Processes can definitely serve a few different purposes when designing an application, and sometimes it’s fun to just play around a bit, trying out a totally different way of doing things. Like implementing an OO approach with processes.. :wink:

However, for me it tends to come down to a few core points:

Concurrency

This is of course the first thing one tends to learn about the BEAM – concurrency is implemented by processes exchanging messages, after all. There’s no point in launching 2 million processes just because you can, however; try to stay close to the natural concurrency of the problem you’re solving.

If devices, end users, etc are connecting to your application, then that’s usually a good start – each of those probably need at least one process, for as long as they’re interacting. Do you have recurring tasks, cleanup tasks, etc? They probably also need their own processes. But.. each semi-involved task of some kind that’s initiated by a user of the system? It might not be warranted a process of its own; perhaps every user has a single “background worker”, effectively serializing tasks per user in order to democratize resources somewhat, making sure a single user can’t bog down the entire system..

Fault Isolation

This one didn’t really “click” for me until I’d started learning Erlang a long time ago, and after reading a couple of books and trying some examples, decided that I needed to build something “for real”.

As I was toying around with a small online game prototype, juggling things like TCP connections, command processing, long-running environmental effects, room-based communication and navigation etc, I finally realized that processes are a very powerful tool for fault isolation – especially in combination with proper supervision trees.

I was used to C/C++ mostly at the time, where errors usually are a bit of an all or nothing affair; either you anticipate and handle the error where it occurs, or your whole application comes crashing down. Naturally you’ve got ways to carefully navigate around that, but it’s not easy either way.

With the BEAM, instead, we can start to think about what parts of our systems that can safely fail, and how we can best handle that. User input processing and TCP communication? That all happens in user-specific processes, and if something fails, we just need to make sure that we’ve structured our processes so that everything that needs to be stopped / restarted is properly linked.

As a consequence, you can start thinking about separating very simple, reliable core parts of your application into their own supervision tree(s), so that even if everything else comes crashing down, those parts will survive, either to make sure everything else is properly restarted again, or if nothing else, to safely shut down, perhaps persisting critical data etc.

Serialization

Another important concern is if you have some part of you application that can’t handle concurrency at all; this can then be wrapped in a process that manages whatever it needs to do, allowing other processes to send messages at will but always confident that you’re processing them one at a time.

Naturally this can turn into a performance bottleneck, so it requires some careful thinking. If you’re writing to a transaction log, for example, perhaps it’s tolerable to keep the last few transactions in memory, only flushing to disk every now and then, in order to achieve better throughput. Yes, you risk losing some transactions - but depending on what you’re doing that may be perfectly acceptable.

5
Post #23
— All posts loaded —