<?xml version="1.0"?>
<rss version="2.0"
     xmlns:dc="http://purl.org/dc/elements/1.1/"
     xmlns:dcterms="http://purl.org/dc/terms/" >
<channel>
<title>Erlang - Justin&#x27;s Ramblings</title>
<link>http://bouncybouncy.net//ramblings/tags/erlang/</link>
<description>BB.Net</description>
<item>
	
	<title>erlang basic distributed application</title>
	
	<guid>http://bouncybouncy.net//ramblings/posts/erlang_basic_distributed_application/</guid>
	<link>http://bouncybouncy.net//ramblings/posts/erlang_basic_distributed_application/</link>
	
	
	<category>tags/erlang</category>
	
	<category>tags/tech</category>
	
	
	<pubDate>Mon, 09 Jun 2008 18:15:31 -0400</pubDate>
	<dcterms:modified>2008-06-10T11:56:22Z</dcterms:modified>
	
	<description><![CDATA[<p><a href=
"http://erlang.org/doc/design_principles/part_frame.html">Erlang
with OTP</a> is a fairly powerful framework for creating
distributed redundant applications. The basic
<code>gen_server</code> behavior can easily extended to create a
redundant server with built in failover. With <a href=
"http://www.erlang.org/doc/apps/mnesia/index.html">Mnesia</a> you
also get a replicated Database.</p>
<p>I've been trying to figure out how exactly this is supposed to
work, so I've been working on a quick application to demonstrate
this. It's nothing fancy, just a simple set(k,v) and get(k)
API.</p>
<p>The files are available as a tarball from here <a href="http://bouncybouncy.net//ramblings/tags/erlang/../../files/erlang/ddict.tgz">ddict.tgz</a></p>
<div class="syntax">
<pre>
<span class="synType">-module</span>(ddict)<span class=
"synSpecial">.</span>
<span class=
"synStatement">-</span>behaviour(gen_server)<span class="synSpecial">.</span>
<span class="synType">-export</span>([start<span class=
"synStatement">/</span><span class=
"synConstant">0</span>,stop<span class=
"synStatement">/</span><span class=
"synConstant">0</span>,terminate<span class=
"synStatement">/</span><span class=
"synConstant">2</span>])<span class="synSpecial">.</span>
<span class="synType">-export</span>([init<span class=
"synStatement">/</span><span class=
"synConstant">1</span>, handle_call<span class=
"synStatement">/</span><span class=
"synConstant">3</span>, handle_cast<span class=
"synStatement">/</span><span class=
"synConstant">2</span>,handle_info<span class=
"synStatement">/</span><span class=
"synConstant">2</span>])<span class="synSpecial">.</span>
<span class="synType">-export</span>([create_schema<span class=
"synStatement">/</span><span class=
"synConstant">0</span>])<span class="synSpecial">.</span>
<span class="synType">-export</span>([<span class=
"synIdentifier">get</span><span class=
"synStatement">/</span><span class=
"synConstant">1</span>,set<span class=
"synStatement">/</span><span class=
"synConstant">2</span>])<span class="synSpecial">.</span>


<span class=
"synType">-define</span>(GD,{global, ddict})<span class="synSpecial">.</span>

<span class="synType">-include</span><span class=
"synSpecial">_</span>lib(<span class=
"synConstant">"stdlib/include/qlc.hrl"</span>)<span class=
"synSpecial">.</span>
<span class="synType">-record</span>(rec, {key, value})<span class=
"synSpecial">.</span>


init_mnesia() <span class="synStatement">-&gt;</span>
    <span class="synIdentifier">mnesia</span><span class=
"synSpecial">:</span><span class="synIdentifier">start</span>(),
    ok <span class="synStatement">=</span> <span class=
"synIdentifier">mnesia</span><span class=
"synSpecial">:</span><span class=
"synIdentifier">wait</span><span class=
"synSpecial">_</span><span class=
"synIdentifier">for</span><span class=
"synSpecial">_</span><span class=
"synIdentifier">tables</span>([rec], 2000)<span class=
"synSpecial">.</span>

init(<span class="synSpecial">_</span>Arg) <span class=
"synStatement">-&gt;</span>
    <span class="synIdentifier">process_flag</span>(<span class=
"synSpecial">trap_exit</span>, <span class=
"synStatement">true</span>),
    <span class="synIdentifier">io</span><span class=
"synSpecial">:</span><span class=
"synIdentifier">format</span>(<span class=
"synConstant">"dict server starting</span><span class=
"synSpecial">~n</span><span class="synConstant">"</span>),
    init_mnesia(),
    {ok, []}<span class="synSpecial">.</span>

start() <span class="synStatement">-&gt;</span>
    <span class="synIdentifier">gen</span><span class=
"synSpecial">_</span><span class=
"synIdentifier">server</span><span class=
"synSpecial">:</span><span class=
"synIdentifier">start</span><span class=
"synSpecial">_</span><span class=
"synIdentifier">link</span>(?GD, ddict, [], [])<span class=
"synSpecial">.</span>

stop() <span class="synStatement">-&gt;</span>
    <span class="synIdentifier">gen</span><span class=
"synSpecial">_</span><span class=
"synIdentifier">server</span><span class=
"synSpecial">:</span><span class=
"synIdentifier">cast</span>(?GD, stop)<span class=
"synSpecial">.</span>

terminate(Reason, State) <span class="synStatement">-&gt;</span>
   <span class="synIdentifier">io</span><span class=
"synSpecial">:</span><span class=
"synIdentifier">format</span>(<span class=
"synConstant">"dict server terminating</span><span class=
"synSpecial">~n</span><span class=
"synConstant">"</span>)<span class="synSpecial">.</span>

<span class="synComment">%"model" methods</span>
do_get(Key) <span class="synStatement">-&gt;</span>
    Res <span class="synStatement">=</span> <span class=
"synIdentifier">mnesia</span><span class=
"synSpecial">:</span><span class=
"synIdentifier">dirty</span><span class=
"synSpecial">_</span><span class=
"synIdentifier">read</span>({rec, Key}),
    <span class="synStatement">case</span> Res <span class=
"synStatement">of</span> 
        [] <span class="synStatement">-&gt;</span> undefined;
        [Rec] <span class=
"synStatement">-&gt;</span> Rec#rec<span class=
"synSpecial">.</span>value
    <span class="synStatement">end</span><span class=
"synSpecial">.</span>

do_set(Key, Value) <span class="synStatement">-&gt;</span>
    F <span class="synStatement">=</span> <span class=
"synStatement">fun</span>() <span class="synStatement">-&gt;</span>
            Row <span class=
"synStatement">=</span> #rec{key<span class=
"synStatement">=</span>Key, value<span class=
"synStatement">=</span>Value},
            <span class="synIdentifier">mnesia</span><span class=
"synSpecial">:</span><span class="synIdentifier">write</span>(Row)
        <span class="synStatement">end</span>,
    {atomic, ok} <span class="synStatement">=</span> <span class=
"synIdentifier">mnesia</span><span class=
"synSpecial">:</span><span class=
"synIdentifier">transaction</span>(F),
    ok<span class="synSpecial">.</span>

<span class="synComment">%"controller" methods</span>
handle_call({<span class=
"synIdentifier">get</span>, Key}, From, State) <span class=
"synStatement">-&gt;</span>
    Rec <span class="synStatement">=</span> do_get(Key),
    {reply, Rec, State};

handle_call({set, Key, Value}, From, State) <span class=
"synStatement">-&gt;</span>
    Rec <span class="synStatement">=</span> do_set(Key, Value),
    {reply, Rec, State}<span class="synSpecial">.</span>

handle_cast(stop, State) <span class="synStatement">-&gt;</span>
    <span class="synIdentifier">io</span><span class=
"synSpecial">:</span><span class=
"synIdentifier">format</span>(<span class=
"synConstant">"ddict server stopping</span><span class=
"synSpecial">~n</span><span class="synConstant">"</span>),
    {stop, <span class=
"synStatement">normal</span>, State}<span class=
"synSpecial">.</span>

handle_info(Info, State) <span class="synStatement">-&gt;</span>
    {noreply, State}<span class="synSpecial">.</span>


<span class="synComment">%"client api" methods</span>
<span class="synIdentifier">get</span>(Key) <span class=
"synStatement">-&gt;</span>
   <span class="synIdentifier">gen</span><span class=
"synSpecial">_</span><span class=
"synIdentifier">server</span><span class=
"synSpecial">:</span><span class=
"synIdentifier">call</span>(?GD, {<span class=
"synIdentifier">get</span>, Key})<span class="synSpecial">.</span>

set(Key,Value) <span class="synStatement">-&gt;</span>
   <span class="synIdentifier">gen</span><span class=
"synSpecial">_</span><span class=
"synIdentifier">server</span><span class=
"synSpecial">:</span><span class=
"synIdentifier">call</span>(?GD, {set, Key, Value})<span class=
"synSpecial">.</span>


create_schema() <span class="synStatement">-&gt;</span>
    <span class="synIdentifier">mnesia</span><span class=
"synSpecial">:</span><span class=
"synIdentifier">create</span><span class=
"synSpecial">_</span><span class=
"synIdentifier">schema</span>([<span class=
"synSpecial">node</span>()|<span class=
"synIdentifier">nodes</span>()]),
    <span class="synIdentifier">mnesia</span><span class=
"synSpecial">:</span><span class="synIdentifier">start</span>(),
    <span class="synComment">%this is defnitely wrong</span>
    <span class="synIdentifier">lists</span><span class=
"synSpecial">:</span><span class=
"synIdentifier">foreach</span>(<span class=
"synStatement">fun</span>(N) <span class=
"synStatement">-&gt;</span>
        <span class="synIdentifier">io</span><span class=
"synSpecial">:</span><span class=
"synIdentifier">format</span>(<span class=
"synConstant">"starting mnesia on </span><span class=
"synSpecial">~w~n</span><span class="synConstant">"</span>, [N]),
        <span class="synIdentifier">rpc</span><span class=
"synSpecial">:</span><span class=
"synIdentifier">call</span>(N, mnesia, start, [])
    <span class="synStatement">end</span>, <span class=
"synIdentifier">nodes</span>()),
    <span class="synIdentifier">mnesia</span><span class=
"synSpecial">:</span><span class=
"synIdentifier">create</span><span class=
"synSpecial">_</span><span class=
"synIdentifier">table</span>(rec, [
        {disc_copies, [<span class=
"synSpecial">node</span>()|<span class=
"synIdentifier">nodes</span>()]},
        {attributes, <span class=
"synStatement">record_info</span>(fields, rec)}
    ])<span class="synSpecial">.</span>

</pre>
<span class="synTitle"><a href=
"http://www.bouncybouncy.net/main%20gen_server%20file">download
file "main gen_server file"</a></span></div>
<p>The key to this server being distributed is the use of {global,
ddict} as the server name, instead of {local, ddict}. This enables
other nodes in the cluster to see this server.</p>
<p><code>do_get()</code> and <code>do_set()</code> are the "model"
like methods that deal with mnesia. <code>handle_call</code>
defines the <code>gen_server</code> api. get() and set() are helper
functions that call the remote gen server. If there was more to
this module, it would be a good idea to put these methods in
separate modules.</p>
<p>The one thing I am not sure about is the
<code>create_schema()</code> method. I'm sure there is a propper
way to initalize mnesia on a cluster, I just have no idea what it
is yet <img src="http://bouncybouncy.net//ramblings/tags/erlang/../../../smileys/smile.png" alt=":-)" /></p>
<p>To make this into a propper gen server the supervisor and
application needs to be defined with the following three files:</p>
<div class="syntax">
<pre>
<span class="synType">-module</span>(ddict_sup)<span class=
"synSpecial">.</span>
<span class=
"synStatement">-</span>behaviour(supervisor)<span class="synSpecial">.</span>

<span class="synType">-export</span>([start_link<span class=
"synStatement">/</span><span class=
"synConstant">0</span>])<span class="synSpecial">.</span>
<span class="synType">-export</span>([init<span class=
"synStatement">/</span><span class=
"synConstant">1</span>])<span class="synSpecial">.</span>

start_link() <span class="synStatement">-&gt;</span>
    <span class="synIdentifier">supervisor</span><span class=
"synSpecial">:</span><span class=
"synIdentifier">start</span><span class=
"synSpecial">_</span><span class=
"synIdentifier">link</span>(ddict_sup, [])<span class=
"synSpecial">.</span>

init(<span class="synSpecial">_</span>Args) <span class=
"synStatement">-&gt;</span>
    {ok, {{one_for_one, 10, 60},
          [{ddict, {ddict, start, []},
            permanent, brutal_kill, worker, [ddict]}]}}<span class=
"synSpecial">.</span>

</pre>
<span class="synTitle"><a href=
"http://www.bouncybouncy.net//ramblings/files/erlang/ddict/ddict_sup.erl">download
file "/ramblings/files/erlang/ddict/ddict_sup.erl"</a></span></div>
<div class="syntax">
<pre>
<span class="synType">-module</span>(ddict_app)<span class=
"synSpecial">.</span>
<span class=
"synStatement">-</span>behaviour(application)<span class=
"synSpecial">.</span>

<span class="synType">-export</span>([start<span class=
"synStatement">/</span><span class=
"synConstant">2</span>, stop<span class=
"synStatement">/</span><span class=
"synConstant">1</span>,go<span class=
"synStatement">/</span><span class=
"synConstant">0</span>])<span class="synSpecial">.</span>

start(<span class="synSpecial">_</span>Type, <span class=
"synSpecial">_</span>Args) <span class="synStatement">-&gt;</span>
    <span class="synIdentifier">ddict</span><span class=
"synSpecial">_</span><span class=
"synIdentifier">sup</span><span class=
"synSpecial">:</span><span class=
"synIdentifier">start</span><span class=
"synSpecial">_</span><span class=
"synIdentifier">link</span>()<span class="synSpecial">.</span>

stop(<span class="synSpecial">_</span>State) <span class=
"synStatement">-&gt;</span>
    <span class="synIdentifier">io</span><span class=
"synSpecial">:</span><span class=
"synIdentifier">format</span>(<span class=
"synConstant">"ddict server terminating</span><span class=
"synSpecial">~n</span><span class="synConstant">"</span>),
    ok<span class="synSpecial">.</span>

go() <span class="synStatement">-&gt;</span>
    <span class="synIdentifier">application</span><span class=
"synSpecial">:</span><span class=
"synIdentifier">start</span>(ddict)<span class=
"synSpecial">.</span>

</pre>
<span class="synTitle"><a href=
"http://www.bouncybouncy.net//ramblings/files/erlang/ddict/ddict_app.erl">download
file "/ramblings/files/erlang/ddict/ddict_app.erl"</a></span></div>
<div class="syntax">
<pre>
{application, ddict,
[
    {mod, {ddict_app,[]}}
]}.

</pre>
<span class="synTitle"><a href=
"http://www.bouncybouncy.net//ramblings/files/erlang/ddict/ddict.app">download
file "/ramblings/files/erlang/ddict/ddict.app"</a></span></div>
<p>To get erlang to start this application on boot, a config file
for each node needs to be written:</p>
<div class="syntax">
<pre>
[{kernel,
  [{distributed, [{ddict, 3000, [one@media, {two@media}]}]},
   {sync_nodes_optional, [two@media]},
   {sync_nodes_timeout, 5000}
  ]
 }
].

</pre>
<span class="synTitle"><a href=
"http://www.bouncybouncy.net//ramblings/files/erlang/ddict/one.config">download
file "/ramblings/files/erlang/ddict/one.config"</a></span></div>
<div class="syntax">
<pre>
[{kernel,
  [{distributed, [{ddict, 3000, [one@media, {two@media}]}]},
   {sync_nodes_optional, [one@media]},
   {sync_nodes_timeout, 5000}
  ]
 }
].

</pre>
<span class="synTitle"><a href=
"http://www.bouncybouncy.net//ramblings/files/erlang/ddict/two.config">download
file "/ramblings/files/erlang/ddict/two.config"</a></span></div>
<p>To create the initial database I ran the
<code>ddict:create_schema</code> method, which I'm sure is
completely incorrect, but it works:</p>
<div class="syntax">
<pre>
erl -sname one -config one.config
erl -sname two -config two.config

(one@media)1&gt; ddict:create_schema().
starting mnesia on two@media
{atomic,ok}
(one@media)2&gt; mnesia:info().
...
running db nodes   = [two@media,one@media]
disc_copies        = [rec,schema]
[{one@media,disc_copies},{two@media,disc_copies}] = [schema,rec]
...
ok

</pre>
<span class="synTitle"><a href=
"http://www.bouncybouncy.net//ramblings/files/erlang/ddict/create_db.txt">download
file "/ramblings/files/erlang/ddict/create_db.txt"</a></span></div>
<p>Once that is done, the application can be started with</p>
<div class="syntax">
<pre>
erl  -pa . -sname one -config one.config -s ddict_app go
erl  -pa . -sname two -config two.config -s mnesia start -s ddict_app go

</pre></div>
<p>I have to start mnesia separately on the second VM because I
haven't yet figured out how mnesia should be started when dealing
with distributed applications. mnesia needs to be running on both
nodes, but not the ddict application itself.</p>
<p>Once it is running, you can call ddict:set("Foo","bar") and
ddict:get("Foo"). You can also kill either VM, and it will restart
the server after 3 seconds on the other node.</p>
<p><a href="http://www.reddit.com/info/6mois/comments/">Comments
here</a></p>

]]></description>
	
</item>

</channel>
</rss>
