<div dir="ltr">Hi,<div><br></div><div>There's been a lot of good advice in this thread so far. I would like to throw in my two cents regarding designing and thinking in process structures.</div><div><br></div><div>Erlang is known as a highly concurrent language, every tutorial talks a lot about processes and so on; so it's no surprise working with processes sounds very exciting (and maybe even scary sometimes) for newcomers. But I believe in most cases you should ignore them as long as you can.</div><div><br></div><div>A typical server application would serve incoming requests. Signing up a new user for example. Your system will be concurrent by simply allowing each request to be served in parallel. So there will be one process per request. This is something you would get typically out of the box from your Erlang web server - no need to think much about processes so far.</div><div><br></div><div>Your job would be to code how to handle a request. How to sign up a new user for example. This is often just simple sequential code: add the user to the DB, send a welcome email, whatever. You don't need to throw in processes just because you're working in Erlang. It's not like programming a GPU, where you have to think hard how to allow all those tiny processors crunch through your initially very sequential looking calculation. If your algorithm is best expressed as sequential code, just write sequential code. Your server will be fast not because it could use all CPU cores to serve that single sign up request. It will be fast because it will be able to serve different sign up requests on all those cores.</div><div><br></div><div>As you go on with your sequential code, you will sometimes encounter problems where the need for new processes arise naturally. Then you can add these processes, and it will all feel very obvious and easy. Some typical situations that call for processes:</div><div><ul><li><font size="2">You have some stateful component that needs atomic updates. A process can hold the state and its message queue will serialise the update requests.</font></li><li><font size="2">You have to perform some task as you serve the request, but it doesn't have to complete before sending a response to the user. Just delegate this asynchronous task to a different process. Typical example: logging.</font></li><li><font size="2">You need to do 2 or more tasks that don't depend on each other and can take a long time. Just spawn a process for each task and let them run in parallel.</font></li><li><font size="2">You have a resource like an ETS table or port that has to be around for a long time. You will need a long living process to own this resource.</font></li><li><font size="2">You need to do some heavy (CPU or memory wise) computation. Like resizing the avatar the new user has uploaded. You are afraid that if too many concurrent sign ups would happen at the same time you would overload the system. This is a point where you concurrent processes need to synchronise with each other (maybe via an other process or just an ETS table) to limit the concurrency in your system. (Yes, this is kind of the opposite of the above points...)</font></li></ul><div><font size="2">And there are of course advanced situations when you can (or even need to) code process-aware. There are nice tricks like offloading work from processes with a possibly long message queue to a different process with guaranteed no messages, spawning new processes in a library to protect the global state of the process calling into the library and so on. But in the beginning just concentrate on the simple cases.</font></div></div><div><font size="2"><br></font></div><div><font size="2">Once you start using more and more processes, you will have to answer the question of what to do if one of them dies? Shall asynchronous tasks still be carried on if the initial request crashed? You will quickly learn a lot about links and supervisors when you get to this point.</font></div><div><font size="2"><br></font></div><div><font size="2">So my advise is to start writing the sequential part of the code, and see what other processes emerge naturally. If you want to first design the process structure and supervisor tree of your application, than you risk implementing a lot of functionality that could be plain sequential library code as gen_servers. Which would only introduce unnecessary bottlenecks into your system.</font></div><div><font size="2"><br></font></div><div><font size="2">Cheers,</font></div><div><font size="2">Daniel</font></div></div><br><div class="gmail_quote"><div dir="ltr">On Thu, 15 Dec 2016 at 17:56 Sergey Safarov <<a href="mailto:s.safarov@gmail.com">s.safarov@gmail.com</a>> wrote:<br></div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><p dir="ltr" class="gmail_msg">As guide for first step in erlang I use <a href="http://learnyousomeerlang.com/contents" class="gmail_msg" target="_blank">http://learnyousomeerlang.com/contents</a></p>
<p dir="ltr" class="gmail_msg">Think it will help you.</p>
<br class="gmail_msg"><div class="gmail_quote gmail_msg"><div dir="ltr" class="gmail_msg">чт, 15 дек. 2016, 18:33 Loïc Hoguin <<a href="mailto:essen@ninenines.eu" class="gmail_msg" target="_blank">essen@ninenines.eu</a>>:<br class="gmail_msg"></div></div><div class="gmail_quote gmail_msg"><blockquote class="gmail_quote gmail_msg" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">Welcome,<br class="gmail_msg">
<br class="gmail_msg">
On 12/15/2016 01:04 PM, IRLeif wrote:<br class="gmail_msg">
> Coming from an object-oriented and data-centric background, I have<br class="gmail_msg">
> cognitive difficulties when it comes to conceptualizing, thinking about<br class="gmail_msg">
> and designing systems consisting of modules, processes and key-value<br class="gmail_msg">
> data stores.<br class="gmail_msg">
><br class="gmail_msg">
> My brain reverts to thinking about classes, objects, inheritance trees,<br class="gmail_msg">
> encapsulation and SQL-style relational data models. I'm afraid this<br class="gmail_msg">
> could lead to unidiomatic Erlang system architectures and<br class="gmail_msg">
> implementations, which would be undesirable.<br class="gmail_msg">
<br class="gmail_msg">
That's what brains do, unfortunately.<br class="gmail_msg">
<br class="gmail_msg">
Let me stop you there by correcting one small thing: SQL-style<br class="gmail_msg">
relational data models can still apply depending on the system you are<br class="gmail_msg">
building. PostgreSQL usage is not uncommon in the Erlang world, at least<br class="gmail_msg">
for small to medium systems.<br class="gmail_msg">
<br class="gmail_msg">
Classes, objects, and so on, on the other hand, do not exist, and<br class="gmail_msg">
thinking in terms of objects will have a negative impact on the design<br class="gmail_msg">
of your system.<br class="gmail_msg">
<br class="gmail_msg">
How do you solve this? Well it depends on your experience outside of OO.<br class="gmail_msg">
Here are a few early tips, apply the relevant ones:<br class="gmail_msg">
<br class="gmail_msg">
- Have you used a non-OO imperative language? C for example. In these<br class="gmail_msg">
languages, you only have functions. In Erlang, you only have functions.<br class="gmail_msg">
Erlang is closer to C than OO, so switch your brain to C-style<br class="gmail_msg">
programming instead of OO.<br class="gmail_msg">
<br class="gmail_msg">
If necessary, spend a few hours writing a short C program (or another<br class="gmail_msg">
language as long as you don't use/write OO code) and then switch to<br class="gmail_msg">
Erlang. This will set your mind on the right path.<br class="gmail_msg">
<br class="gmail_msg">
- Have you ever heard of the best practice that dictates that variables<br class="gmail_msg">
should not be reused? Erlang basically enforces that.<br class="gmail_msg">
<br class="gmail_msg">
When writing that short program, make sure you do not reuse variables.<br class="gmail_msg">
<br class="gmail_msg">
- Have you ever written code that recurses over directories to for<br class="gmail_msg">
example file files matching a pattern? You can't do that with only<br class="gmail_msg">
loops, regardless of the language.<br class="gmail_msg">
<br class="gmail_msg">
Have the short program use recursion instead of loops.<br class="gmail_msg">
<br class="gmail_msg">
After doing this your mind will be very close to how it needs to be to<br class="gmail_msg">
write sequential Erlang programs. The rest is minor syntax differences<br class="gmail_msg">
like commas and semicolons or uppercase first letter for variable names.<br class="gmail_msg">
<br class="gmail_msg">
95% of the Erlang code you write is short sequential programs. If you<br class="gmail_msg">
have experience with microservices, then think of Erlang processes as<br class="gmail_msg">
microservices. If you have experience with client/server development,<br class="gmail_msg">
then think of them all as servers that communicate to each other in a<br class="gmail_msg">
mostly client/server fashion. The only difference is that they<br class="gmail_msg">
communicate using Erlang messages.<br class="gmail_msg">
<br class="gmail_msg">
It definitely helps to reimplement something like a gen_server or<br class="gmail_msg">
gen_statem as you will better grasp links, monitors and timeouts. These<br class="gmail_msg">
are useful to have solid systems; although you can do mostly without and<br class="gmail_msg">
still get results.<br class="gmail_msg">
<br class="gmail_msg">
> Here are some of the essential complexities I have difficulties grasping:<br class="gmail_msg">
><br class="gmail_msg">
> A) Identifying discrete modules and processes and finding good names for<br class="gmail_msg">
> them.<br class="gmail_msg">
<br class="gmail_msg">
There's basically two kinds of modules in Erlang: those implementing a<br class="gmail_msg">
process, and those containing useful functions. For the latter it should<br class="gmail_msg">
be obvious: group all the related functions together. For the former,<br class="gmail_msg">
you are almost required to put them in a module as you implement<br class="gmail_msg">
behaviours, so no big trouble there.<br class="gmail_msg">
<br class="gmail_msg">
As for naming, well, good luck with that.<br class="gmail_msg">
<br class="gmail_msg">
I generally follow this pattern: <ns>_<name>[_<suffix>] where <ns> is a<br class="gmail_msg">
namespace I put in all modules of my application (often the name of the<br class="gmail_msg">
application), <name> is the descriptive name (for example 'router' or<br class="gmail_msg">
'http') and <suffix> is the type of the module, for example 'server',<br class="gmail_msg">
'sup' for supervisor, 'h' for Cowboy handler, 't' for a module<br class="gmail_msg">
containing functions for converting from/to and manipulating a type, and<br class="gmail_msg">
so on.<br class="gmail_msg">
<br class="gmail_msg">
> D) Designing a sensible persistent data model with Mnesia or other NoSQL<br class="gmail_msg">
> data models (e.g. using CouchDB).<br class="gmail_msg">
<br class="gmail_msg">
Depending on what you are doing you might not need those. And if you do,<br class="gmail_msg">
I would advise not jumping into it early. Start with what you know.<br class="gmail_msg">
Erlang is a big enough change as it is.<br class="gmail_msg">
<br class="gmail_msg">
> E) Deciding which processes should read and write persistent data records.<br class="gmail_msg">
<br class="gmail_msg">
Start simple, measure, then decide what to do about it. Pools of<br class="gmail_msg">
connections are not uncommon, and already written for you in most cases.<br class="gmail_msg">
<br class="gmail_msg">
> F) Incorporating global modules/"shared facilities" like event handlers,<br class="gmail_msg">
> loggers, etc.<br class="gmail_msg">
<br class="gmail_msg">
Don't know what you mean with event handlers exactly, but as far as<br class="gmail_msg">
logging goes, use either error_logger or lager depending on your needs.<br class="gmail_msg">
Again not much to think about there.<br class="gmail_msg">
<br class="gmail_msg">
> H) Organizing source code files into separate projects and directory<br class="gmail_msg">
> structures.<br class="gmail_msg">
<br class="gmail_msg">
A few simple rules:<br class="gmail_msg">
<br class="gmail_msg">
- A module covers one small topic<br class="gmail_msg">
- A process does one thing<br class="gmail_msg">
- An application covers one topic and does one thing that involves<br class="gmail_msg">
multiple modules and processes<br class="gmail_msg">
<br class="gmail_msg">
> B) Appointing supervisor and worker modules; defining process hierarchies.<br class="gmail_msg">
> C) Deciding which processes should communicate with each other and how.<br class="gmail_msg">
> G) Visualizing the system architecture, processes and communication<br class="gmail_msg">
> lines; what kind of graphics to use.<br class="gmail_msg">
<br class="gmail_msg">
I'll leave that one for others.<br class="gmail_msg">
<br class="gmail_msg">
> Questions:<br class="gmail_msg">
><br class="gmail_msg">
> 1) How do you unlearn "bad habits" from object-oriented way of thinking?<br class="gmail_msg">
<br class="gmail_msg">
The only way to unlearn something is to not use it for extended periods<br class="gmail_msg">
of time. I'm afraid that's not much help. Instead, try manipulating your<br class="gmail_msg">
brain into finding the right state of mind when you work on Erlang projects.<br class="gmail_msg">
<br class="gmail_msg">
It's the same problem you have when you play tennis and then switch to<br class="gmail_msg">
ping pong. The only difference is that you don't play them in the same<br class="gmail_msg">
environment or with the same tools so it's easier to switch from one to<br class="gmail_msg">
the other.<br class="gmail_msg">
<br class="gmail_msg">
Actually changing environment could help you into switching your OO<br class="gmail_msg">
brain off: change desk, change editor, and so on.<br class="gmail_msg">
<br class="gmail_msg">
> 2) How do you think and reason about process-centric systems designs?<br class="gmail_msg">
> 3) When designing a new system, how do you approach the above activities?<br class="gmail_msg">
<br class="gmail_msg">
Start small and naive, measure, then take decisions as to how the<br class="gmail_msg">
processes should be organized. Erlang is very easy to refactor compared<br class="gmail_msg">
to OO, so take advantage of that. Go for a working prototype, then<br class="gmail_msg">
improve upon it. How long you take to go from prototype to production<br class="gmail_msg">
will mostly depend on your experience.<br class="gmail_msg">
<br class="gmail_msg">
I've been interrupted about a thousand times when writing this, so<br class="gmail_msg">
hopefully I make some sort of sense. :-)<br class="gmail_msg">
<br class="gmail_msg">
Cheers,<br class="gmail_msg">
<br class="gmail_msg">
--<br class="gmail_msg">
Loïc Hoguin<br class="gmail_msg">
<a href="https://ninenines.eu" rel="noreferrer" class="gmail_msg" target="_blank">https://ninenines.eu</a><br class="gmail_msg">
_______________________________________________<br class="gmail_msg">
erlang-questions mailing list<br class="gmail_msg">
<a href="mailto:erlang-questions@erlang.org" class="gmail_msg" target="_blank">erlang-questions@erlang.org</a><br class="gmail_msg">
<a href="http://erlang.org/mailman/listinfo/erlang-questions" rel="noreferrer" class="gmail_msg" target="_blank">http://erlang.org/mailman/listinfo/erlang-questions</a><br class="gmail_msg">
</blockquote></div>
_______________________________________________<br class="gmail_msg">
erlang-questions mailing list<br class="gmail_msg">
<a href="mailto:erlang-questions@erlang.org" class="gmail_msg" target="_blank">erlang-questions@erlang.org</a><br class="gmail_msg">
<a href="http://erlang.org/mailman/listinfo/erlang-questions" rel="noreferrer" class="gmail_msg" target="_blank">http://erlang.org/mailman/listinfo/erlang-questions</a><br class="gmail_msg">
</blockquote></div>