How to describe gen_server visually?
Asked Answered
S

1

7

Disclaimer: The author is a newbie in OTP having some basic knowledge of Erlang's syntax, processes and messages.

I am trying to grasp the notion of behaviours in Erlang, but a lot of questions spring in my head preventing me from understanding the whole principle of such a behaviour like gen_server.

Okay, the official documentation for gen_server shows a nice diagram of a server and three clients connected with Query and Reply arrows: http://www.erlang.org/doc/design_principles/gen_server_concepts.html

But each time I try to understand the concept further, I get stuck.

There is a lot of concepts which I cannot build into one larger concept in my head:

  • behaviour implementation;
  • behaviour container;
  • behaviour interface;
  • callback module;
  • callback functions;
  • API functions.

I use the following resources:

I am still in the state "we call one function in one module, this function calls the other function, that function creates a process... stuck"

Is there any way to describe the notion of gen_server in a diagram? How can an interaction flow between clients and a server be shown visually? (to help a not so smart newcomer to understand the concept visually)

For example like here: http://support.novell.com/techcenter/articles/img/dnd2003080506.gif

UPD: I have tried to draw a diagram of my own, but I still don't get the purpose of any connector in the diagram: http://postimage.org/image/qe215ric/full/

UPD2: This is something similar to what I would like to see: http://cryptoanarchy.org/wiki/Worker_patterns (The Model). However, it doesn't show the interaction between modules, functions and processes.

Surfacetosurface answered 11/8, 2011 at 9:29 Comment(3)
Martin, one of the things that might be confusing it the registration of a singleton gen_server. I see it in most tutorials as the first introduction to gen_server, and it's so confusing if you trying to grasp gen_server. so, {local, CH3} in the start_link makes it a singleton -> there will be only one process of the gen_server module you are looking at.Thermel
woah!!! You didnt mention the book of Armstrong at all ...the pragmatic programming !!! You MUST go through it. It has got a very simple and step by step guide into OTPTapeworm
@AruMu As I wrote to the author of LYSE, your advice was really useful, thank you!Surfacetosurface
L
11

I don't have a precise drawing to explain it, but I have this chapter and the one after showing how to build gen_server starting with the abstraction principles behind it.

To help with the individual components:

behaviour implementation

The behaviour itself is a bit like what is shown in the chapter I linked before. It's a module with a bunch of functions doing all the generic stuff for you: receiving messages, defining functions and hidden protocols to communicate, etc. Advanced OTP stuff contains special kinds of messages used to do software upgrades and also special code for tracing options.

behaviour container

I'm not sure what this is supposed to be. Maybe just the module with the name of the behaviour?

behaviour interface

In the same module your behaviour implementation is, you have to define a behaviour_info/1 function. That function will let the Erlang compiler know that some callbacks are expected from any module that has -behaviour(SomeModuleName) in it. The SomeModuleName is equivalent to a SomeModuleName.erl (and .beam) file that contains the implementation and the behaviour_info function.

callback module

The module that will contain all the specific code, handling all the custom stuff.

callback functions

Everything that isn't generic gets to be delegated to the callback module in the form of YourModule:SomeCall(Args). These are provided by your module, the one that has the -behaviour(gen_server). line in it.

API functions

The callback module has two interfaces, if you want: the one for the gen_server behaviour (init/0, handle_call/3, handle_info/2, handle_cast/2, terminate/2, code_change/3), and the one for the user (start the server, send some information, ask for some information back).

I could try to describe it that way

---------------------------------------------------------------------
| some process          |                server process             |
------------------------+--------------------------------------------
   [client]             |      [callback]     :        [behaviour]
                        |                     :
 callback:start >-------|---------------------:--> starting the process
                        |                     :           V
                        |                     :           |
                        |       init()  <-----:-----------`
                        |         |           :
                        |         `-----------:------> initial state
  {ok, Pid}  <----------|---------------------:----------,/
                        |                     :
 callback:store  >------|---------------------:--> handles message
 (calls the process)    |    (formats msg)    :           V
                        |                     :           |
                        |    handle_call() <--:-----------` 
                        |         |           :
                        |          `----------:--> updates state, sends reply
                        |                     :        V
                        |                     :        |
   gets result <--------|---------------------:--------`
                        |                     :       

All the generic parts are on the right of the server process, within the behaviour, and all the specific parts are on the left (callback). The client uses the callback module's API/interface to contact the server process and have effects on it.

You have to see the behaviour as some kind of very generic code segment that sometimes gives up its execution flow (for more precise parts, like receiving and sending messages) to the specific code (how to react to these messages).

Hopefully this helps.

Lumper answered 11/8, 2011 at 16:30 Comment(3)
Nice answer, thank you! The combination of your picture and the advice to read Armstrong's book have helped me to understand the concept. By the way, do you think it is better to learn OTP with only a very basic knowledge of Erlang itself as 'Erlang/OTP in Action' suggests? (page 92: "You don’t need any prior knowledge of modules, processes, functions, or messaging to grasp..."Surfacetosurface
I don't agree with that, no. That might however depend in the way I learn compared to some other people. I like frameworks when I can understand why they are the way they are, and what problems they solve other than 'makes stuff easier'. I like to know what OTP does for me and on what principles it was built. I find that it helps me respect the original idea and makes it easier for me to expand on it. You'll see that's the way I show things in LYSE: show why we need abstractions, then show how to build the abstraction crudely, then show the framework.Lumper
It perfectly makes sense to me that some other people have different ways of learning that is more natural to them, though.Lumper

© 2022 - 2024 — McMap. All rights reserved.