Matt

Matt

Structuring an OTP application

I have a question about the structure of an OTP application. Given a fictitious employee time tracking application for my example, I am trying to reason which application layout would make the most sense using OTP.

Note: This is just an example fictiicous application, nothing I am working on. Just an example to better understand OTP orchestration.

The fictitious employee time tracking application will have employees, schedules, time tracking (clock in/clock out), vacation requests, and overtime submissions. Following OTP, each one should be a process, gen_server for example. Which application makes the most sense:

Option Number One

  • Each employee is a gen_server process.
    The employee process holds the employees name, id number, etc.

  • Each schedule is a gen_server process.
    A schedule holds an employee’s schedule, which days they work, etc. The schedule process is linked to an employee, or a worker process under an employee gen_server?

  • Each time tracking event is a gen_server process.
    This process holds a clock-in or clock-out event. Also linked to an employee, or supervised by an employee gen_server, not sure which is better. This does seem like too many processes may accumulate over time though.

  • Each vacation request is a gen_server process
    This process holds information regarding an employees vacation request, dates, and approval information. Also linked, or supervised by an employee gen_server process.

  • Each overtime submission is a gen_server process
    This process holds overtime information, and who may have approved it. Also linked to an employee, or supervised by an employee gen_server process.

This setup seems like it might spawn too many gen_server processes especially for the clock-in/clock-out. So would the second option be more appropriate?

Option Number Two

  • Each employee is a gen_server process.
    The employee process holds the employees name, id number, etc.

  • Each schedule is a gen_server process.
    A schedule holds an employee’s schedule, which days they work, etc. The schedule process is linked to an employee, or a worker process under an employee gen_server?

  • Each time events is a gen_server process.
    This process holds a list of clock-in or click-out events. One process for each employee. So, instead of one process for each clock-in or click-out even, just one process holding a list of clock-in and click-out events.

  • Each vacation requests is a gen_server process
    Same as the above, one process per employee, containing a list of vacation requests instead of each request being a process itself.

  • Each overtime submissions is a gen_server process
    Same as the above, one process per employee, containing a list of overtime requests instead of each request being a process itself.

I’m very curious to hear your thoughts. Please remember this application is not real, not a web app question and not a persistence question. Just simply curious about people’s opinion on structuring processes.

Most Liked

ericmj

ericmj

Elixir Core Team

It’s a common misconception that you should use processes to structure your application. Instead use modules to organize your application and use processes when you need concurrency or shared state between processes.

I think you should start by considering the external interface of your application, right now it’s a black box so you can design it however you want. But if you add an HTTP interface you probably want to handle requests in parallel, so the web server will start a process per connection or request. So now you need shared state between these processes which can be solved as you proposed, but your solutions are overly complex. Why a process per employee, why not a single process for all employees, or a single process for your whole database?

You should also be careful about using processes to store information that is not transient. You don’t want to lose employee information because the employee process crashed.

13
Post #3
peerreynders

peerreynders

Employees, timecard events, schedules, vacations, and overtime may be important concepts for structuring data and possibly even parts of application state but really don’t inform much in terms application behaviour.

Alan Kay (1998)

I’m sorry that I long ago coined the term “objects” for this topic because it gets many people to focus on the lesser idea. The big idea is “messaging” …

This comment highlights how people like to focus in on (static) “objects” because that is comparatively easy when in fact the application value is derived primarily by the (dynamic) “collaborations” that implement application behaviour.

Similarly in a BEAM application the ideal process structure is influenced much more heavily by the behaviour the application is meant to exhibit rather than the structure of the data it is managing or transforming.

This Erlang developer puts a different spin on it:
Lambda Days 2015 - Torben Hoffmann - Thinking like an Erlanger

Processes as the building blocks for protocols - so the big idea is designing protocols realized through communicating processes to implement application behaviour.

ericmj

ericmj

Elixir Core Team

This is impossible to answer. If we take your employees as an example, using one process per employee is likely wrong because it’s hard to argue why you should have one process per employee instead of a single process holding a list of employees. You also want to store schedules, should they be stored separately from employees? Possibly, but what would you gain from that?

Eventually you can consider storing the whole database in one process. What are the benefits and downsides of this? One benefit is less complexity, one process is simpler than multiple, another is that you can more easily implement transactions because you don’t need synchronization between multiple process.

One downside can be seen as an code organization, all your database would be in one location, but this is the misconception. Code is organized with modules, so you can have multiple modules for one process. Another downside can be scalability, you may want to shard your database to multiple process so you can use all your cores. This is when you should start considering using multiple process.

So to summarize, it is hard to answer this question because you are asking how to structure an application using processes when we are saying that you shouldn’t. I would suggest that you start writing the application without processes or with a single process and then when you hit a road block that you think processes will solve come back and ask a question that is specific to your problem and less abstract and hypothetical.

ericmj

ericmj

Elixir Core Team

Yes, that’s the misconception. You should start by structuring your application using modules and then add processes where they are actually needed to solve your business problems.

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.

Where Next?

Popular in Questions Top

gshaw
What is the idiomatic way of matching for not nil in Elixir? E.g., First way: defp halt_if_not_signed_in(conn, signed_in_account) when...
New
JorisKok
I have a server on AWS, and was running a load test using artillery. When looking at the Phoenix dashboard I see the Ports going to 100% ...
New
polypush135
As many of you may have realized by now (sorry for all the posts here) I’ve been working on a db problem where I’m trying to aggregate a ...
New
lk-geimfari
What is most correct way to open, read and parse JSON file with poison? For example if we have example.json file in root of some projec...
New
minhajuddin
I have seen a lot of code which picks the first element from a list using Enum.at(0) instead of List.first. Is there a reason why people ...
New
mathew4509
I have a list say x = ["23gh", "56kh", "97mh"] I would like to pass each element to Val in each iteration. Say, in iteration 1 -------...
New
johnnyicon
Hi all, I've just started learning Elixir and Phoenix Framework, so please pardon my n00bness at this stage. I'm trying to use Postg...
New
ovidiubadita
Hey all, I discovered Elixir and I love it. I always wanted to learn a functional programming and I intended to go for Haskell, but afte...
New
kostonstyle
Hi all I want to have a unix time, from the current time plus 1 hour. DateTime.now + 1 hour How to get it in elixir? Thanks
New
chrisalley
ExUnit now has describe blocks which is a welcome addition coming from RSpec. In the docs, it states that nested hierarchies of describe ...
New

Other popular topics Top

sergio
In Ruby, I can go: User.find_by(email: "foobar@email.com").update(email: "hello@email.com") How can I do something similar in Elixir? ...
New
dotdotdotPaul
Okay, I'm having a heck of a time trying to figure out how to best handle the validation of belongs_to associations in Ecto. I'm sure I'...
New
lessless
I believe there are people here who are dealing with CSV files import on the daily basis, and since Excel is a really popular tool there ...
New
Jim
As a follow up to my earlier question: I have the code compiling and running but not getting a successful login from the rest server. ...
New
fireproofsocks
I’m working on defining a simple Ecto schema for a table (in PostGres), but I don’t see where I can define a column as NOT NULL. Conside...
New
chrismccord
As promised, the first release candidate of Phoenix 1.3.0 is out! This release focuses on code generators with improved project structure...
New
myronmarston
The Elixir Typespec docs show the following syntax for keyword lists in typespecs: # ... | [key: type] # keyword lis...
New
chrisalley
ExUnit now has describe blocks which is a welcome addition coming from RSpec. In the docs, it states that nested hierarchies of describe ...
New
belgoros
I’m not a pro in using Regex and can’t figure out why the following behaviour happens, especially if we take into account the difference ...
New
magnetic
Hey :wave:t3: Elixir community, I’ve been learning Elixir, and working on some side projects. My editor of choice is VSCode, and althoug...
New

We're in Beta

About us Mission Statement