Tuesday, August 5, 2008

Erlang tips and tricks: interactive shell

In my previous post I described issues you can run into when configuring an Erlang cluster. Now let's move a step further.

One of the most impressive Erlang features is its unique ability to do a hot code swapping. It means you can exchange the code of a running system without stopping it! Pretty neat, huh? Let's have a look at a very simple server, that just loops and displays its status when asked:
-module(test).
-export([start/0, loop/0]).
start() ->
register(test, spawn(test, loop, [])).
loop() ->
receive
status ->
io:format("~n Status OK ~n"),
loop();
reload ->
test:loop();
quit ->
ok;
_ ->
loop()
end.
The main loop waits for signals from clients and according to the signal does one of the following:
1) On status it displays a text "Status OK" and continues to loop.
2) On reload it hot swaps the server code.
3) On quit it quits with finishes with ok.
4) On any other signal it just loops.
After saving this code as test.erl and compiling with:
erlc test.erl
you can start a REPL shell and run the server with:
test:start().
The start() function spawns a new Erlang process and registers it on the currently running node, so you can call it through a name instead of through its process ID like this:
test ! status.
If you play around with Erlang you already know that. But how about calling test from another node? When you connect another node to the cluster and try to call it you will get an error saying that test is not a registered name. The answer is simple, but unfortunately not so easy to find in Erlang documentation. You need to call it with a tuple (pair) containing both node name and process name:
{test, test1@host.domain} ! status.
So now comes the time for hot swapping. You can edit the source file, change "Status OK" to any other text of your choice and recompile - of course without leaving the interactive shell, since our server is supposed to have 100% uptime :-)
Afterwards you switch back to REPL, do
test ! reload.
than
test ! status.
and you see... no change whatsoever. Why? Because Erlang shell caches executable code. If you want to load the modified version of the code you just compiled into REPL, you need to tell it to do so with:
l(test).
Suppose you have more nodes in your cluster where the code exists and you want to refresh Erlang cache on all of them, you should use:
nl(test).
You can also use the latter command to distribute your code across the cluster, without the need of sending the compiled binary test.beam to all of them through FTP or SCP. Sweet, isn't it?

No comments: