[erlang-questions] Beginner trying to figure out idiomatic OTP architecture

Matthew Shapiro <>
Tue Mar 22 14:11:13 CET 2016


Neat, that definitely makes things less complicated to talk to processes
managed by another supervisor!

On Tue, Mar 22, 2016 at 4:48 AM, Oliver Korpilla <>
wrote:

> Hello, Matthew.
>
> I'm a beginner, too. I just perked my ears when you wrote about the
> "which_children()" call...
>
> The gen_xyz() modules all follow a similar startup logic that allow you to
> register the child under an ID. The way you see used in "Learn you some
> Erlang good" is the easiest one, the local registration ({local, <name>})
> where <name> must be an atom. You can, however, use other registries - like
> "global". Google "man erlang global" for more details about that registry.
> There an arbitary expression is good enough as a name.
>
> This is hugely advantageous for many applications, including your own, as
> you can use something like {<atom>, <number>} or {<atom>, <string>} as
> process name. With global:whereis_name you can ask for the needed PID to
> send messages to. It returns undefined if it doesn't exist. So, instead of
> asking the supervisor about the children, you just call whereis_name with
> the computed ID you would be looking for, like {user, "DanWahlberg69" } and
> if you get something else but undefined, you can directly talk to the
> worker you are looking for. This should keep your supervisors simple.
>
> All you need is a scheme to generate unique names and you are set.
>
> Hope this helps,
> Oliver
>
> *Gesendet:* Dienstag, 22. März 2016 um 04:06 Uhr
> *Von:* "Matthew Shapiro" <>
> *An:* 
> *Betreff:* [erlang-questions] Beginner trying to figure out idiomatic OTP
> architecture
> I am trying to learn Erlang in order to create an application that seems
> to check off all the boxes of the Beam VM (low latency requirements,
> extremely concurrent, long lasting connections making hot code swapping
> desirable for upgrades, etc...).  In order to learn I've gone through most
> of Learn You Some Erlang For Great Good, and so far have really been
> enjoying the process.
>
> I am now at the point where I want to test some of this knowledge out, and
> I thought a good idea was to create a (basic) IRC server (as I've written
> them in the past in other languages, and it seemed like a good use case for
> Erlang).  Basic idea is to support allowing users to connect, set a
> nickname, connect to channels, send messages to channels, and send messages
> to users.
>
> However, I am a bit confused on the proper way to architect an OTP
> application and hoping that my current idea of how to architect this is
> correct or not.  Also, I find rubber ducking helps :)
>
> The high level supervision tree I have floating in my head is to have:
>
> * application
> ** app_supervisor
> *** tcp_listener
> *** handshake_supervisor
> **** handshake servers (dynamic)
> *** user_supervisor
> **** user servers (dynamic)
> *** channel_supervisor
> **** channel servers (dynamic)
>
> My thinking is the tcp_listener, handshake, user, and channel modules
> would each be gen_servers, and the 4 supervisors would be standard otp
> supervisors. The app_supervisor would have a one_for_one spec while
> user_supervisor, chandshake_supervisor and channel supervisor would have
> simple_one_for_one spec since they can spin children up and down
> dynamically based on users connected and channels with at least one user in
> it.
>
> The flow I have in my head is:
>
> 1) tcp_listener is a server that waits for connections, accepts them and
> tells the handshake_supervisor to spawn a new handshake server for the
> accepted socket (and changes ownership of the socket to the handshake
> server.
> 2) The handshake server handles the IRC handshake, and when the user
> requests a nickname it asks the user_supervisor if any children exist with
> the id matching the requested nickname.
> 3) If the nickname is available, the handshake server tells the
> user_supervisor to start a new user server with the accepted socket and
> nickname, sets the user server as the socket owner, and then the handshake
> server terminates
> 4) When the user server is started, the user_supervisor would set its id
> of the child spec to be the user's nickname.
> 5) When the user joins a channel, the user module sends the accepted
> socket and the users nickname to the channel_supervisor and asks it to send
> it to the correct channel.  The channel_supervisor will look for a child
> process with the id matching the name of the channel, and if none exists it
> will create a new child with the channel name as the id in the child spec.
> It will then send the child process with the matching id/name a message
> with the users nickname.
> 6) When the user leaves a channel it will call on the channel_supervisor
> to send a message to the child process with a matching id/name with the
> nickname of the user leaving the channel (or disconnecting).
> 7) The channel server will keep a state with a list of connected users,
> and when a join, leave, or channel message is received it will modify the
> client list as needed.
> 8) When required (user joins, user leaves, someone says something,
> etc....) The channel server will send a message to a specific user by
> calling the user_supervisor to send a string to a child process with an id
> matching the nickname of the user, and send that child process an erlang
> message with the irc message to send to the user.  The user server will
> then take that, translate it into the IRC protocol and send it to the
> socket.
> 9) On client disconnection, the user server will announce to the channel
> supervisor that it has disconnected, and the channel supervisor will tell
> all children it no longer is connected to the channel.
>
>
> So the problem I have on this architecture is it requires a lot of logic
> in the supervisors (which I am not sure is a good or bad thing).  It also
> seems to invite myself to race conditions due to all the
> supervisor:which_children() calls that would be required to identify the
> correct child process to send a message to, since theoretically a child
> process could die between a which_children() and actually sending the
> message.  It also means going through several layers of supervisors in
> order to find the process another process needs to communicate with (for
> example, user server would have to ask the user_supervisor to ask the
> app_supervisor what process the channel_supervisor is, which would then
> have to ask the channel_supervisor what process the correct channel server
> is).  This seems hacky but I am unsure of how to work around that.
>
> Am I on the correct path with this?  Is there an easier way to facilitate
> servers talking to other servers managed by other supervisors?
>
> Thanks.
> _______________________________________________ erlang-questions mailing
> list 
> http://erlang.org/mailman/listinfo/erlang-questions
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://erlang.org/pipermail/erlang-questions/attachments/20160322/9438eb94/attachment.html>


More information about the erlang-questions mailing list