For as old as Erlang is, there’s a distinct lack of documentation about some aspects of it.  I’ve had a hard time finding information about how to run a code server, so I’m going to write down what I’ve learned here.

First, there are a couple of things I should note:

  1. While you can use the erlang runtime as a hack-as-you-go platform, it’s really better to follow the OTP principles; you get a lot of things for free that you otherwise end up writing yourself.  So, go ahead and use gen_servers, and write your .app and .rel scripts.  I know, it feels like a lot of metadata, and I hate systems that require a bunch of metadata files scattered around.  This is because a profusion of metadata files is a common feature of some of the worst software systems – Hibernate, for example – but in this case, it’s a necessary evil.  You* *can get pretty far without the files, but in the end, you’ll end up needing them anyway to take full advantage of the OTP, so go ahead and write them.
  2. Erlang has a really wicked (as in, good) distribution mechanism, so if you can use it, consider it.  When used, it’ll build a tar-ball containing everything you need, including a stripped-down runtime, to run  a worker node; copy the tar-ball to the destination machine, unpack it, and run your app.
  3. What I describe here is probably not Erlang an “best practice,” but it satisfies a need, and with a small tweak, it produces #3 above.

This whole thing hinges entirely on the boot script.  I have been unable to figure out a way to run a code server without a boot script; the Erlang documentation says this, indirectly, in several places by noting that the boot script tells the runtime how (and from where) to load code.  Although you might think, from reading the erl manpage, that you can inform the runtime about where to get code purely with -loader inet -hosts IP -id Name, you can’t – at least, I’ve not been able to coerce it so.  A boot script depends on an .app, and while it can be written by hand, it’s easier to generate it from a .rel.

This is what I believe to be the bare minimum to get a client to load code from a boot server:

  • A module with the application behaviour
  • A module with the supervisor behaviour
  • An .app file for your application
  • A .rel file, used to generate the boot script.

And if anybody knows of a way to use a code server with less crap, let me know.  If you want to start up services on the worker, that’s easy enough – just fire them off from within the supervisor.

I’m starting entirely from scratch; you should be able to copy/paste and get up and running:

44)~% mkdir myapp ; cd myapp
45)~/myapp % mkdir ebin src 
46)~/myapp % cat > src/myapp.erl
start(_,_) -> myapp_sup:start_link().
stop(_) -> ok. 
47)~/myapp % cat > src/myapp_sup.erl
-export([start_link/0, init/1]).
start_link() -> supervisor:start_link({local, ?MODULE}, ?MODULE, []).
init(_) -> {ok, {{one_for_one, 1, 60}, [] }}. 
48)~/myapp % cat > ebin/
{application, myapp,
  [{description, "MyApp"},
   {id, "MyApp"},
   {vsn, "0.0.1"},
   {modules, [ myapp, myapp_sup ]}, 
   {applications, [kernel,stdlib]},
   {maxT, infinity},
   {registered, [myapp, myapp_sup]},
   {mod, {myapp,[]}}
49)~/myapp % cat > ebin/myapp.rel
{release, {"MyApp", "0.0.1"}, {erts, "5.8.2"},
  [{kernel, "2.14.2"}
  ,{stdlib, "1.17.2"}
  ,{sasl, ""}
  ,{crypto, "2.0.2"}
  ,{myapp, "0.0.1"}
50)~/myapp % erlc -o ebin src/*.erl
51)~/myapp % erl -id bootserver -name bootserver -setcookie cookie -run erl_boot_server start
Erlang R14B01 (erts-5.8.2) [source] [smp:2:2] [rq:2
async-threads:0] [hipe] [kernel-poll:false]
Eshell V5.8.2  (abort with ^G) 
(bootserver@Sean-Russells-MacBook-Pro.local)1> systools:make_script("ebin/myapp", {path,["./ebin"]},local]).
(bootserver@Sean-Russells-MacBook-Pro.local)2> ls("ebin").          myapp.beam         myapp.boot         myapp.rel
myapp.script       myapp_sup.beam
User switch command
 --> q
52)~/myapp %

  You don’t need to run erl with all of those args if you’re just making the boot script; they’re needed to run the boot server.  Here’s the client – I’ve pared it down, because SASL dumps a bunch of debugging information that isn’t relevant for this tutorial:

53)~ % erl -id slave -name slave -setcookie cookie -loader inet -hosts -boot /Users/seanrussell/myapp/ebin/myapp
Erlang R14B01 (erts-5.8.2) [source] [smp:2:2] [rq:2] [async-threads:0] [hipe] [kernel-poll:false]
=PROGRESS REPORT==== 22-Jan-2011::08:40:58 === 
    supervisor: {local,sasl_safe_sup}
       started: [{pid,<0.40.0>},
           {mfargs,{alarm_handler,start_link,[]}}, {restart_type,permanent},

A bunch of stuff snipped here

=PROGRESS REPORT==== 22-Jan-2011::08:40:58 ===
   application: myapp
    started_at: 'slave@Sean-Russells-MacBook-Pro.local'
Eshell V5.8.2  (abort with ^G)
(slave@Sean-Russells-MacBook-Pro.local)1> pwd().
(slave@Sean-Russells-MacBook-Pro.local)2> code:which(myapp).
User switch command
 --> q
54)~ % 

Because I did this all on one machine, you might think that the slave is getting the code directly from the filesystem, but it isn’t; this works just as well from a different machine.

It’s instructive to take a look at the myapp.script file that systools creates, paying special attention to the paths.  If you remove the local argument from make_script, then systools will change the absolute paths to $ROOT, which it expands to the value of that variable on the server, which is in turn set by the erl command.  You can also add an argument {variable,[MYAPP,"."]}, and systools will replace the full path with the variable $MYAPP, which you can then define on the slave by adding the argument -env MYAPP /absolute/path/to/myapp/parent to the erl command.

Hopefully, this will help somebody else; it took me about three full days to figure this out.  Erlang is well documented, but having good documentation doesn’t always provide enough information for how to use a system, and Erlang OTP is horrible in that respect.  Once you get it up and running, though, it’s a thing of beauty.