Il saggio progetto svedese, rinato: Elixir e Phoenix

This entry is part 1 of 19 in the series Programming Languages

Negli ultimi 20 anni sono successe tante cose imprevedibili. Nel 1995 nasce Java. Nello stesso periodo nasce Erlang. Java inizialmente segue la strada culturale tracciata da Sun, e si configura come un linguaggio estremamente verboso, con API specifiche per la gestione della concorrenza (es keyword synchronized per gestire nativamente le zone critiche che necessitano di mutex). Inzialmente Java è per far girare applet, poi Sun presenta un chip ad hoc (picoJava) poi IBM ci mette del suo e il linguaggio si trasforma in una ottima piattaforma per lo sviluppo server side.

Nel corso di questi 20 anni Java ha cambiato pelle almeno tre volte (applet, enterprise e ora macchina virtuale come base per linguaggi come Scala e Clojure). Le sue ultime evoluzioni hanno tradito un po’ le sue origini: sono stati introdotti i template (considerati “troppo complessi” nei tutorial del 1995!) e le “default implementation” nelle interfacce (una idea orripilante, ma tant’é….).

Mantenere tutta la retro compatibilità sta portando Java a diventare un linguaggio bizzarro: il codice scritto 6/10 anni fa ora si può riscrivere in modo completamente diverso.

Erlang ha scelto una strada diversa, è pensato per essere completamente asicrono e funzionale. Forse è stata solo fortuna, ma il piccolo linguaggio svedese ha retto bene alla prova del tempo, viene aggiornato circa una volta l’anno e anche se ha beneficiato di una frazione degli investimenti miliardari di Java, si dimostra efficace.

Al giorno d’oggi, dove anche un cellulare ha almeno un paio di processori, sviluppare sistemi che possano performare il meglio possibile su più processori è un must. E’ notizia di questi mesi che la Intel ha smesso di spingere la legge di moore (che consentiva un raddoppio della potenza dei processori ogni 18 mesi) a causa dei costi eccessivi che il processo di mignaturizzazione porta con sé. Si badi bene che ancora non abbiamo toccato il limite fisco teorico, è un problema di strategia e di costi.

In tutto questo scenario c’è Elixir, una evoluzione sintattica di Erlang con qualche buona freccia al suo arco, e un buon numero di librerie disponibili

Elixir svecchia la sintassi di Erlang lo rende più amichevole, è retro compatibile con le liberie Erlang e ha consentito la creazione di framework alla ruby on ralis chiamato Phoenix che sostiene di essere molto efficente.

C’è un aspetto critico che mi porta a insistere ogni anno su Erlang.

Quando facevamo l’università c’era un problema noto a tutti nello sviluppo di applicazioni client server (non necessariamente web).

C’erano due tipi sostanziali di tecniche:

  1. server multi richiesta multi processo
    Più semplici da programmare prevedevano la creazione di un nuovo thread per ogni richiesta in arrivo.
    Vantaggi: scala se hai tante richieste contemporanee, ma il costo associato alla creazione di un thread è molto alto.
    Ideale per task CPU Bound.
  2. server multi richiesta mono processo
    Più complessi e intricati da sviluppare, prevedevano un server che non si bloccava in attesa dei dati ma processava la prima socket client con dati pronti
    Vantaggi: scala bene se hai moltissime connessioni con parecchie latenze
    Ideal per sistemi I/O bound che non riescano a saturare il mono-processo.

Ora nessuna di queste due tecniche in realtà è migliore dell’altra, e in OOP non è banale bilanciare tra questi due approcci.
Per come è pensato Erlang però, è possibile avere i vantaggi di entrambe le teniche in un colpo solo!

Difatti l’architettura a processi stateless (anziché a istanze di classi su cui si “montano” thread) consente di sviluppare una macchina virtuale supervisore (un application server, per gli amanti di Java) che possa di volta in volta bilanciare tra l’esigenza (1) e (2).

Non è possibile ottenere questi vantaggi con un framework, come ha evidenziato il creatore di Erlang in un’email che ho ripreso tempo fa: si devono avere primitive di invio e ricezione di messaggi asincroni, innervate nella semantica del linguaggio.

Il fatto poi che Erlang sia funzionale e senza stato è stata la sinergia vincente. In caso di errore è banale far ripertire la parte di processi Erlang in errore; poiché sono privi di stato, l’anomalia viene limitata al caso critico.

Processi separati e senza stato consentono un Garbge Collector più semplice; in Java siamo dovuti arrivare al G1, la nuova implementazione a bassa latenza per risolvere alcuni problemi che i gestori di memoria automatici si portano dietro.

 

 

Property Based Testing Tool

Tonight I stubled upon PropEr, a Property based testing tool for Erlang. It seems a very smart idea: instead of writing a single unit test, you write a set of properties your code must satisfy.

From the site:

A property-based testing tool, when supplied with this information, should randomly produce progressively more complex valid inputs, then apply those inputs to the program while monitoring its execution, to ensure that it behaves according to its specification, as outlined in the supplied properties.

QuickCheck is the father of PropEr: give a try to thw wikipedia page to see if your language is supported and start a new way of doing…Test Driven Developement!

Recuperare Erlang

Erlang è un linguaggio a mio avviso sottostimato, che ha parecchie cose da insegnare ai vari Scala/Rust/Java(Script) out of there… In particolare ci sono un insieme di feature di Erlang che prese singolarmente non sono difficili da comprendere e implementare, ma è l’insieme delle idee fondanti di Erlang che lo rende assai diverso dagli altri.

Erlang ha:

  1. Una sintassi che prevede che le variabili possano essere assegnate una volta sola. Questo rende side-effect free il suo codice (almeno dal punto di vista formale).
    Però non è ostico come linguaggi che non prevedono variabili oppure le prevedono  mutabilil.
  2. Poiché le strutture dati (e quindi i messaggi tra processi) sono immutabili, l’invio di un messaggio si traduce nel copiare un messaggio dal chiamante al ricevente, tra aree non-condivise (e quindi senza mutex).
  3. Poiché i processi sono side effect-free e state-less, e poiché i messaggi sono tutti asincroni, è molto facile gestire un errore che genera il crash di un processo: cioé è sufficiente far ripartire il processo in crash senza bisogno di ricostruitire il suo stato (perché….non lo ha!).
  4. Ricorsivamente, se un processo che va in crash ne fa morire altri, è facile ricostruirli tutti.
    Poiché i messaggi sono asincroni, un errore di comunicazione si traduce in un semplice “rallentamento”.
    Un errore che si verifica casualmente (per un bug molto subdolo per esempio) si traduce anch’esso in un “rallentamento”.
  5. Erlang minimizza la latenza di risposta, e questo porta automaticamente a massimizzare l’utilizzo dei pool di risorse ed il throughput.
    Per esempio in ErZauker (una applicazione che ho scritto e che ha un numero massimo di connessioni a Redis) Erlang finiva per usare sempre il 100% delle connessioni disponibili, e teneva questo valore sempre al massimo.
  6. Poiché per ogni problema l’approccio è creare processi leggeri, la ErlangVM è facilitata nel minimizzare la latenza poiché può agire su un numero sempre maggiore di message queues per fare il tuning. Se per assurdo tutto fosse fatto da un processo solo, non sarebbe possibile massimizzare la latenza.
  7. E’ banale in Erlang definire le startegie di recovery, tanto che i supervisori “generici” sono codificati e già forniti
  8. Erlang è uno dei pochi linguaggi full-stack production ready. Potete esporre una applicazione Erlang direttamente su internet, con il web server in Erlang, il db in Erlang ecc
    Di converso con Python/Ruby/Java ciò non è possibile/consigliabile.

Ognuno delle due visioni del mondo ha pro e contro, tra i punti di debolezza di Erlang rileviamo:

  1. Erlang per lunghissimo tempo non ha avuto una struttura dati di tipo record “vera” ma tuple indicizzate dal compilatore (l’ultima versione introduce finalmente il tipo “map”).
  2. Benché abbia un compilatore di codice nativo (HiPe), Erlang risulta molto più lento di Java e di altri linguaggi: può compensare girando su più macchine (è banale rilocare un processo!).

 

 

Riferimenti

http://www.erlang.org/doc/efficiency_guide/processes.html

Erlang: a lesson to learn…again!

This entry is part 20 of 19 in the series Programming Languages

Erlang is a great language.

It was just released version 18, so I wanna to come to the party!
On April, Erlang father’s Joe Armstrong give us a very interesting leasson I want to tell about.

There was a long thread titled “Erlang and Akka, The Sequel” on the erlang mailing list, reasoning about the need of some standard pattern on Promises and Furure. A lot of JavaScript libraries deal about that (also jQuery has its implementation).

I want to report the Joe Amstrong reply becuse it give us a very clear understanding on the reason Erlang is different and you should at least try it once.

Form Joe email (code bold by me)
Promises, futures and so on are extremely easy to implement in Erlang.
This is how I explain things when I teach Erlang:

We’ll start with an RPC – we can write this in many ways.
One way might be:

  rpc(Pid, Query) ->
   Ref = make_ref(),
   Pid ! {self(), Ref, Query},
   receive
     {Ref, Response} ->
         Response
   end.

The server that receives this message does something like:

 receive
    ...
    {From, Ref, Query} ->
        Response = ...
           From ! {Ref, Response}
    ...
 end,

This basic pattern is repeated all over the place is with many small
variations.

20140509-091351.jpg
Remember this pattern (repeat 100 times) – this should dance out of your
fingertips and not require conscious thought

For example, all the gen_server does is wrap this pattern
with a few convenience functions.

Now keeping the server code unchanged we can modify the RPC

Start with the original (and stare at the added comment):

 

rpc(Pid, Query) ->
 Ref = make_ref(),
 Pid ! {self(), Ref, Query},
 %% watch this space **************
 receive
 {Ref, Response} ->
 Response
 end.

Now I’ll rename rpc as rpc1 and split it into two functions at the comment:

rpc1(Pid, Query) ->
 Ref = make_ref(),
 Pid ! {self(), Ref, Query},
 Ref.
wait(Ref) ->
 receive
 {Ref, Response} ->
 Response
 end.

So obviously

 

rpc(Pid, Query) ->
 Ref = rpc1(Pid, Query),
 wait(Ref).

 

How about some renaming? I’ll call rpc1 “promise” and wait “yield”

So

promise(Pid, Query) ->
 Ref = make_ref(),
 Pid ! {self(), Ref, Query},
 Ref.
yield(Ref) ->
 receive
 {Ref, Response} ->
 Response
 end.

<aside>we’ve invented futures :-) </aside> Now we can do something in the gap between the promise and the yield:

compute_something(...) ->
 P1 = promise(...)
 Val1 = ... some local computation ...
 Val2 = yield(P1),
 ...

So now Val1 and Val2 are computed in parallel. (We’ve now invented one of the basic mechanisms for parallel programming this might appear as parbegin … parend in some programming language :-)

The reason why Erlang does not have futures/promises/ .. or whatever else you might like to call them is that they are trivially implemented in a few lines of code using just spawn/send/receive. In languages that are basically sequential this is not possible – that’s why it’s a big deal (TM) to have libraries or language features to support this.

And now for the tricky part …. Broken Promises – Remember that scene in Casablanca when Iisa confronts Rick, this is all about broken promises. Fulfilled promises are easy to deal with, but we must ask what happens if the server crashes and never sends back a message? Now life gets difficult, and as Rick found out the consequences of a broken promise lead to all sorts of problems…

In Erlang, dealing with broken promises is possible (though not easy) using links, monitors and by trapping exits.

Giovanni humble note: because Erlang is a functional side effect-free language, dealing with broken promise is a lot easier.

On Ruby/Python/Java/PHP if you get an exception you fail and report the error to the user:  you have an hard time figuring how to redo your work, and you end up rolling back your work. You need to be sure your object are in the right state, your variable are reset and so on: a nightmare-

With Erlang you can relaunch the processes, and the entire business logic can be playback again.
There are some limitations (like dealing with data already commited, system to resync and so on) but is is a lot easier in a functional language.

 

The spawn/send/receive group of primitives are used to program the non error cases where things don’t go wrong. trap_exits/links/monitors deal with error cases where things go wrong.

The gen_servers, supervision trees and so on just wrap these primitives in combinations that we have found useful for  solving “common” problems.

In cases where the library behaviors don’t do exactly what you want it’s often easier to “roll you own” rather than shoehorning your problem into the wrong solution.

The reason there are not a load of design patterns is that we don’t need them.

We do need to teach the basics though. spawn/send/receive are as basic to Erlang as for/if/case are to sequential programming .

This is where we have a problem – in sequential languages nobody bothers to teach what for/if/case/switch/while etc do – it is “implicit knowledge that all programmers have” (I’m excluding total beginners here) – Experienced Erlang programers know the power of spawn/send/receive so rarely bother to explain how to build things with these primitives.

My advice would be to stare hard at spawn/send/receive and memorise the RPC pattern and the basic client/server setup.

Then understand links. Write things without using the libraries – then learn the libraries. Note I said you can write RPC in “many” ways – what did I mean by this? To illustrate, here’s are some variations:

rpc(Pid, Query) ->
 Pid ! {self(), Query},
 receive
 {Pid, Response} ->
 Response
 end.

or

rpc(Pid, Query) ->
 Pid ! {self(), Query},
 receive
 {Pid, Response} ->
 Response
 after 10000 ->
 exit(timout)
 end.

or

rpc(Pid, Query) ->
 Pid ! {self(), Query},
 receive
 {Pid, Response} ->
 {ok, Response}
 after 10000 ->
 {error, timeout}
 end.

You can see how the basic pattern remains – we can add timeouts
etc. but then we have to decide what to do with the variants.

Only the most common patterns are found in the gen_server
so it’s a good idea to understand the basic pattern and
modify it to your specific needs.

/Joe