<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Richard Jones, Esq. &#187; c</title>
	<atom:link href="http://www.metabrew.com/article/tag/c/feed" rel="self" type="application/rss+xml" />
	<link>http://www.metabrew.com</link>
	<description>Erlang, PHP, C, C++, Java, PostgreSQL, MySQL, Hadoop, Linux, awk, bash, sed, grep, screen, vim, irc, ssh etc...</description>
	<lastBuildDate>Sun, 20 Dec 2009 18:59:32 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.0</generator>
		<item>
		<title>Rewriting Playdar: C++ to Erlang, massive savings</title>
		<link>http://www.metabrew.com/article/rewriting-playdar-c-to-erlang-massive-savings/</link>
		<comments>http://www.metabrew.com/article/rewriting-playdar-c-to-erlang-massive-savings/#comments</comments>
		<pubDate>Wed, 21 Oct 2009 21:29:15 +0000</pubDate>
		<dc:creator>RJ</dc:creator>
				<category><![CDATA[playdar]]></category>
		<category><![CDATA[programming]]></category>
		<category><![CDATA[c]]></category>
		<category><![CDATA[erlang]]></category>
		<category><![CDATA[rewrite]]></category>

		<guid isPermaLink="false">http://www.metabrew.com/?p=279</guid>
		<description><![CDATA[I&#8217;ve heard many anecdotes and claims about how many lines of code are saved when you write in Erlang instead of [C++/other language]. I&#8217;m happy to report that I now have first-hand experience and some data to share. I initially wrote Playdar in C++ (using Boost and Asio libraries), starting back in February this year. [...]]]></description>
			<content:encoded><![CDATA[<p>
I&#8217;ve heard many anecdotes and claims about how many lines of code are saved when you write in Erlang instead of [C++/other language]. I&#8217;m happy to report that I now have first-hand experience and some data to share.
</p>
<p>
I initially wrote Playdar in C++ (using Boost and Asio libraries), starting back in February this year. I was fortunate to be working with some experienced developers who helped me come to terms with C++. There were three of us hacking on it regularly up until a few months ago, and despite being relatively new to C++, I&#8217;ll say that we ended up with a well designed and robust codebase, all things considered.
</p>
<h2>On Feeling Smug</h2>
<p>
I&#8217;ll admit I felt rather smug making it all work in C++ with Boost and ASIO. Getting it to build on all three platforms and dynamically load extensions (DLLs etc) at runtime in a cross-platform way was also quite satisfying (I had plenty of help with that side of things).  I learned a lot about C++, Boost, ASIO and CMake. But, as the codebase grew, I began to seriously question my decision to use C++.
</p>
<p>
My initial reasons for choosing C++ were twofold:</p>
<ul>
<li>Distribution &#8211; shipping the Erlang VM didn&#8217;t sound like fun</li>
<li><a href="http://developer.kde.org/~wheeler/taglib.html" target="taglib">Taglib</a>  &#8211; *the* library to read metadata from audio files (mp3, m4a, ogg etc) is C++</li>
</ul>
<p>It turns out Playdar is naturally a good fit for Erlang &#8211; it does lots in parallel, and lots of stuff it does is asynchronous  and event based. Even with all the stuff you get with Boost, multithreaded stuff in C++ is inelegant, to put it kindly.
</p>
<h2>SLOCed and Loaded</h2>
<p>
Anyway, a couple of weeks ago I sat down to re-implement Playdar from scratch in Erlang. I thrashed out the guts of it in a couple of days, and by the end of the week I almost had it 1:1 features with the C++ codebase. There&#8217;s still a bit of C++ left &#8211; code to interface with taglib.</p>
<p>Using the SLOCcount tool (SLOC=source lines of code) I counted the lines of code in various modules from both codebases, here are the results:<br />
<br/></p>
<style type="text/css">
#matrix td{ font-size:90%; vertical-align:top; padding: 3px; } #matrix tr { background: #f0f0f0; } #matrix tr.odd { background: #ddd; }
#matrix td.b {font-size:100%; font-weight:bold;}
</style>
<table id="matrix" border="0">
<tbody>
<tr>
<td class="b"></td>
<td class="b">Erlang Version</td>
<td class="b">C++ Version</td>
<td class="b">Savings</td>
</tr>
<tr class="odd">
<td class="b">Core Daemon</td>
<td>1,100</td>
<td>4,491</td>
<td>75%</td>
</tr>
<tr>
<td class="b">Library + Scanner</td>
<td>197 + 167.cpp</td>
<td>1,355</td>
<td>73%</td>
</tr>
<tr class="odd">
<td class="b">LAN Resolver</td>
<td>105</td>
<td>427</td>
<td>75%</td>
</tr>
<tr>
<td class="b">P2P</td>
<td>463</td>
<td>1,762</td>
<td>74%</td>
</tr>
<tr class="odd b">
<td class="b">TOTAL</td>
<td><em>2,032</em></td>
<td><em>8,035</em></td>
<td><em>75%</em></td>
</tr>
</tbody>
</table>
<p><strong><br />
75% less lines of code using Erlang compared to C++ to implement the same thing &#8211; not too shabby :)<br />
</strong><br />
The second time around writing in Erlang I knew exactly what I was building, so it&#8217;s unfair to compare development time of the two codebases, but given how fast I can type I reckon I saved a good few hours of just pounding the keyboard to input the code (and countless hours of debugging: Erlang tends to work first time, really). Well I&#8217;m not sure if &#8220;saved&#8221; is the right word, considering It was working in C++ already, but it&#8217;s my time to waste :)
</p>
<p>
If you count the third party code bundled with both codebases (excluding boost/asio!) then the erlang codebase saves a whopping 92%. I&#8217;m more interested in the savings in code I had to write, however.
</p>
<h2>Memory and CPU Usage</h2>
<p>
I&#8217;ve done some preliminary comparisons between both projects, when it comes to CPU and memory usage both projects are pretty similar. The Erlang codebase uses slightly more memory than C++ at the moment, but I&#8217;m convinced I can get that down to at least as low as the C++ project was. I picked up a few optimization tricks from my three-part <a href="http://www.metabrew.com/article/a-million-user-comet-application-with-mochiweb-part-1/">Million-user comet experiment</a> in Erlang earlier this year. I&#8217;ll post more about this if I learn any new tricks.
</p>
<p>
One thing I&#8217;ve realised about the Erlang codebase is that I&#8217;ve used processes to encapsulate state (active queries, specifically)  where I didn&#8217;t really need to. It seemed sensible at the time, but it&#8217;s probably just a waste of memory. I&#8217;m going to change it to spawn processes to get the work done (ie, a process that runs the query) but not necessarily just to maintain state.</p>
<p><h2>Distribution to the desktop</h2>
<h3>C++</h3>
<p>You just have to make sure that you build everything and ship with any DLLs along with checks in the installer for system libraries needed (runtime dlls). Oh, and make sure you don&#8217;t change the plugin binary interface in the main app, or new plugins will crash and burn when you load them. Add a check for that. Oh and be careful about compiling taglib and stuff with mingw and the rest with VC++, or things might mysteriously crash. Also I heard a horror story about allocating memory in plugin code but deallocating it in the main app when the plugin was compiled against a different stdlib than the main app. This is all par for the course, and the experienced C++ developers I asked for help had no trouble making it work. <strong>Size of installable pacakge: 2.5MB</strong>
</p>
<h3>Erlang</h3>
<p>
Compiling, and building/loading plugins in the Erlang codebase is straightforward on all platforms, as is often the way with VMs. I was against shipping the Erlang VM originally because I figured it would be a lot of hassle and increase the download size substantially. Packaging an Erlang app for the desktop involves taking the installed VM directory structure and stripping out all the docs, source and parts of the Erlang stdlib we don&#8217;t use, then packaging it along with the compiled Playdar code. <a href="http://couchdb.apache.org/" target="cdb">CouchDB</a> does something like this too, and <a href="http://www.rabbitmq.com/" target="rabbit">RabbitMQ</a> ships the Erlang VM without stripping unneeded libs. We&#8217;ll work on packaging some more (for all platforms), but to date <a href="http://twitter.com/mxcl">Max</a> has crafted a package that contains the necessary bits of the Erlang VM, a sexy Prefpane to start/stop the daemon on OS X, and the compiled Playdar code all <strong>weighing in under 10MB.</strong>
</p>
<p>
We&#8217;ll put together a Windows installer soon that&#8217;ll probably be around the same size. A 10MB download isn&#8217;t so bad nowadays, and I expect we can optimize the packaging process some more. Linux users will get a package that depends on the erlang VM in their package manager.<br />
Seems like shipping Erlang apps to the desktop isn&#8217;t so hard after all.
</p>
<h2>tl;dr</h2>
<p>
Someone rewrote a C++ app in Erlang: 75% less lines of code for same functionality.
</p>
<p>
You should read this <a href="http://musicmachinery.com/2009/10/18/playing-with-playdar/">blog post about Playdar, by Paul Lamere</a>,  and take a look at the <a href="http://www.playdar.org/">Playdar website</a>.
</p>
<p>
<a href="http://github.com/RJ/playdar">C++ codebase (deprecated)</a><br />
<a href="http://github.com/RJ/playdar-core">Erlang codebase</a>
</p>
<p>
<strong>Playdar is the future, and the future is written in Erlang :)</strong></p>
]]></content:encoded>
			<wfw:commentRss>http://www.metabrew.com/article/rewriting-playdar-c-to-erlang-massive-savings//feed</wfw:commentRss>
		<slash:comments>14</slash:comments>
		</item>
		<item>
		<title>A Million-user Comet Application with Mochiweb, Part 3</title>
		<link>http://www.metabrew.com/article/a-million-user-comet-application-with-mochiweb-part-3/</link>
		<comments>http://www.metabrew.com/article/a-million-user-comet-application-with-mochiweb-part-3/#comments</comments>
		<pubDate>Tue, 04 Nov 2008 16:49:05 +0000</pubDate>
		<dc:creator>RJ</dc:creator>
				<category><![CDATA[programming]]></category>
		<category><![CDATA[c]]></category>
		<category><![CDATA[cnode]]></category>
		<category><![CDATA[comet]]></category>
		<category><![CDATA[erlang]]></category>
		<category><![CDATA[libevent]]></category>
		<category><![CDATA[mochiweb]]></category>

		<guid isPermaLink="false">http://www.metabrew.com/?p=152</guid>
		<description><![CDATA[Part 1 and Part 2 in this series showed how to build a comet application using mochiweb, and how to route messages to connected users. We managed to squeeze application memory down to 8KB per connection. We did ye olde c10k test, and observed what happened with 10,000 connected users. We made graphs. It was [...]]]></description>
			<content:encoded><![CDATA[<p><a href="http://www.metabrew.com/article/a-million-user-comet-application-with-mochiweb-part-1/">Part 1</a> and <a href="http://www.metabrew.com/article/a-million-user-comet-application-with-mochiweb-part-2/">Part 2</a> in this series showed how to build a comet application using mochiweb, and how to route messages to connected users. We managed to squeeze application memory down to 8KB per connection. We did ye olde c10k test, and observed what happened with 10,000 connected users. We made graphs. It was fun, but now it&#8217;s time to make good on the claims made in the title, and turn it up to 1 million connections.</p>
<p>This post covers the following:</p>
<ul>
<li>Add a pubsub-like subscription database using Mnesia</li>
<li>Generate a realistic friends dataset for a million users</li>
<li>Tune mnesia and bulk load in our friends data</li>
<li>Opening a million connections from one machine</li>
<li>Benchmark with 1 Million connected users</li>
<li>Libevent + C for connection handling</li>
<li>Final thoughts</li>
</ul>
<p>One of the challenging parts of this test was actually being able to open 1M connections from a single test machine. Writing a server to accept 1M connections is easier than actually creating 1M connections to test it with, so a fair amount of this article is about the techniques used to open 1M connections from a single machine.</p>
<h2>Getting our pubsub on</h2>
<p>In <a href="http://www.metabrew.com/article/a-million-user-comet-application-with-mochiweb-part-2/">Part 2</a> we used the router to send messages to specific users. This is fine for a chat/IM system, but that there are sexier things we could do instead. Before we launch into a large-scale test, let&#8217;s add one more module &#8211; a subscription database. We want the application store who your friends are, so it can push you all events generated by people on your friends list.</p>
<p>My intention is to use this for Last.fm so I can get a realtime feed of songs <a href="http://www.last.fm/user/RJ/friends">my friends</a> are currently listening to.  It could equally apply to other events generated on social networks. Flickr photo uploads, Facebook newsfeed items, Twitter messages etc. FriendFeed even have a realtime API in beta, so this kind of thing is definitely topical. (Although I&#8217;ve not heard of anyone except Facebook using Erlang for this kind of thing).</p>
<h2>Implementing the subscription-manager</h2>
<p>We&#8217;re implementing a general subscription manager, but we&#8217;ll be subscribing people to everyone on their friends list automatically &#8211; so you could also think of this as a friends database for now.</p>
<p>The subsmanager API:</p>
<ul>
<li>add_subscriptions([{Subscriber, Subscribee},...])</li>
<li>remove_subscriptions([{Subscriber, Subscribee},...])</li>
<li>get_subscribers(User)</li>
</ul>
<p>subsmanager.erl</p>
<div class="dean_ch" style="white-space: wrap;">
<ol>
<li class="li1">
<div class="de1">-<span class="kw2">module</span><span class="br0">&#40;</span>subsmanager<span class="br0">&#41;</span>.</div>
</li>
<li class="li1">
<div class="de1">-<span class="kw2">behaviour</span><span class="br0">&#40;</span>gen_server<span class="br0">&#41;</span>.</div>
</li>
<li class="li1">
<div class="de1">-include<span class="br0">&#40;</span><span class="st0">&quot;/usr/local/lib/erlang/lib/stdlib-1.15.4/include/qlc.hrl&quot;</span><span class="br0">&#41;</span>.</div>
</li>
<li class="li1">
<div class="de1">-<span class="kw2">export</span><span class="br0">&#40;</span><span class="br0">&#91;</span>init/<span class="nu0">1</span>, handle_call/<span class="nu0">3</span>, handle_cast/<span class="nu0">2</span>, handle_info/<span class="nu0">2</span>, terminate/<span class="nu0">2</span>, code_change/<span class="nu0">3</span><span class="br0">&#93;</span><span class="br0">&#41;</span>.</div>
</li>
<li class="li2">
<div class="de2">-<span class="kw2">export</span><span class="br0">&#40;</span><span class="br0">&#91;</span>add_subscriptions/<span class="nu0">1</span>,</div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;remove_subscriptions/<span class="nu0">1</span>,</div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;get_subscribers/<span class="nu0">1</span>,</div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;first_run/<span class="nu0">0</span>,</div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;stop/<span class="nu0">0</span>,</div>
</li>
<li class="li2">
<div class="de2">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw3">start_link</span>/<span class="nu0">0</span><span class="br0">&#93;</span><span class="br0">&#41;</span>.</div>
</li>
<li class="li1">
<div class="de1">-record<span class="br0">&#40;</span>subscription, <span class="br0">&#123;</span>subscriber, subscribee<span class="br0">&#125;</span><span class="br0">&#41;</span>.</div>
</li>
<li class="li1">
<div class="de1">-record<span class="br0">&#40;</span>state, <span class="br0">&#123;</span><span class="br0">&#125;</span><span class="br0">&#41;</span>. <span class="co1">% state is all in mnesia</span></div>
</li>
<li class="li1">
<div class="de1">-define<span class="br0">&#40;</span><span class="re0">SERVER</span>, global:<span class="me2">whereis_name</span><span class="br0">&#40;</span>?<span class="re0">MODULE</span><span class="br0">&#41;</span><span class="br0">&#41;</span>.</div>
</li>
<li class="li1">
<div class="de1">&nbsp;</div>
</li>
<li class="li2">
<div class="de2"><span class="kw3">start_link</span><span class="br0">&#40;</span><span class="br0">&#41;</span> -&gt;</div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; <span class="me1">gen_server</span>:<span class="kw3">start_link</span><span class="br0">&#40;</span><span class="br0">&#123;</span>global, ?<span class="re0">MODULE</span><span class="br0">&#125;</span>, ?<span class="re0">MODULE</span>, <span class="br0">&#91;</span><span class="br0">&#93;</span>, <span class="br0">&#91;</span><span class="br0">&#93;</span><span class="br0">&#41;</span>.</div>
</li>
<li class="li1">
<div class="de1">&nbsp;</div>
</li>
<li class="li1">
<div class="de1">stop<span class="br0">&#40;</span><span class="br0">&#41;</span> -&gt;</div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; <span class="me1">gen_server</span>:<span class="kw3">call</span><span class="br0">&#40;</span>?<span class="re0">SERVER</span>, <span class="br0">&#123;</span>stop<span class="br0">&#125;</span><span class="br0">&#41;</span>.</div>
</li>
<li class="li2">
<div class="de2">&nbsp;</div>
</li>
<li class="li1">
<div class="de1">add_subscriptions<span class="br0">&#40;</span><span class="re0">SubsList</span><span class="br0">&#41;</span> -&gt;</div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; <span class="me1">gen_server</span>:<span class="kw3">call</span><span class="br0">&#40;</span>?<span class="re0">SERVER</span>, <span class="br0">&#123;</span>add_subscriptions, <span class="re0">SubsList</span><span class="br0">&#125;</span>, infinity<span class="br0">&#41;</span>.</div>
</li>
<li class="li1">
<div class="de1">&nbsp;</div>
</li>
<li class="li1">
<div class="de1">remove_subscriptions<span class="br0">&#40;</span><span class="re0">SubsList</span><span class="br0">&#41;</span> -&gt;</div>
</li>
<li class="li2">
<div class="de2">&nbsp; &nbsp; <span class="me1">gen_server</span>:<span class="kw3">call</span><span class="br0">&#40;</span>?<span class="re0">SERVER</span>, <span class="br0">&#123;</span>remove_subscriptions, <span class="re0">SubsList</span><span class="br0">&#125;</span>, infinity<span class="br0">&#41;</span>.</div>
</li>
<li class="li1">
<div class="de1">&nbsp;</div>
</li>
<li class="li1">
<div class="de1">get_subscribers<span class="br0">&#40;</span><span class="re0">User</span><span class="br0">&#41;</span> -&gt;</div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; <span class="me1">gen_server</span>:<span class="kw3">call</span><span class="br0">&#40;</span>?<span class="re0">SERVER</span>, <span class="br0">&#123;</span>get_subscribers, <span class="re0">User</span><span class="br0">&#125;</span><span class="br0">&#41;</span>.</div>
</li>
<li class="li1">
<div class="de1">&nbsp;</div>
</li>
<li class="li2">
<div class="de2"><span class="co1">%%</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp;</div>
</li>
<li class="li1">
<div class="de1">init<span class="br0">&#40;</span><span class="br0">&#91;</span><span class="br0">&#93;</span><span class="br0">&#41;</span> -&gt;</div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; <span class="me1">ok</span> = mnesia:<span class="me2">start</span><span class="br0">&#40;</span><span class="br0">&#41;</span>,</div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; io:<span class="kw3">format</span><span class="br0">&#40;</span><span class="st0">&quot;Waiting on mnesia tables..<span class="es0">\n</span>&quot;</span>,<span class="br0">&#91;</span><span class="br0">&#93;</span><span class="br0">&#41;</span>,</div>
</li>
<li class="li2">
<div class="de2">&nbsp; &nbsp; mnesia:<span class="me2">wait_for_tables</span><span class="br0">&#40;</span><span class="br0">&#91;</span>subscription<span class="br0">&#93;</span>, <span class="nu0">30000</span><span class="br0">&#41;</span>,</div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; <span class="re0">Info</span> = mnesia:<span class="me2">table_info</span><span class="br0">&#40;</span>subscription, all<span class="br0">&#41;</span>,</div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; io:<span class="kw3">format</span><span class="br0">&#40;</span><span class="st0">&quot;OK. Subscription table info: <span class="es0">\n</span>~w<span class="es0">\n</span><span class="es0">\n</span>&quot;</span>,<span class="br0">&#91;</span><span class="re0">Info</span><span class="br0">&#93;</span><span class="br0">&#41;</span>,</div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; <span class="br0">&#123;</span>ok, #state<span class="br0">&#123;</span><span class="br0">&#125;</span><span class="br0">&#125;</span>.</div>
</li>
<li class="li1">
<div class="de1">&nbsp;</div>
</li>
<li class="li2">
<div class="de2">handle_call<span class="br0">&#40;</span><span class="br0">&#123;</span>stop<span class="br0">&#125;</span>, _<span class="re0">From</span>, <span class="re0">State</span><span class="br0">&#41;</span> -&gt;</div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; <span class="br0">&#123;</span>stop, stop, <span class="re0">State</span><span class="br0">&#125;</span>;</div>
</li>
<li class="li1">
<div class="de1">&nbsp;</div>
</li>
<li class="li1">
<div class="de1">handle_call<span class="br0">&#40;</span><span class="br0">&#123;</span>add_subscriptions, <span class="re0">SubsList</span><span class="br0">&#125;</span>, _<span class="re0">From</span>, <span class="re0">State</span><span class="br0">&#41;</span> -&gt;</div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; <span class="co1">% Transactionally is slower:</span></div>
</li>
<li class="li2">
<div class="de2">&nbsp; &nbsp; <span class="co1">% F = fun() -&gt;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; <span class="co1">% &nbsp; &nbsp; &nbsp; &nbsp; [ ok = mnesia:write(S) || S &lt;- SubsList ]</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; <span class="co1">% &nbsp; &nbsp; end,</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; <span class="co1">% mnesia:transaction(F),</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; <span class="br0">&#91;</span> mnesia:<span class="me2">dirty_write</span><span class="br0">&#40;</span><span class="re0">S</span><span class="br0">&#41;</span> || <span class="re0">S</span> &lt;- <span class="re0">SubsList</span> <span class="br0">&#93;</span>,</div>
</li>
<li class="li2">
<div class="de2">&nbsp; &nbsp; <span class="br0">&#123;</span>reply, ok, <span class="re0">State</span><span class="br0">&#125;</span>;</div>
</li>
<li class="li1">
<div class="de1">&nbsp;</div>
</li>
<li class="li1">
<div class="de1">handle_call<span class="br0">&#40;</span><span class="br0">&#123;</span>remove_subscriptions, <span class="re0">SubsList</span><span class="br0">&#125;</span>, _<span class="re0">From</span>, <span class="re0">State</span><span class="br0">&#41;</span> -&gt;</div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; <span class="re0">F</span> = fun<span class="br0">&#40;</span><span class="br0">&#41;</span> -&gt;</div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; <span class="br0">&#91;</span> ok = mnesia:<span class="me2">delete_object</span><span class="br0">&#40;</span><span class="re0">S</span><span class="br0">&#41;</span> || <span class="re0">S</span> &lt;- <span class="re0">SubsList</span> <span class="br0">&#93;</span></div>
</li>
<li class="li2">
<div class="de2">&nbsp; &nbsp; <span class="kw1">end</span>,</div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; mnesia:<span class="me2">transaction</span><span class="br0">&#40;</span><span class="re0">F</span><span class="br0">&#41;</span>,</div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; <span class="br0">&#123;</span>reply, ok, <span class="re0">State</span><span class="br0">&#125;</span>;</div>
</li>
<li class="li1">
<div class="de1">&nbsp;</div>
</li>
<li class="li1">
<div class="de1">handle_call<span class="br0">&#40;</span><span class="br0">&#123;</span>get_subscribers, <span class="re0">User</span><span class="br0">&#125;</span>, <span class="re0">From</span>, <span class="re0">State</span><span class="br0">&#41;</span> -&gt;</div>
</li>
<li class="li2">
<div class="de2">&nbsp; &nbsp; <span class="re0">F</span> = fun<span class="br0">&#40;</span><span class="br0">&#41;</span> -&gt;</div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; <span class="re0">Subs</span> = mnesia:<span class="me2">dirty_match_object</span><span class="br0">&#40;</span>#subscription<span class="br0">&#123;</span>subscriber=<span class="st0">&#8216;_&#8217;</span>, subscribee=<span class="re0">User</span><span class="br0">&#125;</span><span class="br0">&#41;</span>,</div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; <span class="re0">Users</span> = <span class="br0">&#91;</span><span class="re0">Dude</span> || #subscription<span class="br0">&#123;</span>subscriber=<span class="re0">Dude</span>, subscribee=_<span class="br0">&#125;</span> &lt;- <span class="re0">Subs</span><span class="br0">&#93;</span>,</div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; gen_server:<span class="me2">reply</span><span class="br0">&#40;</span><span class="re0">From</span>, <span class="re0">Users</span><span class="br0">&#41;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; <span class="kw1">end</span>,</div>
</li>
<li class="li2">
<div class="de2">&nbsp; &nbsp; spawn<span class="br0">&#40;</span><span class="re0">F</span><span class="br0">&#41;</span>,</div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; <span class="br0">&#123;</span>noreply, <span class="re0">State</span><span class="br0">&#125;</span>.</div>
</li>
<li class="li1">
<div class="de1">&nbsp;</div>
</li>
<li class="li1">
<div class="de1">handle_cast<span class="br0">&#40;</span>_<span class="re0">Msg</span>, <span class="re0">State</span><span class="br0">&#41;</span> -&gt; <span class="br0">&#123;</span>noreply, <span class="re0">State</span><span class="br0">&#125;</span>.</div>
</li>
<li class="li1">
<div class="de1">handle_info<span class="br0">&#40;</span>_<span class="re0">Msg</span>, <span class="re0">State</span><span class="br0">&#41;</span> -&gt; <span class="br0">&#123;</span>noreply, <span class="re0">State</span><span class="br0">&#125;</span>.</div>
</li>
<li class="li2">
<div class="de2">&nbsp;</div>
</li>
<li class="li1">
<div class="de1">terminate<span class="br0">&#40;</span>_<span class="re0">Reason</span>, _<span class="re0">State</span><span class="br0">&#41;</span> -&gt;</div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; <span class="me1">mnesia</span>:<span class="me2">stop</span><span class="br0">&#40;</span><span class="br0">&#41;</span>,</div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; ok.</div>
</li>
<li class="li1">
<div class="de1">&nbsp;</div>
</li>
<li class="li2">
<div class="de2">code_change<span class="br0">&#40;</span>_<span class="re0">OldVersion</span>, <span class="re0">State</span>, _<span class="re0">Extra</span><span class="br0">&#41;</span> -&gt;</div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; <span class="me1">io</span>:<span class="kw3">format</span><span class="br0">&#40;</span><span class="st0">&quot;Reloading code for ?MODULE<span class="es0">\n</span>&quot;</span>,<span class="br0">&#91;</span><span class="br0">&#93;</span><span class="br0">&#41;</span>,</div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; <span class="br0">&#123;</span>ok, <span class="re0">State</span><span class="br0">&#125;</span>.</div>
</li>
<li class="li1">
<div class="de1">&nbsp;</div>
</li>
<li class="li1">
<div class="de1"><span class="co1">%%</span></div>
</li>
<li class="li2">
<div class="de2">&nbsp;</div>
</li>
<li class="li1">
<div class="de1">first_run<span class="br0">&#40;</span><span class="br0">&#41;</span> -&gt;</div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; <span class="me1">mnesia</span>:<span class="me2">create_schema</span><span class="br0">&#40;</span><span class="br0">&#91;</span>node<span class="br0">&#40;</span><span class="br0">&#41;</span><span class="br0">&#93;</span><span class="br0">&#41;</span>,</div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; ok = mnesia:<span class="me2">start</span><span class="br0">&#40;</span><span class="br0">&#41;</span>,</div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; <span class="re0">Ret</span> = mnesia:<span class="me2">create_table</span><span class="br0">&#40;</span>subscription,</div>
</li>
<li class="li2">
<div class="de2">&nbsp; &nbsp; <span class="br0">&#91;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; &nbsp;<span class="br0">&#123;</span>disc_copies, <span class="br0">&#91;</span>node<span class="br0">&#40;</span><span class="br0">&#41;</span><span class="br0">&#93;</span><span class="br0">&#125;</span>,</div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; &nbsp;<span class="br0">&#123;</span>attributes, record_info<span class="br0">&#40;</span>fields, subscription<span class="br0">&#41;</span><span class="br0">&#125;</span>,</div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; &nbsp;<span class="br0">&#123;</span>index, <span class="br0">&#91;</span>subscribee<span class="br0">&#93;</span><span class="br0">&#125;</span>, <span class="co1">%index subscribee too</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; &nbsp;<span class="br0">&#123;</span>type, bag<span class="br0">&#125;</span></div>
</li>
<li class="li2">
<div class="de2">&nbsp; &nbsp; <span class="br0">&#93;</span><span class="br0">&#41;</span>,</div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; <span class="re0">Ret</span>.</div>
</li>
</ol>
</div>
<p><br/><br />
Noteworthy points:</p>
<ul>
<li>I&#8217;ve included qlc.hrl, needed for mnesia queries using list comprehension, using an absolute path. That can&#8217;t be best practice, it wasn&#8217;t finding it otherwise though.</li>
<li><code>get_subscribers</code> spawns another process and delegates the job of replying to that process, using <code>gen_server:reply</code>. This means the gen_server loop won&#8217;t block on that call if we throw lots of lookups at it and mnesia slows down.</li>
<li><code>rr(”subsmanager.erl”).</code> in the example below allows you to use record definitions in the erl shell. Putting your record definitions into a <code>records.hrl</code> file and including that in your modules is considered better style. I inlined it for brevity.</li>
</ul>
<p>Now to test it. <code>first_run()</code> creates the mnesia schema, so it&#8217;s important to run that first. Another potential gotcha with mnesia is that (by default) the database can only be accessed by the node that created it, so give the erl shell a name, and stick with it.</p>
<p><code>$ mkdir /var/mnesia<br />
$ erl -boot start_sasl -mnesia dir '"/var/mnesia_data"' -sname subsman<br />
(subsman@localhost)1&gt; c(subsmanager).<br />
{ok,subsmanager}<br />
(subsman@localhost)2&gt; subsmanager:first_run().<br />
...<br />
{atomic,ok}<br />
(subsman@localhost)3&gt; subsmanager:start_link().<br />
Waiting on mnesia tables..<br />
OK. Subscription table info:<br />
[{access_mode,read_write},{active_replicas,[subsman@localhost]},{arity,3},{attributes,[subscriber,subscribee]},{checkpoints,[]},{commit_work,[{index,bag,[{3,{ram,57378}}]}]},{cookie,{{1224,800064,900003},subsman@localhost}},{cstruct,{cstruct,subscription,bag,[],[subsman@localhost],[],0,read_write,[3],[],false,subscription,[subscriber,subscribee],[],[],{{1224,863164,904753},subsman@localhost},{{2,0},[]}}},{disc_copies,[subsman@localhost]},{disc_only_copies,[]},{frag_properties,[]},{index,[3]},{load_by_force,false},{load_node,subsman@localhost},{load_order,0},{load_reason,{dumper,create_table}},{local_content,false},{master_nodes,[]},{memory,288},{ram_copies,[]},{record_name,subscription},{record_validation,{subscription,3,bag}},{type,bag},{size,0},{snmp,[]},{storage_type,disc_copies},{subscribers,[]},{user_properties,[]},{version,{{2,0},[]}},{where_to_commit,[{subsman@localhost,disc_copies}]},{where_to_read,subsman@localhost},{where_to_write,[subsman@localhost]},{wild_pattern,{subscription,'_','_'}},{{index,3},57378}]</code><br />
<code><br />
{ok,&lt;0.105.0&gt;}<br />
(subsman@localhost)4&gt; rr("subsmanager.erl").<br />
[state,subscription]<br />
(subsman@localhost)5&gt; subsmanager:add_subscriptions([ #subscription{subscriber=alice, subscribee=rj} ]).<br />
ok<br />
(subsman@localhost)6&gt; subsmanager:add_subscriptions([ #subscription{subscriber=bob, subscribee=rj} ]).<br />
ok<br />
(subsman@localhost)7&gt; subsmanager:get_subscribers(rj).<br />
[bob,alice]<br />
(subsman@localhost)8&gt; subsmanager:remove_subscriptions([ #subscription{subscriber=bob, subscribee=rj} ]).<br />
ok<br />
(subsman@localhost)8&gt; subsmanager:get_subscribers(rj).<br />
[alice]<br />
(subsman@localhost)10&gt; subsmanager:get_subscribers(charlie).<br />
[]</code></p>
<p>We&#8217;ll use integer Ids to represent users for the benchmark &#8211; but for this test I used atoms (rj, alice, bob) and assumed that alice and bob are both on rj&#8217;s friends list. It&#8217;s nice that mnesia (and ets/dets) doesn&#8217;t care what values you use &#8211; any Erlang term is valid. This means it&#8217;s a simple upgrade to support multiple types of resource. You could start using <code>{user, 123}</code> or <code>{photo, 789}</code> to represent different things people might subscribe to, without changing anything in the subsmanager module.</p>
<h2>Modifying the router to use subscriptions</h2>
<p>Instead of addressing messages to specific users, ie <code>router:send(123, "Hello user 123")</code>, we&#8217;ll mark messages with a subject &#8211; that is, the person who generated the message (who played the song, who uploaded the photo etc) &#8211; and have the router deliver the message to every user who has subscribed to the subject user. In other words, the API will work like this: <code>router:send(123, "Hello everyone subscribed to user 123")</code></p>
<p>Updated router.erl:</p>
<div class="dean_ch" style="white-space: wrap;">
<ol>
<li class="li1">
<div class="de1">-<span class="kw2">module</span><span class="br0">&#40;</span>router<span class="br0">&#41;</span>.</div>
</li>
<li class="li1">
<div class="de1">-<span class="kw2">behaviour</span><span class="br0">&#40;</span>gen_server<span class="br0">&#41;</span>.</div>
</li>
<li class="li1">
<div class="de1">&nbsp;</div>
</li>
<li class="li1">
<div class="de1">-<span class="kw2">export</span><span class="br0">&#40;</span><span class="br0">&#91;</span><span class="kw3">start_link</span>/<span class="nu0">0</span><span class="br0">&#93;</span><span class="br0">&#41;</span>.</div>
</li>
<li class="li2">
<div class="de2">-<span class="kw2">export</span><span class="br0">&#40;</span><span class="br0">&#91;</span>init/<span class="nu0">1</span>, handle_call/<span class="nu0">3</span>, handle_cast/<span class="nu0">2</span>, handle_info/<span class="nu0">2</span>,</div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; &nbsp;terminate/<span class="nu0">2</span>, code_change/<span class="nu0">3</span><span class="br0">&#93;</span><span class="br0">&#41;</span>.</div>
</li>
<li class="li1">
<div class="de1">&nbsp;</div>
</li>
<li class="li1">
<div class="de1">-<span class="kw2">export</span><span class="br0">&#40;</span><span class="br0">&#91;</span>send/<span class="nu0">2</span>, login/<span class="nu0">2</span>, logout/<span class="nu0">1</span><span class="br0">&#93;</span><span class="br0">&#41;</span>.</div>
</li>
<li class="li1">
<div class="de1">&nbsp;</div>
</li>
<li class="li2">
<div class="de2">-define<span class="br0">&#40;</span><span class="re0">SERVER</span>, global:<span class="me2">whereis_name</span><span class="br0">&#40;</span>?<span class="re0">MODULE</span><span class="br0">&#41;</span><span class="br0">&#41;</span>.</div>
</li>
<li class="li1">
<div class="de1">&nbsp;</div>
</li>
<li class="li1">
<div class="de1"><span class="co1">% will hold bidirectional mapping between id &lt;&#8211;&gt; pid</span></div>
</li>
<li class="li1">
<div class="de1">-record<span class="br0">&#40;</span>state, <span class="br0">&#123;</span>pid2id, id2pid<span class="br0">&#125;</span><span class="br0">&#41;</span>.</div>
</li>
<li class="li1">
<div class="de1">&nbsp;</div>
</li>
<li class="li2">
<div class="de2"><span class="kw3">start_link</span><span class="br0">&#40;</span><span class="br0">&#41;</span> -&gt;</div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; <span class="me1">gen_server</span>:<span class="kw3">start_link</span><span class="br0">&#40;</span><span class="br0">&#123;</span>global, ?<span class="re0">MODULE</span><span class="br0">&#125;</span>, ?<span class="re0">MODULE</span>, <span class="br0">&#91;</span><span class="br0">&#93;</span>, <span class="br0">&#91;</span><span class="br0">&#93;</span><span class="br0">&#41;</span>.</div>
</li>
<li class="li1">
<div class="de1">&nbsp;</div>
</li>
<li class="li1">
<div class="de1"><span class="co1">% sends Msg to anyone subscribed to Id</span></div>
</li>
<li class="li1">
<div class="de1">send<span class="br0">&#40;</span><span class="re0">Id</span>, <span class="re0">Msg</span><span class="br0">&#41;</span> -&gt;</div>
</li>
<li class="li2">
<div class="de2">&nbsp; &nbsp; <span class="me1">gen_server</span>:<span class="kw3">call</span><span class="br0">&#40;</span>?<span class="re0">SERVER</span>, <span class="br0">&#123;</span>send, <span class="re0">Id</span>, <span class="re0">Msg</span><span class="br0">&#125;</span><span class="br0">&#41;</span>.</div>
</li>
<li class="li1">
<div class="de1">&nbsp;</div>
</li>
<li class="li1">
<div class="de1">login<span class="br0">&#40;</span><span class="re0">Id</span>, <span class="re0">Pid</span><span class="br0">&#41;</span> when is_pid<span class="br0">&#40;</span><span class="re0">Pid</span><span class="br0">&#41;</span> -&gt;</div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; <span class="me1">gen_server</span>:<span class="kw3">call</span><span class="br0">&#40;</span>?<span class="re0">SERVER</span>, <span class="br0">&#123;</span>login, <span class="re0">Id</span>, <span class="re0">Pid</span><span class="br0">&#125;</span><span class="br0">&#41;</span>.</div>
</li>
<li class="li1">
<div class="de1">&nbsp;</div>
</li>
<li class="li2">
<div class="de2">logout<span class="br0">&#40;</span><span class="re0">Pid</span><span class="br0">&#41;</span> when is_pid<span class="br0">&#40;</span><span class="re0">Pid</span><span class="br0">&#41;</span> -&gt;</div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; <span class="me1">gen_server</span>:<span class="kw3">call</span><span class="br0">&#40;</span>?<span class="re0">SERVER</span>, <span class="br0">&#123;</span>logout, <span class="re0">Pid</span><span class="br0">&#125;</span><span class="br0">&#41;</span>.</div>
</li>
<li class="li1">
<div class="de1">&nbsp;</div>
</li>
<li class="li1">
<div class="de1"><span class="co1">%%</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp;</div>
</li>
<li class="li2">
<div class="de2">init<span class="br0">&#40;</span><span class="br0">&#91;</span><span class="br0">&#93;</span><span class="br0">&#41;</span> -&gt;</div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; <span class="co1">% set this so we can catch death of logged in pids:</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; process_flag<span class="br0">&#40;</span>trap_exit, true<span class="br0">&#41;</span>,</div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; <span class="co1">% use ets for routing tables</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; <span class="br0">&#123;</span>ok, #state<span class="br0">&#123;</span></div>
</li>
<li class="li2">
<div class="de2">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; pid2id = ets:<span class="me2">new</span><span class="br0">&#40;</span>?<span class="re0">MODULE</span>, <span class="br0">&#91;</span>bag<span class="br0">&#93;</span><span class="br0">&#41;</span>,</div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; id2pid = ets:<span class="me2">new</span><span class="br0">&#40;</span>?<span class="re0">MODULE</span>, <span class="br0">&#91;</span>bag<span class="br0">&#93;</span><span class="br0">&#41;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="br0">&#125;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; <span class="br0">&#125;</span>.</div>
</li>
<li class="li1">
<div class="de1">&nbsp;</div>
</li>
<li class="li2">
<div class="de2">handle_call<span class="br0">&#40;</span><span class="br0">&#123;</span>login, <span class="re0">Id</span>, <span class="re0">Pid</span><span class="br0">&#125;</span>, _<span class="re0">From</span>, <span class="re0">State</span><span class="br0">&#41;</span> when is_pid<span class="br0">&#40;</span><span class="re0">Pid</span><span class="br0">&#41;</span> -&gt;</div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; <span class="me1">ets</span>:<span class="me2">insert</span><span class="br0">&#40;</span><span class="re0">State</span>#state.pid2id, <span class="br0">&#123;</span><span class="re0">Pid</span>, <span class="re0">Id</span><span class="br0">&#125;</span><span class="br0">&#41;</span>,</div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; ets:<span class="me2">insert</span><span class="br0">&#40;</span><span class="re0">State</span>#state.id2pid, <span class="br0">&#123;</span><span class="re0">Id</span>, <span class="re0">Pid</span><span class="br0">&#125;</span><span class="br0">&#41;</span>,</div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; link<span class="br0">&#40;</span><span class="re0">Pid</span><span class="br0">&#41;</span>, <span class="co1">% tell us if they exit, so we can log them out</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; <span class="co1">%io:format(&quot;~w logged in as ~w\n&quot;,[Pid, Id]),</span></div>
</li>
<li class="li2">
<div class="de2">&nbsp; &nbsp; <span class="br0">&#123;</span>reply, ok, <span class="re0">State</span><span class="br0">&#125;</span>;</div>
</li>
<li class="li1">
<div class="de1">&nbsp;</div>
</li>
<li class="li1">
<div class="de1">handle_call<span class="br0">&#40;</span><span class="br0">&#123;</span>logout, <span class="re0">Pid</span><span class="br0">&#125;</span>, _<span class="re0">From</span>, <span class="re0">State</span><span class="br0">&#41;</span> when is_pid<span class="br0">&#40;</span><span class="re0">Pid</span><span class="br0">&#41;</span> -&gt;</div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; <span class="me1">unlink</span><span class="br0">&#40;</span><span class="re0">Pid</span><span class="br0">&#41;</span>,</div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; <span class="re0">PidRows</span> = ets:<span class="me2">lookup</span><span class="br0">&#40;</span><span class="re0">State</span>#state.pid2id, <span class="re0">Pid</span><span class="br0">&#41;</span>,</div>
</li>
<li class="li2">
<div class="de2">&nbsp; &nbsp; <span class="kw1">case</span> <span class="re0">PidRows</span> <span class="kw1">of</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; <span class="br0">&#91;</span><span class="br0">&#93;</span> -&gt;</div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="me1">ok</span>;</div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; _ -&gt;</div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="re0">IdRows</span> = <span class="br0">&#91;</span> <span class="br0">&#123;</span><span class="re0">I</span>,<span class="re0">P</span><span class="br0">&#125;</span> || <span class="br0">&#123;</span><span class="re0">P</span>,<span class="re0">I</span><span class="br0">&#125;</span> &lt;- <span class="re0">PidRows</span> <span class="br0">&#93;</span>, <span class="co1">% invert tuples</span></div>
</li>
<li class="li2">
<div class="de2">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; ets:<span class="me2">delete</span><span class="br0">&#40;</span><span class="re0">State</span>#state.pid2id, <span class="re0">Pid</span><span class="br0">&#41;</span>, &nbsp; <span class="co1">% delete all pid-&gt;id entries</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="br0">&#91;</span> ets:<span class="me2">delete_object</span><span class="br0">&#40;</span><span class="re0">State</span>#state.id2pid, <span class="re0">Obj</span><span class="br0">&#41;</span> || <span class="re0">Obj</span> &lt;- <span class="re0">IdRows</span> <span class="br0">&#93;</span> <span class="co1">% and all id-&gt;pid</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; <span class="kw1">end</span>,</div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; <span class="co1">%io:format(&quot;pid ~w logged out\n&quot;,[Pid]),</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; <span class="br0">&#123;</span>reply, ok, <span class="re0">State</span><span class="br0">&#125;</span>;</div>
</li>
<li class="li2">
<div class="de2">&nbsp;</div>
</li>
<li class="li1">
<div class="de1">handle_call<span class="br0">&#40;</span><span class="br0">&#123;</span>send, <span class="re0">Id</span>, <span class="re0">Msg</span><span class="br0">&#125;</span>, <span class="re0">From</span>, <span class="re0">State</span><span class="br0">&#41;</span> -&gt;</div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; <span class="re0">F</span> = fun<span class="br0">&#40;</span><span class="br0">&#41;</span> -&gt;</div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">% get users who are subscribed to Id:</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; <span class="re0">Users</span> = subsmanager:<span class="me2">get_subscribers</span><span class="br0">&#40;</span><span class="re0">Id</span><span class="br0">&#41;</span>,</div>
</li>
<li class="li2">
<div class="de2">&nbsp; &nbsp; &nbsp; &nbsp; io:<span class="kw3">format</span><span class="br0">&#40;</span><span class="st0">&quot;Subscribers of ~w = ~w<span class="es0">\n</span>&quot;</span>,<span class="br0">&#91;</span><span class="re0">Id</span>, <span class="re0">Users</span><span class="br0">&#93;</span><span class="br0">&#41;</span>,</div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">% get pids of anyone logged in from Users list:</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; <span class="re0">Pids0</span> = lists:<span class="me2">map</span><span class="br0">&#40;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; fun<span class="br0">&#40;</span><span class="re0">U</span><span class="br0">&#41;</span>-&gt;</div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="br0">&#91;</span> <span class="re0">P</span> || <span class="br0">&#123;</span> _<span class="re0">I</span>, <span class="re0">P</span> <span class="br0">&#125;</span> &lt;- ets:<span class="me2">lookup</span><span class="br0">&#40;</span><span class="re0">State</span>#state.id2pid, <span class="re0">U</span><span class="br0">&#41;</span> <span class="br0">&#93;</span></div>
</li>
<li class="li2">
<div class="de2">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">end</span>,</div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="br0">&#91;</span> <span class="re0">Id</span> | <span class="re0">Users</span> <span class="br0">&#93;</span> <span class="co1">% we are always subscribed to ourselves</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; <span class="br0">&#41;</span>,</div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; <span class="re0">Pids</span> = lists:<span class="me2">flatten</span><span class="br0">&#40;</span><span class="re0">Pids0</span><span class="br0">&#41;</span>,</div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; io:<span class="kw3">format</span><span class="br0">&#40;</span><span class="st0">&quot;Pids: ~w<span class="es0">\n</span>&quot;</span>, <span class="br0">&#91;</span><span class="re0">Pids</span><span class="br0">&#93;</span><span class="br0">&#41;</span>,</div>
</li>
<li class="li2">
<div class="de2">&nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">% send Msg to them all</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; <span class="re0">M</span> = <span class="br0">&#123;</span>router_msg, <span class="re0">Msg</span><span class="br0">&#125;</span>,</div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; <span class="br0">&#91;</span> <span class="re0">Pid</span> ! <span class="re0">M</span> || <span class="re0">Pid</span> &lt;- <span class="re0">Pids</span> <span class="br0">&#93;</span>,</div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">% respond with how many users saw the message</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; gen_server:<span class="me2">reply</span><span class="br0">&#40;</span><span class="re0">From</span>, <span class="br0">&#123;</span>ok, length<span class="br0">&#40;</span><span class="re0">Pids</span><span class="br0">&#41;</span><span class="br0">&#125;</span><span class="br0">&#41;</span></div>
</li>
<li class="li2">
<div class="de2">&nbsp; &nbsp; <span class="kw1">end</span>,</div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; spawn<span class="br0">&#40;</span><span class="re0">F</span><span class="br0">&#41;</span>,</div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; <span class="br0">&#123;</span>noreply, <span class="re0">State</span><span class="br0">&#125;</span>.</div>
</li>
<li class="li1">
<div class="de1">&nbsp;</div>
</li>
<li class="li1">
<div class="de1"><span class="co1">% handle death and cleanup of logged in processes</span></div>
</li>
<li class="li2">
<div class="de2">handle_info<span class="br0">&#40;</span><span class="re0">Info</span>, <span class="re0">State</span><span class="br0">&#41;</span> -&gt;</div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; <span class="kw1">case</span> <span class="re0">Info</span> <span class="kw1">of</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; <span class="br0">&#123;</span><span class="st0">&#8216;EXIT&#8217;</span>, <span class="re0">Pid</span>, _<span class="re0">Why</span><span class="br0">&#125;</span> -&gt;</div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="me1">handle_call</span><span class="br0">&#40;</span><span class="br0">&#123;</span>logout, <span class="re0">Pid</span><span class="br0">&#125;</span>, blah, <span class="re0">State</span><span class="br0">&#41;</span>;</div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; <span class="re0">Wtf</span> -&gt;</div>
</li>
<li class="li2">
<div class="de2">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="me1">io</span>:<span class="kw3">format</span><span class="br0">&#40;</span><span class="st0">&quot;Caught unhandled message: ~w<span class="es0">\n</span>&quot;</span>, <span class="br0">&#91;</span><span class="re0">Wtf</span><span class="br0">&#93;</span><span class="br0">&#41;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; <span class="kw1">end</span>,</div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; <span class="br0">&#123;</span>noreply, <span class="re0">State</span><span class="br0">&#125;</span>.</div>
</li>
<li class="li1">
<div class="de1">&nbsp;</div>
</li>
<li class="li1">
<div class="de1">handle_cast<span class="br0">&#40;</span>_<span class="re0">Msg</span>, <span class="re0">State</span><span class="br0">&#41;</span> -&gt;</div>
</li>
<li class="li2">
<div class="de2">&nbsp; &nbsp; <span class="br0">&#123;</span>noreply, <span class="re0">State</span><span class="br0">&#125;</span>.</div>
</li>
<li class="li1">
<div class="de1">terminate<span class="br0">&#40;</span>_<span class="re0">Reason</span>, _<span class="re0">State</span><span class="br0">&#41;</span> -&gt;</div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; <span class="me1">ok</span>.</div>
</li>
<li class="li1">
<div class="de1">code_change<span class="br0">&#40;</span>_<span class="re0">OldVsn</span>, <span class="re0">State</span>, _<span class="re0">Extra</span><span class="br0">&#41;</span> -&gt;</div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; <span class="br0">&#123;</span>ok, <span class="re0">State</span><span class="br0">&#125;</span>.</div>
</li>
</ol>
</div>
<p><br/></p>
<p>And here&#8217;s a quick test that doesn&#8217;t require mochiweb &#8211; I&#8217;ve used atoms instead of user ids, and omitted some output for clarity:</p>
<p><code>(subsman@localhost)1&gt; c(subsmanager), c(router), rr("subsmanager.erl").<br />
(subsman@localhost)2&gt; subsmanager:start_link().<br />
(subsman@localhost)3&gt; router:start_link().<br />
(subsman@localhost)4&gt; Subs = [#subscription{subscriber=alice, subscribee=rj}, #subscription{subscriber=bob, subscribee=rj}].<br />
[#subscription{subscriber = alice,subscribee = rj},<br />
#subscription{subscriber = bob,subscribee = rj}]<br />
(subsman@localhost)5&gt; subsmanager:add_subscriptions(Subs).<br />
ok<br />
(subsman@localhost)6&gt; router:send(rj, "RJ did something").<br />
Subscribers of rj = [bob,alice]<br />
Pids: []<br />
{ok,0}<br />
(subsman@localhost)7&gt; router:login(alice, self()).<br />
ok<br />
(subsman@localhost)8&gt; router:send(rj, "RJ did something").<br />
Subscribers of rj = [bob,alice]<br />
Pids: [&lt;0.46.0&gt;]<br />
{ok,1}<br />
(subsman@localhost)9&gt; receive {router_msg, M} -&gt; io:format("~s\n",[M]) end.<br />
RJ did something<br />
ok</code></p>
<p>This shows how alice can a receive a message when the subject is someone she is subscribed to (rj), even though the message wasn&#8217;t sent directly to alice. The output shows that the router identified possible targets as <code>[alice,bob]</code> but only delivered the message to one person, alice, because bob was not logged in.</p>
<h2>Generating a typical social-network friends dataset</h2>
<p>We could generate lots of friend relationships at random, but that&#8217;s not particularly realistic. Social networks tend to exhibit a power law distribution. Social networks usually have a few super-popular users (<a href="http://twitter.com/barackobama">some Twitter users</a> have over 100,000 followers) and plenty of people with just a handful of friends. The Last.fm friends data is typical &#8211; it fits a <a href="http://en.wikipedia.org/wiki/Barab%C3%A1si-Albert_model">Barabási–Albert graph model</a>, so that&#8217;s what I&#8217;ll use.</p>
<p>To generate the dataset I&#8217;m using the python module from the excellent <a href="http://cneurocvs.rmki.kfki.hu/igraph/">igraph library</a>:</p>
<p>fakefriends.py:</p>
<div class="dean_ch" style="white-space: wrap;">
<ol>
<li class="li1">
<div class="de1"><span class="kw1">import</span> igraph</div>
</li>
<li class="li1">
<div class="de1">g = igraph.<span class="me1">Graph</span>.<span class="me1">Barabasi</span><span class="br0">&#40;</span><span class="nu0">1000000</span>, <span class="nu0">15</span>, directed=<span class="kw2">False</span><span class="br0">&#41;</span></div>
</li>
<li class="li1">
<div class="de1"><span class="kw1">print</span> <span class="st0">&quot;Edges: &quot;</span> + <span class="kw2">str</span><span class="br0">&#40;</span>g.<span class="me1">ecount</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="br0">&#41;</span> + <span class="st0">&quot; Verticies: &quot;</span> + <span class="kw2">str</span><span class="br0">&#40;</span>g.<span class="me1">vcount</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="br0">&#41;</span></div>
</li>
<li class="li1">
<div class="de1">g.<span class="me1">write_edgelist</span><span class="br0">&#40;</span><span class="st0">&quot;fakefriends.txt&quot;</span><span class="br0">&#41;</span></div>
</li>
</ol>
</div>
<p><br/><br />
This will generate with 2 user ids per line, space separated. These are the friend relationships we&#8217;ll load into our subsmanager. User ids range from 1 to a million.</p>
<h2>Bulk loading friends data into mnesia</h2>
<p>This small module will read the fakefriends.txt file and create a list of subscription records.</p>
<p>readfriends.erl &#8211; to read the fakefriends.txt and create subscription records:</p>
<div class="dean_ch" style="white-space: wrap;">
<ol>
<li class="li1">
<div class="de1">-<span class="kw2">module</span><span class="br0">&#40;</span>readfriends<span class="br0">&#41;</span>.</div>
</li>
<li class="li1">
<div class="de1">-<span class="kw2">export</span><span class="br0">&#40;</span><span class="br0">&#91;</span>load/<span class="nu0">1</span><span class="br0">&#93;</span><span class="br0">&#41;</span>.</div>
</li>
<li class="li1">
<div class="de1">-record<span class="br0">&#40;</span>subscription, <span class="br0">&#123;</span>subscriber, subscribee<span class="br0">&#125;</span><span class="br0">&#41;</span>.</div>
</li>
<li class="li1">
<div class="de1">&nbsp;</div>
</li>
<li class="li2">
<div class="de2">load<span class="br0">&#40;</span><span class="re0">Filename</span><span class="br0">&#41;</span> -&gt;</div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; <span class="me1">for_each_line_in_file</span><span class="br0">&#40;</span><span class="re0">Filename</span>,</div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; fun<span class="br0">&#40;</span><span class="re0">Line</span>, <span class="re0">Acc</span><span class="br0">&#41;</span> -&gt;</div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="br0">&#91;</span><span class="re0">As</span>, <span class="re0">Bs</span><span class="br0">&#93;</span> = string:<span class="me2">tokens</span><span class="br0">&#40;</span>string:<span class="me2">strip</span><span class="br0">&#40;</span><span class="re0">Line</span>, right, $\n<span class="br0">&#41;</span>, <span class="st0">&quot; &quot;</span><span class="br0">&#41;</span>,</div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="br0">&#123;</span><span class="re0">A</span>, _<span class="br0">&#125;</span> = string:<span class="me2">to_integer</span><span class="br0">&#40;</span><span class="re0">As</span><span class="br0">&#41;</span>,</div>
</li>
<li class="li2">
<div class="de2">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="br0">&#123;</span><span class="re0">B</span>, _<span class="br0">&#125;</span> = string:<span class="me2">to_integer</span><span class="br0">&#40;</span><span class="re0">Bs</span><span class="br0">&#41;</span>,</div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="br0">&#91;</span> #subscription<span class="br0">&#123;</span>subscriber=<span class="re0">A</span>, subscribee=<span class="re0">B</span><span class="br0">&#125;</span> | <span class="re0">Acc</span> <span class="br0">&#93;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">end</span>, <span class="br0">&#91;</span>read<span class="br0">&#93;</span>, <span class="br0">&#91;</span><span class="br0">&#93;</span><span class="br0">&#41;</span>.</div>
</li>
<li class="li1">
<div class="de1">&nbsp;</div>
</li>
<li class="li1">
<div class="de1"><span class="co1">% via: http://www.trapexit.org/Reading_Lines_from_a_File</span></div>
</li>
<li class="li2">
<div class="de2">for_each_line_in_file<span class="br0">&#40;</span><span class="re0">Name</span>, <span class="re0">Proc</span>, <span class="re0">Mode</span>, <span class="re0">Accum0</span><span class="br0">&#41;</span> -&gt;</div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; <span class="br0">&#123;</span>ok, <span class="re0">Device</span><span class="br0">&#125;</span> = file:<span class="me2">open</span><span class="br0">&#40;</span><span class="re0">Name</span>, <span class="re0">Mode</span><span class="br0">&#41;</span>,</div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; for_each_line<span class="br0">&#40;</span><span class="re0">Device</span>, <span class="re0">Proc</span>, <span class="re0">Accum0</span><span class="br0">&#41;</span>.</div>
</li>
<li class="li1">
<div class="de1">&nbsp;</div>
</li>
<li class="li1">
<div class="de1">for_each_line<span class="br0">&#40;</span><span class="re0">Device</span>, <span class="re0">Proc</span>, <span class="re0">Accum</span><span class="br0">&#41;</span> -&gt;</div>
</li>
<li class="li2">
<div class="de2">&nbsp; &nbsp; <span class="kw1">case</span> io:<span class="me2">get_line</span><span class="br0">&#40;</span><span class="re0">Device</span>, <span class="st0">&quot;&quot;</span><span class="br0">&#41;</span> <span class="kw1">of</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; eof &nbsp;-&gt; <span class="me1">file</span>:<span class="kw3">close</span><span class="br0">&#40;</span><span class="re0">Device</span><span class="br0">&#41;</span>, <span class="re0">Accum</span>;</div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; <span class="re0">Line</span> -&gt; <span class="re0">NewAccum</span> = <span class="re0">Proc</span><span class="br0">&#40;</span><span class="re0">Line</span>, <span class="re0">Accum</span><span class="br0">&#41;</span>,</div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; for_each_line<span class="br0">&#40;</span><span class="re0">Device</span>, <span class="re0">Proc</span>, <span class="re0">NewAccum</span><span class="br0">&#41;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; <span class="kw1">end</span>.</div>
</li>
</ol>
</div>
<p><br/><br />
Now in the subsmanager shell, you can read from the text file and add the subscriptions:</p>
<p><code>$ erl -name router@minifeeds4.gs2 +K true +A 128 -setcookie secretcookie -mnesia dump_log_write_threshold 50000 -mnesia dc_dump_limit 40<br />
erl&gt; c(readfriends), c(subsmanager).<br />
erl&gt; subsmanager:first_run().<br />
erl&gt; subsmanager:start_link().<br />
erl&gt; subsmanager:add_subscriptions( readfriends:load("fakefriends.txt") ).</code></p>
<p>Note the additional mnesia parameters &#8211; these are to avoid the <strong>** WARNING ** Mnesia is overloaded</strong> messages you would (probably) otherwise see. Refer to my previous post: <a href="http://www.metabrew.com/article/on-bulk-loading-data-into-mnesia/">On bulk loading data into Mnesia</a> for alternative ways to load in lots of data. The best solution seems to be (as pointed out in the comments, thanks Jacob!) to set those options. The <a href="http://www.erlang.org/doc/apps/mnesia/">Mnesia reference manual</a> contains many other settings under Configuration Parameters, and is worth a look.</p>
<h2>Turning it up to 1 Million</h2>
<p>Creating a million tcp connections from one host is non-trivial. I&#8217;ve a feeling that people who do this regularly have small clusters dedicated to simulating lots of client connections, probably running a real tool like Tsung. Even with the tuning from <a href="http://www.metabrew.com/article/a-million-user-comet-application-with-mochiweb-part-1/">Part 1</a> to increase kernel tcp memory, increase the file descriptor ulimits and set the local port range to the maximum, we will still hit a hard limit on ephemeral ports. When making a tcp connection, the client end is allocated (or you can specify) a port from the range in <code>/proc/sys/net/ipv4/ip_local_port_range</code>. It doesn&#8217;t matter if you specify it manually, or use an ephemeral port, you&#8217;re still going to run out. In Part 1, we set the range to &#8220;1024 65535&#8243; &#8211; meaning there are 65535-1024 = 64511 unprivileged ports available. Some of them will be used by other processes, but we&#8217;ll never get over 64511 client connections, because we&#8217;ll run out of ports.</p>
<p>The local port range is assigned per-IP, so if we make our outgoing connections specifically from a range of different local IP addresses, we&#8217;ll be able to open more than 64511 outgoing connections in total.</p>
<p>So let&#8217;s bring up 17 new IP addresses, with the intention of making 62,000 connections from each &#8211; giving us a total of 1,054,000 connections. Safely over the 2^32 mark:</p>
<p><code>$ for i in `seq 1 17`; do echo sudo ifconfig eth0:$i 10.0.0.$i up ; done</code></p>
<p>If you run <code>ifconfig</code> now you should see your virtual interfaces: eth0:1, eth0:2 &#8230; eth0:17, each with a different IP address. Obviously you should chose a sensible part of whatever address space you are using.</p>
<p>All that remains now is to modify the <code>floodtest</code> tool from Part 1 to specify the local IP it should connect from&#8230; Unfortunately the <a href="http://www.erlang.org/doc/man/http.html">erlang http client</a> doesn&#8217;t let you specify the source IP. Neither does ibrowse, the alternative http client library. Damn.</p>
<p><i>&lt;crazy idea&gt;</i><br />
At this point I considered another option: bringing up 17 pairs of IPs &#8211; one on the server and one on the client &#8211; each pair in their own isolated /30 subnet. I think that if I then made the client connect to any given server IP, it would force the local address to be other half of the pair on that subnet, because only one of the local IPs would actually be able to reach the server IP. In theory, this would mean declaring the local source IP on the client machine would not be necessary (although the range of server IPs would need to be specified). I don&#8217;t know if this would really work &#8211; it sounded plausible at the time. In the end I decided it was too perverted and didn&#8217;t try it.<br />
<i>&lt;/crazy idea&gt;</i><br/></p>
<p>I also poked around in OTP&#8217;s <code>http_transport</code> code and considered adding support for specifying the local IP. It&#8217;s not really a feature you usually need in an HTTP client though, and it would certainly have been more work.</p>
<p><code>gen_tcp</code> lets you specify the source address, so I ended up writing a rather crude client using <code>gen_tcp</code> specifically for this test:</p>
<p>floodtest2.erl</p>
<div class="dean_ch" style="white-space: wrap;">
<ol>
<li class="li1">
<div class="de1">-<span class="kw2">module</span><span class="br0">&#40;</span>floodtest2<span class="br0">&#41;</span>.</div>
</li>
<li class="li1">
<div class="de1">-compile<span class="br0">&#40;</span>export_all<span class="br0">&#41;</span>.</div>
</li>
<li class="li1">
<div class="de1">-define<span class="br0">&#40;</span><span class="re0">SERVERADDR</span>, <span class="st0">&quot;10.1.2.3&quot;</span><span class="br0">&#41;</span>. <span class="co1">% where mochiweb is running</span></div>
</li>
<li class="li1">
<div class="de1">-define<span class="br0">&#40;</span><span class="re0">SERVERPORT</span>, <span class="nu0">8000</span><span class="br0">&#41;</span>.</div>
</li>
<li class="li2">
<div class="de2">&nbsp;</div>
</li>
<li class="li1">
<div class="de1"><span class="co1">% Generate the config in bash like so (chose some available address space):</span></div>
</li>
<li class="li1">
<div class="de1"><span class="co1">% EACH=62000; for i in `seq 1 17`; do echo &quot;{{10,0,0,$i}, $((($i-1)*$EACH+1)), $(($i*$EACH))}, &quot;; done</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp;</div>
</li>
<li class="li1">
<div class="de1">run<span class="br0">&#40;</span><span class="re0">Interval</span><span class="br0">&#41;</span> -&gt;</div>
</li>
<li class="li2">
<div class="de2">&nbsp; &nbsp; &nbsp; &nbsp; <span class="re0">Config</span> = <span class="br0">&#91;</span></div>
</li>
<li class="li1">
<div class="de1"><span class="br0">&#123;</span><span class="br0">&#123;</span><span class="nu0">10</span>,<span class="nu0">0</span>,<span class="nu0">0</span>,<span class="nu0">1</span><span class="br0">&#125;</span>, <span class="nu0">1</span>, <span class="nu0">62000</span><span class="br0">&#125;</span>,</div>
</li>
<li class="li1">
<div class="de1"><span class="br0">&#123;</span><span class="br0">&#123;</span><span class="nu0">10</span>,<span class="nu0">0</span>,<span class="nu0">0</span>,<span class="nu0">2</span><span class="br0">&#125;</span>, <span class="nu0">62001</span>, <span class="nu0">124000</span><span class="br0">&#125;</span>,</div>
</li>
<li class="li1">
<div class="de1"><span class="br0">&#123;</span><span class="br0">&#123;</span><span class="nu0">10</span>,<span class="nu0">0</span>,<span class="nu0">0</span>,<span class="nu0">3</span><span class="br0">&#125;</span>, <span class="nu0">124001</span>, <span class="nu0">186000</span><span class="br0">&#125;</span>,</div>
</li>
<li class="li1">
<div class="de1"><span class="br0">&#123;</span><span class="br0">&#123;</span><span class="nu0">10</span>,<span class="nu0">0</span>,<span class="nu0">0</span>,<span class="nu0">4</span><span class="br0">&#125;</span>, <span class="nu0">186001</span>, <span class="nu0">248000</span><span class="br0">&#125;</span>,</div>
</li>
<li class="li2">
<div class="de2"><span class="br0">&#123;</span><span class="br0">&#123;</span><span class="nu0">10</span>,<span class="nu0">0</span>,<span class="nu0">0</span>,<span class="nu0">5</span><span class="br0">&#125;</span>, <span class="nu0">248001</span>, <span class="nu0">310000</span><span class="br0">&#125;</span>,</div>
</li>
<li class="li1">
<div class="de1"><span class="br0">&#123;</span><span class="br0">&#123;</span><span class="nu0">10</span>,<span class="nu0">0</span>,<span class="nu0">0</span>,<span class="nu0">6</span><span class="br0">&#125;</span>, <span class="nu0">310001</span>, <span class="nu0">372000</span><span class="br0">&#125;</span>,</div>
</li>
<li class="li1">
<div class="de1"><span class="br0">&#123;</span><span class="br0">&#123;</span><span class="nu0">10</span>,<span class="nu0">0</span>,<span class="nu0">0</span>,<span class="nu0">7</span><span class="br0">&#125;</span>, <span class="nu0">372001</span>, <span class="nu0">434000</span><span class="br0">&#125;</span>,</div>
</li>
<li class="li1">
<div class="de1"><span class="br0">&#123;</span><span class="br0">&#123;</span><span class="nu0">10</span>,<span class="nu0">0</span>,<span class="nu0">0</span>,<span class="nu0">8</span><span class="br0">&#125;</span>, <span class="nu0">434001</span>, <span class="nu0">496000</span><span class="br0">&#125;</span>,</div>
</li>
<li class="li1">
<div class="de1"><span class="br0">&#123;</span><span class="br0">&#123;</span><span class="nu0">10</span>,<span class="nu0">0</span>,<span class="nu0">0</span>,<span class="nu0">9</span><span class="br0">&#125;</span>, <span class="nu0">496001</span>, <span class="nu0">558000</span><span class="br0">&#125;</span>,</div>
</li>
<li class="li2">
<div class="de2"><span class="br0">&#123;</span><span class="br0">&#123;</span><span class="nu0">10</span>,<span class="nu0">0</span>,<span class="nu0">0</span>,<span class="nu0">10</span><span class="br0">&#125;</span>, <span class="nu0">558001</span>, <span class="nu0">620000</span><span class="br0">&#125;</span>,</div>
</li>
<li class="li1">
<div class="de1"><span class="br0">&#123;</span><span class="br0">&#123;</span><span class="nu0">10</span>,<span class="nu0">0</span>,<span class="nu0">0</span>,<span class="nu0">11</span><span class="br0">&#125;</span>, <span class="nu0">620001</span>, <span class="nu0">682000</span><span class="br0">&#125;</span>,</div>
</li>
<li class="li1">
<div class="de1"><span class="br0">&#123;</span><span class="br0">&#123;</span><span class="nu0">10</span>,<span class="nu0">0</span>,<span class="nu0">0</span>,<span class="nu0">12</span><span class="br0">&#125;</span>, <span class="nu0">682001</span>, <span class="nu0">744000</span><span class="br0">&#125;</span>,</div>
</li>
<li class="li1">
<div class="de1"><span class="br0">&#123;</span><span class="br0">&#123;</span><span class="nu0">10</span>,<span class="nu0">0</span>,<span class="nu0">0</span>,<span class="nu0">13</span><span class="br0">&#125;</span>, <span class="nu0">744001</span>, <span class="nu0">806000</span><span class="br0">&#125;</span>,</div>
</li>
<li class="li1">
<div class="de1"><span class="br0">&#123;</span><span class="br0">&#123;</span><span class="nu0">10</span>,<span class="nu0">0</span>,<span class="nu0">0</span>,<span class="nu0">14</span><span class="br0">&#125;</span>, <span class="nu0">806001</span>, <span class="nu0">868000</span><span class="br0">&#125;</span>,</div>
</li>
<li class="li2">
<div class="de2"><span class="br0">&#123;</span><span class="br0">&#123;</span><span class="nu0">10</span>,<span class="nu0">0</span>,<span class="nu0">0</span>,<span class="nu0">15</span><span class="br0">&#125;</span>, <span class="nu0">868001</span>, <span class="nu0">930000</span><span class="br0">&#125;</span>,</div>
</li>
<li class="li1">
<div class="de1"><span class="br0">&#123;</span><span class="br0">&#123;</span><span class="nu0">10</span>,<span class="nu0">0</span>,<span class="nu0">0</span>,<span class="nu0">16</span><span class="br0">&#125;</span>, <span class="nu0">930001</span>, <span class="nu0">992000</span><span class="br0">&#125;</span>,</div>
</li>
<li class="li1">
<div class="de1"><span class="br0">&#123;</span><span class="br0">&#123;</span><span class="nu0">10</span>,<span class="nu0">0</span>,<span class="nu0">0</span>,<span class="nu0">17</span><span class="br0">&#125;</span>, <span class="nu0">992001</span>, <span class="nu0">1054000</span><span class="br0">&#125;</span><span class="br0">&#93;</span>,</div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; start<span class="br0">&#40;</span><span class="re0">Config</span>, <span class="re0">Interval</span><span class="br0">&#41;</span>.</div>
</li>
<li class="li1">
<div class="de1">&nbsp;</div>
</li>
<li class="li2">
<div class="de2">start<span class="br0">&#40;</span><span class="re0">Config</span>, <span class="re0">Interval</span><span class="br0">&#41;</span> -&gt;</div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; <span class="re0">Monitor</span> = monitor<span class="br0">&#40;</span><span class="br0">&#41;</span>,</div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; <span class="re0">AdjustedInterval</span> = <span class="re0">Interval</span> / length<span class="br0">&#40;</span><span class="re0">Config</span><span class="br0">&#41;</span>,</div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; <span class="br0">&#91;</span> spawn<span class="br0">&#40;</span>fun start/<span class="nu0">5</span>, <span class="br0">&#91;</span><span class="re0">Lower</span>, <span class="re0">Upper</span>, <span class="re0">Ip</span>, <span class="re0">AdjustedInterval</span>, <span class="re0">Monitor</span><span class="br0">&#93;</span><span class="br0">&#41;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; || <span class="br0">&#123;</span><span class="re0">Ip</span>, <span class="re0">Lower</span>, <span class="re0">Upper</span><span class="br0">&#125;</span> &nbsp;&lt;- <span class="re0">Config</span> <span class="br0">&#93;</span>,</div>
</li>
<li class="li2">
<div class="de2">&nbsp; &nbsp; &nbsp; &nbsp; ok.</div>
</li>
<li class="li1">
<div class="de1">&nbsp;</div>
</li>
<li class="li1">
<div class="de1">start<span class="br0">&#40;</span><span class="re0">LowerID</span>, <span class="re0">UpperID</span>, _, _, _<span class="br0">&#41;</span> when <span class="re0">LowerID</span> == <span class="re0">UpperID</span> -&gt; <span class="me1">done</span>;</div>
</li>
<li class="li1">
<div class="de1">start<span class="br0">&#40;</span><span class="re0">LowerID</span>, <span class="re0">UpperID</span>, <span class="re0">LocalIP</span>, <span class="re0">Interval</span>, <span class="re0">Monitor</span><span class="br0">&#41;</span> -&gt;</div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; <span class="me1">spawn</span><span class="br0">&#40;</span>fun connect/<span class="nu0">5</span>, <span class="br0">&#91;</span>?<span class="re0">SERVERADDR</span>, ?<span class="re0">SERVERPORT</span>, <span class="re0">LocalIP</span>, <span class="st0">&quot;/test/&quot;</span>++<span class="re0">LowerID</span>, <span class="re0">Monitor</span><span class="br0">&#93;</span><span class="br0">&#41;</span>,</div>
</li>
<li class="li2">
<div class="de2">&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">receive</span> <span class="kw1">after</span> <span class="re0">Interval</span> -&gt; <span class="me1">start</span><span class="br0">&#40;</span><span class="re0">LowerID</span> + <span class="nu0">1</span>, <span class="re0">UpperID</span>, <span class="re0">LocalIP</span>, <span class="re0">Interval</span>, <span class="re0">Monitor</span><span class="br0">&#41;</span> <span class="kw1">end</span>.</div>
</li>
<li class="li1">
<div class="de1">&nbsp;</div>
</li>
<li class="li1">
<div class="de1">connect<span class="br0">&#40;</span><span class="re0">ServerAddr</span>, <span class="re0">ServerPort</span>, <span class="re0">ClientIP</span>, <span class="re0">Path</span>, <span class="re0">Monitor</span><span class="br0">&#41;</span> -&gt;</div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; <span class="re0">Opts</span> = <span class="br0">&#91;</span>binary, <span class="br0">&#123;</span>packet, <span class="nu0">0</span><span class="br0">&#125;</span>, <span class="br0">&#123;</span>ip, <span class="re0">ClientIP</span><span class="br0">&#125;</span>, <span class="br0">&#123;</span>reuseaddr, true<span class="br0">&#125;</span>, <span class="br0">&#123;</span>active, false<span class="br0">&#125;</span><span class="br0">&#93;</span>,</div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; <span class="br0">&#123;</span>ok, <span class="re0">Sock</span><span class="br0">&#125;</span> = gen_tcp:<span class="me2">connect</span><span class="br0">&#40;</span><span class="re0">ServerAddr</span>, <span class="re0">ServerPort</span>, <span class="re0">Opts</span><span class="br0">&#41;</span>,</div>
</li>
<li class="li2">
<div class="de2">&nbsp; &nbsp; &nbsp; &nbsp; <span class="re0">Monitor</span> ! open,</div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; <span class="re0">ReqL</span> = io_lib:<span class="kw3">format</span><span class="br0">&#40;</span><span class="st0">&quot;GET ~s<span class="es0">\r</span><span class="es0">\n</span>Host: ~s<span class="es0">\r</span><span class="es0">\n</span><span class="es0">\r</span><span class="es0">\n</span>&quot;</span>, <span class="br0">&#91;</span><span class="re0">Path</span>, <span class="re0">ServerAddr</span><span class="br0">&#93;</span><span class="br0">&#41;</span>,</div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; <span class="re0">Req</span> = list_to_binary<span class="br0">&#40;</span><span class="re0">ReqL</span><span class="br0">&#41;</span>,</div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; ok = gen_tcp:<span class="me2">send</span><span class="br0">&#40;</span><span class="re0">Sock</span>, <span class="br0">&#91;</span><span class="re0">Req</span><span class="br0">&#93;</span><span class="br0">&#41;</span>,</div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; do_recv<span class="br0">&#40;</span><span class="re0">Sock</span>, <span class="re0">Monitor</span><span class="br0">&#41;</span>,</div>
</li>
<li class="li2">
<div class="de2">&nbsp; &nbsp; &nbsp; &nbsp; <span class="br0">&#40;</span>catch gen_tcp:<span class="kw3">close</span><span class="br0">&#40;</span><span class="re0">Sock</span><span class="br0">&#41;</span><span class="br0">&#41;</span>,</div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; ok.</div>
</li>
<li class="li1">
<div class="de1">&nbsp;</div>
</li>
<li class="li1">
<div class="de1">do_recv<span class="br0">&#40;</span><span class="re0">Sock</span>, <span class="re0">Monitor</span><span class="br0">&#41;</span>-&gt;</div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">case</span> gen_tcp:<span class="me2">recv</span><span class="br0">&#40;</span><span class="re0">Sock</span>, <span class="nu0">0</span><span class="br0">&#41;</span> <span class="kw1">of</span></div>
</li>
<li class="li2">
<div class="de2">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="br0">&#123;</span>ok, <span class="re0">B</span><span class="br0">&#125;</span> -&gt;</div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="re0">Monitor</span> ! <span class="br0">&#123;</span>bytes, size<span class="br0">&#40;</span><span class="re0">B</span><span class="br0">&#41;</span><span class="br0">&#125;</span>,</div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; io:<span class="kw3">format</span><span class="br0">&#40;</span><span class="st0">&quot;Recvd ~s<span class="es0">\n</span>&quot;</span>, <span class="br0">&#91;</span> binary_to_list<span class="br0">&#40;</span><span class="re0">B</span><span class="br0">&#41;</span><span class="br0">&#93;</span><span class="br0">&#41;</span>,</div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; io:<span class="kw3">format</span><span class="br0">&#40;</span><span class="st0">&quot;Recvd ~w bytes<span class="es0">\n</span>&quot;</span>, <span class="br0">&#91;</span>size<span class="br0">&#40;</span><span class="re0">B</span><span class="br0">&#41;</span><span class="br0">&#93;</span><span class="br0">&#41;</span>,</div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; do_recv<span class="br0">&#40;</span><span class="re0">Sock</span>, <span class="re0">Monitor</span><span class="br0">&#41;</span>;</div>
</li>
<li class="li2">
<div class="de2">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="br0">&#123;</span>error, closed<span class="br0">&#125;</span> -&gt;</div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="re0">Monitor</span> ! closed,</div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; closed;</div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="re0">Other</span> -&gt;</div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="re0">Monitor</span> ! closed,</div>
</li>
<li class="li2">
<div class="de2">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; io:<span class="kw3">format</span><span class="br0">&#40;</span><span class="st0">&quot;Other:~w<span class="es0">\n</span>&quot;</span>,<span class="br0">&#91;</span><span class="re0">Other</span><span class="br0">&#93;</span><span class="br0">&#41;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">end</span>.</div>
</li>
<li class="li1">
<div class="de1">&nbsp;</div>
</li>
<li class="li1">
<div class="de1"><span class="co1">% Monitor process receives stats and reports how much data we received etc:</span></div>
</li>
<li class="li1">
<div class="de1">monitor<span class="br0">&#40;</span><span class="br0">&#41;</span> -&gt;</div>
</li>
<li class="li2">
<div class="de2">&nbsp; &nbsp; &nbsp; &nbsp; <span class="re0">Pid</span> = spawn<span class="br0">&#40;</span>?<span class="re0">MODULE</span>, monitor0, <span class="br0">&#91;</span><span class="br0">&#123;</span><span class="nu0">0</span>,<span class="nu0">0</span>,<span class="nu0">0</span>,<span class="nu0">0</span><span class="br0">&#125;</span><span class="br0">&#93;</span><span class="br0">&#41;</span>,</div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; timer:<span class="me2">send_interval</span><span class="br0">&#40;</span><span class="nu0">10000</span>, <span class="re0">Pid</span>, report<span class="br0">&#41;</span>,</div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; <span class="re0">Pid</span>.</div>
</li>
<li class="li1">
<div class="de1">&nbsp;</div>
</li>
<li class="li1">
<div class="de1">monitor0<span class="br0">&#40;</span><span class="br0">&#123;</span><span class="re0">Open</span>, <span class="re0">Closed</span>, <span class="re0">Chunks</span>, <span class="re0">Bytes</span><span class="br0">&#125;</span>=<span class="re0">S</span><span class="br0">&#41;</span> -&gt;</div>
</li>
<li class="li2">
<div class="de2">&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">receive</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; report &nbsp;-&gt; <span class="me1">io</span>:<span class="kw3">format</span><span class="br0">&#40;</span><span class="st0">&quot;{Open, Closed, Chunks, Bytes} = ~w<span class="es0">\n</span>&quot;</span>,<span class="br0">&#91;</span><span class="re0">S</span><span class="br0">&#93;</span><span class="br0">&#41;</span>;</div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; open &nbsp; &nbsp;-&gt; <span class="me1">monitor0</span><span class="br0">&#40;</span><span class="br0">&#123;</span><span class="re0">Open</span> + <span class="nu0">1</span>, <span class="re0">Closed</span>, <span class="re0">Chunks</span>, <span class="re0">Bytes</span><span class="br0">&#125;</span><span class="br0">&#41;</span>;</div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; closed &nbsp;-&gt; <span class="me1">monitor0</span><span class="br0">&#40;</span><span class="br0">&#123;</span><span class="re0">Open</span>, <span class="re0">Closed</span> + <span class="nu0">1</span>, <span class="re0">Chunks</span>, <span class="re0">Bytes</span><span class="br0">&#125;</span><span class="br0">&#41;</span>;</div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; chunk &nbsp; -&gt; <span class="me1">monitor0</span><span class="br0">&#40;</span><span class="br0">&#123;</span><span class="re0">Open</span>, <span class="re0">Closed</span>, <span class="re0">Chunks</span> + <span class="nu0">1</span>, <span class="re0">Bytes</span><span class="br0">&#125;</span><span class="br0">&#41;</span>;</div>
</li>
<li class="li2">
<div class="de2">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="br0">&#123;</span>bytes, <span class="re0">B</span><span class="br0">&#125;</span> -&gt; <span class="me1">monitor0</span><span class="br0">&#40;</span><span class="br0">&#123;</span><span class="re0">Open</span>, <span class="re0">Closed</span>, <span class="re0">Chunks</span>, <span class="re0">Bytes</span> + <span class="re0">B</span><span class="br0">&#125;</span><span class="br0">&#41;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">end</span>.</div>
</li>
</ol>
</div>
<p><br/><br />
As an initial test I was connecting to the mochiweb app from Part 1 &#8211; it simply sends one message to every client every 10 seconds.</p>
<p><code>erl&gt; c(floodtest2), floodtest2:run(20).</code></p>
<p><strong>This quickly ate all my memory.</strong></p>
<p>Turns out opening lots of connections with gen_tcp like that eats a lot of ram. I think it&#8217;d need ~36GB to make it work without any additional tuning. I&#8217;m not interested in trying to optimise my quick-hack erlang http client (in the real world, this would be 1M actual web browsers), and the only machine I could get my hands on that has more than 32GB of RAM is one of our production databases, and I can&#8217;t find a good excuse to take Last.fm offline whilst I test this :) Additionally, it seems like it still only managed to open around 64,500 ports. Hmm.</p>
<p>At this point I decided to break out the trusty <a href="http://monkey.org/~provos/libevent/">libevent</a>, which I was pleased to discover has an HTTP API. Newer versions also have a <code>evhttp_connection_set_local_address</code> function in the http API. This sounds promising.</p>
<p>Here&#8217;s the http client in C using libevent:</p>
<div class="dean_ch" style="white-space: wrap;">
<ol>
<li class="li1">
<div class="de1"><span class="co2">#include &lt;sys/types.h&gt;</span></div>
</li>
<li class="li1">
<div class="de1"><span class="co2">#include &lt;sys/time.h&gt;</span></div>
</li>
<li class="li1">
<div class="de1"><span class="co2">#include &lt;sys/queue.h&gt;</span></div>
</li>
<li class="li1">
<div class="de1"><span class="co2">#include &lt;stdlib.h&gt;</span></div>
</li>
<li class="li2">
<div class="de2"><span class="co2">#include &lt;err.h&gt;</span></div>
</li>
<li class="li1">
<div class="de1"><span class="co2">#include &lt;event.h&gt;</span></div>
</li>
<li class="li1">
<div class="de1"><span class="co2">#include &lt;evhttp.h&gt;</span></div>
</li>
<li class="li1">
<div class="de1"><span class="co2">#include &lt;unistd.h&gt;</span></div>
</li>
<li class="li1">
<div class="de1"><span class="co2">#include &lt;stdio.h&gt;</span></div>
</li>
<li class="li2">
<div class="de2"><span class="co2">#include &lt;sys/socket.h&gt;</span></div>
</li>
<li class="li1">
<div class="de1"><span class="co2">#include &lt;netinet/in.h&gt;</span></div>
</li>
<li class="li1">
<div class="de1"><span class="co2">#include &lt;time.h&gt;</span></div>
</li>
<li class="li1">
<div class="de1"><span class="co2">#include &lt;pthread.h&gt;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp;</div>
</li>
<li class="li2">
<div class="de2"><span class="co2">#define BUFSIZE 4096</span></div>
</li>
<li class="li1">
<div class="de1"><span class="co2">#define NUMCONNS 62000</span></div>
</li>
<li class="li1">
<div class="de1"><span class="co2">#define SERVERADDR &quot;10.103.1.43&quot;</span></div>
</li>
<li class="li1">
<div class="de1"><span class="co2">#define SERVERPORT 8000</span></div>
</li>
<li class="li1">
<div class="de1"><span class="co2">#define SLEEP_MS 10</span></div>
</li>
<li class="li2">
<div class="de2">&nbsp;</div>
</li>
<li class="li1">
<div class="de1"><span class="kw4">char</span> buf<span class="br0">&#91;</span>BUFSIZE<span class="br0">&#93;</span>;</div>
</li>
<li class="li1">
<div class="de1">&nbsp;</div>
</li>
<li class="li1">
<div class="de1"><span class="kw4">int</span> bytes_recvd = <span class="nu0">0</span>;</div>
</li>
<li class="li1">
<div class="de1"><span class="kw4">int</span> chunks_recvd = <span class="nu0">0</span>;</div>
</li>
<li class="li2">
<div class="de2"><span class="kw4">int</span> closed = <span class="nu0">0</span>;</div>
</li>
<li class="li1">
<div class="de1"><span class="kw4">int</span> connected = <span class="nu0">0</span>;</div>
</li>
<li class="li1">
<div class="de1">&nbsp;</div>
</li>
<li class="li1">
<div class="de1"><span class="co1">// called per chunk received</span></div>
</li>
<li class="li1">
<div class="de1"><span class="kw4">void</span> chunkcb<span class="br0">&#40;</span><span class="kw4">struct</span> evhttp_request * req, <span class="kw4">void</span> * arg<span class="br0">&#41;</span></div>
</li>
<li class="li2">
<div class="de2"><span class="br0">&#123;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; <span class="kw4">int</span> s = evbuffer_remove<span class="br0">&#40;</span> req-&gt;input_buffer, &amp;buf, BUFSIZE <span class="br0">&#41;</span>;</div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; <span class="co1">//printf(&quot;Read %d bytes: %s\n&quot;, s, &amp;buf);</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; bytes_recvd += s;</div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; chunks_recvd++;</div>
</li>
<li class="li2">
<div class="de2">&nbsp; &nbsp; <span class="kw1">if</span><span class="br0">&#40;</span>connected &gt;= NUMCONNS &amp;&amp; chunks_recvd%<span class="nu0">10000</span>==<span class="nu0">0</span><span class="br0">&#41;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; <a href="http://www.opengroup.org/onlinepubs/009695399/functions/printf.html"><span class="kw3">printf</span></a><span class="br0">&#40;</span><span class="st0">&quot;&gt;Chunks: %d<span class="es0">\t</span>Bytes: %d<span class="es0">\t</span>Closed: %d<span class="es0">\n</span>&quot;</span>, chunks_recvd, bytes_recvd, closed<span class="br0">&#41;</span>;</div>
</li>
<li class="li1">
<div class="de1"><span class="br0">&#125;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp;</div>
</li>
<li class="li1">
<div class="de1"><span class="co1">// gets called when request completes</span></div>
</li>
<li class="li2">
<div class="de2"><span class="kw4">void</span> reqcb<span class="br0">&#40;</span><span class="kw4">struct</span> evhttp_request * req, <span class="kw4">void</span> * arg<span class="br0">&#41;</span></div>
</li>
<li class="li1">
<div class="de1"><span class="br0">&#123;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; closed++;</div>
</li>
<li class="li1">
<div class="de1"><span class="br0">&#125;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp;</div>
</li>
<li class="li2">
<div class="de2"><span class="kw4">int</span> main<span class="br0">&#40;</span><span class="kw4">int</span> argc, <span class="kw4">char</span> **argv<span class="br0">&#41;</span></div>
</li>
<li class="li1">
<div class="de1"><span class="br0">&#123;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; event_init<span class="br0">&#40;</span><span class="br0">&#41;</span>;</div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; <span class="kw4">struct</span> evhttp *evhttp_connection;</div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; <span class="kw4">struct</span> evhttp_request *evhttp_request;</div>
</li>
<li class="li2">
<div class="de2">&nbsp; &nbsp; <span class="kw4">char</span> addr<span class="br0">&#91;</span><span class="nu0">16</span><span class="br0">&#93;</span>;</div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; <span class="kw4">char</span> path<span class="br0">&#91;</span><span class="nu0">32</span><span class="br0">&#93;</span>; <span class="co1">// eg: &quot;/test/123&quot;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; <span class="kw4">int</span> i,octet;</div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; <span class="kw1">for</span><span class="br0">&#40;</span>octet=<span class="nu0">1</span>; octet&lt;=<span class="nu0">17</span>; octet++<span class="br0">&#41;</span><span class="br0">&#123;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; sprintf<span class="br0">&#40;</span>&amp;addr, <span class="st0">&quot;10.224.0.%d&quot;</span>, octet<span class="br0">&#41;</span>;</div>
</li>
<li class="li2">
<div class="de2">&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">for</span><span class="br0">&#40;</span>i=<span class="nu0">1</span>;i&lt;=NUMCONNS;i++<span class="br0">&#41;</span> <span class="br0">&#123;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; evhttp_connection = evhttp_connection_new<span class="br0">&#40;</span>SERVERADDR, SERVERPORT<span class="br0">&#41;</span>;</div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; evhttp_connection_set_local_address<span class="br0">&#40;</span>evhttp_connection, &amp;addr<span class="br0">&#41;</span>;</div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; evhttp_set_timeout<span class="br0">&#40;</span>evhttp_connection, <span class="nu0">864000</span><span class="br0">&#41;</span>; <span class="co1">// 10 day timeout</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; evhttp_request = evhttp_request_new<span class="br0">&#40;</span>reqcb, <span class="kw2">NULL</span><span class="br0">&#41;</span>;</div>
</li>
<li class="li2">
<div class="de2">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; evhttp_request-&gt;chunk_cb = chunkcb;</div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; sprintf<span class="br0">&#40;</span>&amp;path, <span class="st0">&quot;/test/%d&quot;</span>, ++connected<span class="br0">&#41;</span>;</div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">if</span><span class="br0">&#40;</span>i%<span class="nu0">100</span>==<span class="nu0">0</span><span class="br0">&#41;</span> &nbsp;<a href="http://www.opengroup.org/onlinepubs/009695399/functions/printf.html"><span class="kw3">printf</span></a><span class="br0">&#40;</span><span class="st0">&quot;Req: %s<span class="es0">\t</span>-&gt;<span class="es0">\t</span>%s<span class="es0">\n</span>&quot;</span>, addr, &amp;path<span class="br0">&#41;</span>;</div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; evhttp_make_request<span class="br0">&#40;</span> evhttp_connection, evhttp_request, EVHTTP_REQ_GET, path <span class="br0">&#41;</span>;</div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; evhttp_connection_set_timeout<span class="br0">&#40;</span>evhttp_request-&gt;evcon, <span class="nu0">864000</span><span class="br0">&#41;</span>;</div>
</li>
<li class="li2">
<div class="de2">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; event_loop<span class="br0">&#40;</span> EVLOOP_NONBLOCK <span class="br0">&#41;</span>;</div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">if</span><span class="br0">&#40;</span> connected % <span class="nu0">200</span> == <span class="nu0">0</span> <span class="br0">&#41;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <a href="http://www.opengroup.org/onlinepubs/009695399/functions/printf.html"><span class="kw3">printf</span></a><span class="br0">&#40;</span><span class="st0">&quot;<span class="es0">\n</span>Chunks: %d<span class="es0">\t</span>Bytes: %d<span class="es0">\t</span>Closed: %d<span class="es0">\n</span>&quot;</span>, chunks_recvd, bytes_recvd, closed<span class="br0">&#41;</span>;</div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; usleep<span class="br0">&#40;</span>SLEEP_MS*<span class="nu0">1000</span><span class="br0">&#41;</span>;</div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; <span class="br0">&#125;</span></div>
</li>
<li class="li2">
<div class="de2">&nbsp; &nbsp; <span class="br0">&#125;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; event_dispatch<span class="br0">&#40;</span><span class="br0">&#41;</span>;</div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; <span class="kw1">return</span> <span class="nu0">0</span>;</div>
</li>
<li class="li1">
<div class="de1"><span class="br0">&#125;</span></div>
</li>
</ol>
</div>
<p><br/><br />
Most parameters are hardcoded as #define&#8217;s so you configure it by editing the source and recompiling.</p>
<p>Compile and run:<br />
<code>$ gcc -o httpclient httpclient.c -levent<br />
$ ./httpclient</code></p>
<p><strong>This still failed to open more than 64,500 ports</strong>. Although it used less RAM doing it.</p>
<p>It turns out that although I was specifying the local addresses, the ephemeral port allocation somewhere in the kernel or tcp stack didn&#8217;t care, and still ran out after 2^16. So in order to open more than 64,500 connections, you need to specify the local address and local port yourself, and manage them accordingly.</p>
<p>Unfortunately the libevent HTTP API doesn&#8217;t have an option to specify the local port. I <a href="http://monkeymail.org/archives/libevent-users/2008-November/001415.html">patched libevent</a> to add a suitable function:<br />
<code>void evhttp_connection_set_local_port(struct evhttp_connection *evcon, u_short port);</code>. </p>
<p>This was a surprisingly pleasant experience; libevent seems well written, and the documentation is pretty decent too.</p>
<p>With my modified libevent installed, I was able to add the following under the set_local_address line in the above code:<br />
<code>evhttp_connection_set_local_port(evhttp_connection, 1024+i);</code></p>
<p>With that in place, multiple connections from different addresses were able to use the same local port number, specific to the the local address. I recompiled the client and let it run for a bit to confirm it would break the 2^16 barrier.</p>
<p><span title="Pun intended">Netstat confirms it</span>:<br />
<code># netstat -n | awk '/^tcp/ {t[$NF]++}END{for(state in t){print state, t[state]}}'<br />
TIME_WAIT 8<br />
ESTABLISHED 118222</code></p>
<p>This shows how many ports are open in various states. We&#8217;re finally able to open more than 2^16 connections, phew.</p>
<p>Now we have a tool capable of opening a million http connections from a single box. It seems to consume around 2KB per connection, plus whatever the kernel needs. It&#8217;s time to use it for the &#8220;million connected user&#8221; test against our mochiweb comet server.</p>
<h2>C1024K Test &#8211; 1 million comet connections</h2>
<p>For this test I used 4 different servers of varying specs. These specs may be overpowered for the experiment, but they were available and waiting to go into production, and this made a good burn-in test. All four servers are on the same gigabit LAN, with up to 3 switches and a router in the middle somewhere. </p>
<p>The 1 million test I ran is  similar to the 10k test from parts 1 and 2, the main difference being the modified client, now written in C using libevent, and that I&#8217;m running in a proper distributed-erlang setup with more than one machine.</p>
<p>On server 1 &#8211; Quad-core 2GHz CPU, 16GB of RAM</p>
<ul>
<li>Start subsmanager</li>
<li>Load in the friends data</li>
<li>Start the router</li>
</ul>
<p>On server 2 &#8211; Dual Quad-core 2.8GHz CPU, 32GB of RAM</p>
<ul>
<li>Start mochiweb app</li>
</ul>
<p>On server 3 &#8211; Quad-core 2GHz CPU, 16GB of RAM</p>
<ul>
<li>Create 17 virtual IPs as above</li>
<li>Install patched libevent</li>
<li>Run client: <code>./httpclient</code> to create 100 connections per second, up to 1M</li>
</ul>
<p>On server 4 &#8211; Dual-core 2GHz, 2GB RAM</p>
<ul>
<li>Run msggen program, to send lots of messages to the router</li>
</ul>
<p>I measured the memory usage of mochiweb during the ramp-up to a million connections, and for the rest of the day:</p>
<p><a href="http://www.metabrew.com/wp-content/uploads/2008/11/mochimem-c1000k.png"><img src="http://www.metabrew.com/wp-content/uploads/2008/11/mochimem-c1000k.png" alt="" title="Mochiweb memory, 1M connections" width="500" height="300" class="aligncenter size-full wp-image-172" /></a></p>
<p>The httpclient has a built in delay of 10ms between connections, so it took nearly 3 hours to open a million connections. The resident memory used by the mochiweb process with 1M open connections was around 25GB. Here&#8217;s the server this was running on as seen by Ganglia, which measures CPU, network and memory usage and produces nice graphs:</p>
<p><a href="http://www.metabrew.com/wp-content/uploads/2008/11/server21.png"><img src="http://www.metabrew.com/wp-content/uploads/2008/11/server21.png" alt="" title="Server running mochiweb, 1M connections" width="131" height="300" class="alignnone size-medium wp-image-173" /></a></p>
<p>You can see it needs around 38GB and has started to swap. I suspect the difference is mostly consumed by the kernel to keep those connections open. The uplift at the end is when I started sending messages.</p>
<p>Messages were generated using 1,000 processes, with an average time between messages of 60ms per process, giving around 16,666 messages per second overall:</p>
<p><code>erl&gt; [ spawn( fun()->msggen:start(1000000, 10+random:uniform(100), 1000000) end) || I <- lists:seq(1,1000) ].</code></p>
<p>The machine (server-4) generating messages looked like this on Ganglia:</p>
<p><a href="http://www.metabrew.com/wp-content/uploads/2008/11/msggen.png"><img src="http://www.metabrew.com/wp-content/uploads/2008/11/msggen.png" alt="" title="16,666 msgs/sec" width="131" height="300" class="alignnone size-medium wp-image-170" /></a></p>
<p>That&#8217;s 10 MB per second of messages it&#8217;s pumping out &#8211; 16,666 messages a second. Typically these messages would come from a message bus, app servers, or part of an existing infrastructure.</p>
<p>When I started sending messages, the load on server 1 (hosting subsmanager and router) stayed below 1, and CPU utilization increased from 0 to 5%. </p>
<p>CPU on server 2 (hosting mochiweb app, with 1M connections) increased more dramatically:</p>
<p><a href="http://www.metabrew.com/wp-content/uploads/2008/11/server2.png"><img src="http://www.metabrew.com/wp-content/uploads/2008/11/server2.png" alt="" title="Mochiweb server" width="131" height="300" class="alignnone size-medium wp-image-171" /></a></p>
<p>Naturally as processes have to leave their hibernate state to handle messages, memory usage will increase slightly. Having all connections open with no messages is a best-case for memory usage &#8211; unsurprisingly, actually doing stuff requires more memory.</p>
<p>So where does this leave us? To be on the safe side, the mochiweb machine would need 40GB of RAM to hold open 1M active comet connections. Under load, up to 30GB of the memory would be used by the mochiweb app, and the remaining 10GB by the kernel. In other words, you need to allow 40KB per connection.</p>
<p>During various test with lots of connections, I ended up making some additional changes to my sysctl.conf. This was part trial-and-error, I don&#8217;t really know enough about the internals to make especially informed decisions about which values to change. My policy was to wait for things to break, check <code>/var/log/kern.log</code> and see what mysterious error was reported, then increase stuff that sounded sensible after a spot of googling. Here are the settings in place during the above test:</p>
<pre>net.core.rmem_max = 33554432
net.core.wmem_max = 33554432
net.ipv4.tcp_rmem = 4096 16384 33554432
net.ipv4.tcp_wmem = 4096 16384 33554432
net.ipv4.tcp_mem = 786432 1048576 26777216
net.ipv4.tcp_max_tw_buckets = 360000
net.core.netdev_max_backlog = 2500
vm.min_free_kbytes = 65536
vm.swappiness = 0
net.ipv4.ip_local_port_range = 1024 65535</pre>
<p>I would like to learn more about Linux tcp tuning so I can make a more informed decision about these settings. These are almost certainly not optimal, but at least they were enough to get to 1M connections. These changes, along with the fact this is running on a 64bit Erlang VM, and thus has a wordsize of 8bytes instead of 4, might explain why the memory usage is much higher than I observed during the C10k test of part 2.</p>
<h2>An Erlang C-Node using Libevent</h2>
<p>After dabbling with the HTTP api for libevent, it seemed entirely sensible to try the 1M connection test against a libevent HTTPd written in C so we have a basis for comparison.</p>
<p>I&#8217;m guessing that enabling kernel poll means the erlang VM is able to use epoll (or similar), but even so there&#8217;s clearly some overhead involved which we might be able to mitigate by delegating the connection handling to a C program using libevent. I want to reuse most of the Erlang code so far, so let&#8217;s do the bare minimum in C &#8211; just the connection handling and HTTP stuff.</p>
<p>Libevent has an asynchronous HTTP API, which makes implementing http servers trivial &#8211; well, trivial for C, but still less trivial than mochiweb IMO ;) I&#8217;d also been looking for an excuse to try the Erlang C interface, so the following program combines the two. It&#8217;s a comet http server in C using libevent which identifies users using an integer Id (like our mochiweb app), and also acts as an Erlang C-Node. </p>
<p>It connects to a designated erlang node, listens for messages like <code>{123, &lt;&lt;"Hello user 123"&gt;&gt;}</code> then dispatches &#8220;Hello user 123&#8243; to user 123, if connected. Messages for users that are not connected are discarded, just like previous examples.</p>
<p>httpdcnode.c</p>
<div class="dean_ch" style="white-space: wrap;">
<ol>
<li class="li1">
<div class="de1"><span class="co2">#include &lt;sys/types.h&gt;</span></div>
</li>
<li class="li1">
<div class="de1"><span class="co2">#include &lt;sys/time.h&gt;</span></div>
</li>
<li class="li1">
<div class="de1"><span class="co2">#include &lt;sys/queue.h&gt;</span></div>
</li>
<li class="li1">
<div class="de1"><span class="co2">#include &lt;stdlib.h&gt;</span></div>
</li>
<li class="li2">
<div class="de2"><span class="co2">#include &lt;err.h&gt;</span></div>
</li>
<li class="li1">
<div class="de1"><span class="co2">#include &lt;event.h&gt;</span></div>
</li>
<li class="li1">
<div class="de1"><span class="co2">#include &lt;evhttp.h&gt;</span></div>
</li>
<li class="li1">
<div class="de1"><span class="co2">#include &lt;stdio.h&gt;</span></div>
</li>
<li class="li1">
<div class="de1"><span class="co2">#include &lt;sys/socket.h&gt;</span></div>
</li>
<li class="li2">
<div class="de2"><span class="co2">#include &lt;netinet/in.h&gt;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp;</div>
</li>
<li class="li1">
<div class="de1"><span class="co2">#include &quot;erl_interface.h&quot;</span></div>
</li>
<li class="li1">
<div class="de1"><span class="co2">#include &quot;ei.h&quot;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp;</div>
</li>
<li class="li2">
<div class="de2"><span class="co2">#include &lt;pthread.h&gt;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp;</div>
</li>
<li class="li1">
<div class="de1"><span class="co2">#define BUFSIZE 1024</span></div>
</li>
<li class="li1">
<div class="de1"><span class="co2">#define MAXUSERS (17*65536) // C1024K</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp;</div>
</li>
<li class="li2">
<div class="de2"><span class="co1">// List of current http requests by uid:</span></div>
</li>
<li class="li1">
<div class="de1"><span class="kw4">struct</span> evhttp_request * clients<span class="br0">&#91;</span>MAXUSERS<span class="nu0">+1</span><span class="br0">&#93;</span>;</div>
</li>
<li class="li1">
<div class="de1"><span class="co1">// Memory to store uids passed to the cleanup callback:</span></div>
</li>
<li class="li1">
<div class="de1"><span class="kw4">int</span> slots<span class="br0">&#91;</span>MAXUSERS<span class="nu0">+1</span><span class="br0">&#93;</span>;</div>
</li>
<li class="li1">
<div class="de1">&nbsp;</div>
</li>
<li class="li2">
<div class="de2"><span class="co1">// called when user disconnects</span></div>
</li>
<li class="li1">
<div class="de1"><span class="kw4">void</span> cleanup<span class="br0">&#40;</span><span class="kw4">struct</span> evhttp_connection *evcon, <span class="kw4">void</span> *arg<span class="br0">&#41;</span></div>
</li>
<li class="li1">
<div class="de1"><span class="br0">&#123;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; <span class="kw4">int</span> *uidp = <span class="br0">&#40;</span><span class="kw4">int</span> *<span class="br0">&#41;</span> arg;</div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; fprintf<span class="br0">&#40;</span>stderr, <span class="st0">&quot;disconnected uid %d<span class="es0">\n</span>&quot;</span>, *uidp<span class="br0">&#41;</span>;</div>
</li>
<li class="li2">
<div class="de2">&nbsp; &nbsp; clients<span class="br0">&#91;</span>*uidp<span class="br0">&#93;</span> = <span class="kw2">NULL</span>;</div>
</li>
<li class="li1">
<div class="de1"><span class="br0">&#125;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp;</div>
</li>
<li class="li1">
<div class="de1"><span class="co1">// handles http connections, sets them up for chunked transfer,</span></div>
</li>
<li class="li1">
<div class="de1"><span class="co1">// extracts the user id and registers in the global connection table,</span></div>
</li>
<li class="li2">
<div class="de2"><span class="co1">// also sends a welcome chunk.</span></div>
</li>
<li class="li1">
<div class="de1"><span class="kw4">void</span> request_handler<span class="br0">&#40;</span><span class="kw4">struct</span> evhttp_request *req, <span class="kw4">void</span> *arg<span class="br0">&#41;</span></div>
</li>
<li class="li1">
<div class="de1"><span class="br0">&#123;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw4">struct</span> evbuffer *buf;</div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; buf = evbuffer_new<span class="br0">&#40;</span><span class="br0">&#41;</span>;</div>
</li>
<li class="li2">
<div class="de2">&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">if</span> <span class="br0">&#40;</span>buf == <span class="kw2">NULL</span><span class="br0">&#41;</span><span class="br0">&#123;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; err<span class="br0">&#40;</span><span class="nu0">1</span>, <span class="st0">&quot;failed to create response buffer&quot;</span><span class="br0">&#41;</span>;</div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; <span class="br0">&#125;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp;</div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; evhttp_add_header<span class="br0">&#40;</span>req-&gt;output_headers, <span class="st0">&quot;Content-Type&quot;</span>, <span class="st0">&quot;text/html; charset=utf-8&quot;</span><span class="br0">&#41;</span>;</div>
</li>
<li class="li2">
<div class="de2">&nbsp;</div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw4">int</span> uid = <span class="nu0">-1</span>;</div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">if</span><span class="br0">&#40;</span>strncmp<span class="br0">&#40;</span>evhttp_request_uri<span class="br0">&#40;</span>req<span class="br0">&#41;</span>, <span class="st0">&quot;/test/&quot;</span>, <span class="nu0">6</span><span class="br0">&#41;</span> == <span class="nu0">0</span><span class="br0">&#41;</span><span class="br0">&#123;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; uid = atoi<span class="br0">&#40;</span> <span class="nu0">6</span>+evhttp_request_uri<span class="br0">&#40;</span>req<span class="br0">&#41;</span> <span class="br0">&#41;</span>;</div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; <span class="br0">&#125;</span></div>
</li>
<li class="li2">
<div class="de2">&nbsp;</div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">if</span><span class="br0">&#40;</span>uid &lt;= <span class="nu0">0</span><span class="br0">&#41;</span><span class="br0">&#123;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; evbuffer_add_printf<span class="br0">&#40;</span>buf, <span class="st0">&quot;User id not found, try /test/123 instead&quot;</span><span class="br0">&#41;</span>;</div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; evhttp_send_reply<span class="br0">&#40;</span>req, HTTP_NOTFOUND, <span class="st0">&quot;Not Found&quot;</span>, buf<span class="br0">&#41;</span>;</div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; evbuffer_free<span class="br0">&#40;</span>buf<span class="br0">&#41;</span>;</div>
</li>
<li class="li2">
<div class="de2">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">return</span>;</div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; <span class="br0">&#125;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp;</div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">if</span><span class="br0">&#40;</span>uid &gt; MAXUSERS<span class="br0">&#41;</span><span class="br0">&#123;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; evbuffer_add_printf<span class="br0">&#40;</span>buf, <span class="st0">&quot;Max uid allowed is %d&quot;</span>, MAXUSERS<span class="br0">&#41;</span>;</div>
</li>
<li class="li2">
<div class="de2">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; evhttp_send_reply<span class="br0">&#40;</span>req, HTTP_SERVUNAVAIL, <span class="st0">&quot;We ran out of numbers&quot;</span>, buf<span class="br0">&#41;</span>;</div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; evbuffer_free<span class="br0">&#40;</span>buf<span class="br0">&#41;</span>;</div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">return</span>;</div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; <span class="br0">&#125;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp;</div>
</li>
<li class="li2">
<div class="de2">&nbsp; &nbsp; &nbsp; &nbsp; evhttp_send_reply_start<span class="br0">&#40;</span>req, HTTP_OK, <span class="st0">&quot;OK&quot;</span><span class="br0">&#41;</span>;</div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">// Send welcome chunk:</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; evbuffer_add_printf<span class="br0">&#40;</span>buf, <span class="st0">&quot;Welcome, Url: &#8216;%s&#8217; Id: %d<span class="es0">\n</span>&quot;</span>, evhttp_request_uri<span class="br0">&#40;</span>req<span class="br0">&#41;</span>, uid<span class="br0">&#41;</span>;</div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; evhttp_send_reply_chunk<span class="br0">&#40;</span>req, buf<span class="br0">&#41;</span>;</div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; evbuffer_free<span class="br0">&#40;</span>buf<span class="br0">&#41;</span>;</div>
</li>
<li class="li2">
<div class="de2">&nbsp;</div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">// put reference into global uid-&gt;connection table:</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; clients<span class="br0">&#91;</span>uid<span class="br0">&#93;</span> = req;</div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">// set close callback</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; evhttp_connection_set_closecb<span class="br0">&#40;</span> req-&gt;evcon, cleanup, &amp;slots<span class="br0">&#91;</span>uid<span class="br0">&#93;</span> <span class="br0">&#41;</span>;</div>
</li>
<li class="li2">
<div class="de2"><span class="br0">&#125;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp;</div>
</li>
<li class="li1">
<div class="de1">&nbsp;</div>
</li>
<li class="li1">
<div class="de1"><span class="co1">// runs in a thread &#8211; the erlang c-node stuff</span></div>
</li>
<li class="li1">
<div class="de1"><span class="co1">// expects msgs like {uid, msg} and sends a a &#8216;msg&#8217; chunk to uid if connected</span></div>
</li>
<li class="li2">
<div class="de2"><span class="kw4">void</span> cnode_run<span class="br0">&#40;</span><span class="br0">&#41;</span></div>
</li>
<li class="li1">
<div class="de1"><span class="br0">&#123;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; <span class="kw4">int</span> fd; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="coMULTI">/* fd to Erlang node */</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; <span class="kw4">int</span> got; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="coMULTI">/* Result of receive */</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; <span class="kw4">unsigned</span> <span class="kw4">char</span> buf<span class="br0">&#91;</span>BUFSIZE<span class="br0">&#93;</span>; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="coMULTI">/* Buffer for incoming message */</span></div>
</li>
<li class="li2">
<div class="de2">&nbsp; &nbsp; ErlMessage emsg; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="coMULTI">/* Incoming message */</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp;</div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; ETERM *uid, *msg;</div>
</li>
<li class="li1">
<div class="de1">&nbsp;</div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; erl_init<span class="br0">&#40;</span><span class="kw2">NULL</span>, <span class="nu0">0</span><span class="br0">&#41;</span>;</div>
</li>
<li class="li2">
<div class="de2">&nbsp;</div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; <span class="kw1">if</span> <span class="br0">&#40;</span>erl_connect_init<span class="br0">&#40;</span><span class="nu0">1</span>, <span class="st0">&quot;secretcookie&quot;</span>, <span class="nu0">0</span><span class="br0">&#41;</span> == <span class="nu0">-1</span><span class="br0">&#41;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; erl_err_quit<span class="br0">&#40;</span><span class="st0">&quot;erl_connect_init&quot;</span><span class="br0">&#41;</span>;</div>
</li>
<li class="li1">
<div class="de1">&nbsp;</div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; <span class="kw1">if</span> <span class="br0">&#40;</span><span class="br0">&#40;</span>fd = erl_connect<span class="br0">&#40;</span><span class="st0">&quot;httpdmaster@localhost&quot;</span><span class="br0">&#41;</span><span class="br0">&#41;</span> &lt; <span class="nu0">0</span><span class="br0">&#41;</span></div>
</li>
<li class="li2">
<div class="de2">&nbsp; &nbsp; &nbsp; &nbsp; erl_err_quit<span class="br0">&#40;</span><span class="st0">&quot;erl_connect&quot;</span><span class="br0">&#41;</span>;</div>
</li>
<li class="li1">
<div class="de1">&nbsp;</div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; fprintf<span class="br0">&#40;</span>stderr, <span class="st0">&quot;Connected to httpdmaster@localhost<span class="es0">\n</span><span class="es0">\r</span>&quot;</span><span class="br0">&#41;</span>;</div>
</li>
<li class="li1">
<div class="de1">&nbsp;</div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; <span class="kw4">struct</span> evbuffer *evbuf;</div>
</li>
<li class="li2">
<div class="de2">&nbsp;</div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; <span class="kw1">while</span> <span class="br0">&#40;</span><span class="nu0">1</span><span class="br0">&#41;</span> <span class="br0">&#123;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; got = erl_receive_msg<span class="br0">&#40;</span>fd, buf, BUFSIZE, &amp;emsg<span class="br0">&#41;</span>;</div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">if</span> <span class="br0">&#40;</span>got == ERL_TICK<span class="br0">&#41;</span> <span class="br0">&#123;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">continue</span>;</div>
</li>
<li class="li2">
<div class="de2">&nbsp; &nbsp; &nbsp; &nbsp; <span class="br0">&#125;</span> <span class="kw1">else</span> <span class="kw1">if</span> <span class="br0">&#40;</span>got == ERL_ERROR<span class="br0">&#41;</span> <span class="br0">&#123;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; fprintf<span class="br0">&#40;</span>stderr, <span class="st0">&quot;ERL_ERROR from erl_receive_msg.<span class="es0">\n</span>&quot;</span><span class="br0">&#41;</span>;</div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw2">break</span>;</div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; <span class="br0">&#125;</span> <span class="kw1">else</span> <span class="br0">&#123;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">if</span> <span class="br0">&#40;</span>emsg.<span class="me1">type</span> == ERL_REG_SEND<span class="br0">&#41;</span> <span class="br0">&#123;</span></div>
</li>
<li class="li2">
<div class="de2">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">// get uid and body data from eg: {123, &lt;&lt;&quot;Hello&quot;&gt;&gt;}</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; uid = erl_element<span class="br0">&#40;</span><span class="nu0">1</span>, emsg.<span class="me1">msg</span><span class="br0">&#41;</span>;</div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; msg = erl_element<span class="br0">&#40;</span><span class="nu0">2</span>, emsg.<span class="me1">msg</span><span class="br0">&#41;</span>;</div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw4">int</span> userid = ERL_INT_VALUE<span class="br0">&#40;</span>uid<span class="br0">&#41;</span>;</div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw4">char</span> *body = <span class="br0">&#40;</span><span class="kw4">char</span> *<span class="br0">&#41;</span> ERL_BIN_PTR<span class="br0">&#40;</span>msg<span class="br0">&#41;</span>;</div>
</li>
<li class="li2">
<div class="de2">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw4">int</span> body_len = ERL_BIN_SIZE<span class="br0">&#40;</span>msg<span class="br0">&#41;</span>;</div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">// Is this userid connected?</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">if</span><span class="br0">&#40;</span>clients<span class="br0">&#91;</span>userid<span class="br0">&#93;</span><span class="br0">&#41;</span><span class="br0">&#123;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; fprintf<span class="br0">&#40;</span>stderr, <span class="st0">&quot;Sending %d bytes to uid %d<span class="es0">\n</span>&quot;</span>, body_len, userid<span class="br0">&#41;</span>; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;</div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; evbuf = evbuffer_new<span class="br0">&#40;</span><span class="br0">&#41;</span>;</div>
</li>
<li class="li2">
<div class="de2">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; evbuffer_add<span class="br0">&#40;</span>evbuf, <span class="br0">&#40;</span><span class="kw4">const</span> <span class="kw4">void</span>*<span class="br0">&#41;</span>body, <span class="br0">&#40;</span>size_t<span class="br0">&#41;</span> body_len<span class="br0">&#41;</span>;</div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; evhttp_send_reply_chunk<span class="br0">&#40;</span>clients<span class="br0">&#91;</span>userid<span class="br0">&#93;</span>, evbuf<span class="br0">&#41;</span>;</div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; evbuffer_free<span class="br0">&#40;</span>evbuf<span class="br0">&#41;</span>;</div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="br0">&#125;</span><span class="kw1">else</span><span class="br0">&#123;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; fprintf<span class="br0">&#40;</span>stderr, <span class="st0">&quot;Discarding %d bytes to uid %d &#8211; user not connected<span class="es0">\n</span>&quot;</span>, </div>
</li>
<li class="li2">
<div class="de2">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; body_len, userid<span class="br0">&#41;</span>; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;</div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">// noop</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="br0">&#125;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; erl_free_term<span class="br0">&#40;</span>emsg.<span class="me1">msg</span><span class="br0">&#41;</span>;</div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; erl_free_term<span class="br0">&#40;</span>uid<span class="br0">&#41;</span>; </div>
</li>
<li class="li2">
<div class="de2">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; erl_free_term<span class="br0">&#40;</span>msg<span class="br0">&#41;</span>;</div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="br0">&#125;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; <span class="br0">&#125;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; <span class="br0">&#125;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; <span class="co1">// if we got here, erlang connection died.</span></div>
</li>
<li class="li2">
<div class="de2">&nbsp; &nbsp; <span class="co1">// this thread is supposed to run forever</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; <span class="co1">// TODO &#8211; gracefully handle failure / reconnect / etc</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; pthread_exit<span class="br0">&#40;</span><span class="nu0">0</span><span class="br0">&#41;</span>;</div>
</li>
<li class="li1">
<div class="de1"><span class="br0">&#125;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp;</div>
</li>
<li class="li2">
<div class="de2"><span class="kw4">int</span> main<span class="br0">&#40;</span><span class="kw4">int</span> argc, <span class="kw4">char</span> **argv<span class="br0">&#41;</span></div>
</li>
<li class="li1">
<div class="de1"><span class="br0">&#123;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; <span class="co1">// Launch the thread that runs the cnode:</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; pthread_attr_t tattr;</div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; pthread_t helper;</div>
</li>
<li class="li2">
<div class="de2">&nbsp; &nbsp; <span class="kw4">int</span> status;</div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; pthread_create<span class="br0">&#40;</span>&amp;helper, <span class="kw2">NULL</span>, cnode_run, <span class="kw2">NULL</span><span class="br0">&#41;</span>;</div>
</li>
<li class="li1">
<div class="de1">&nbsp;</div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; <span class="kw4">int</span> i;</div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; <span class="kw1">for</span><span class="br0">&#40;</span>i=<span class="nu0">0</span>;i&lt;=MAXUSERS;i++<span class="br0">&#41;</span> slots<span class="br0">&#91;</span>i<span class="br0">&#93;</span>=i;</div>
</li>
<li class="li2">
<div class="de2">&nbsp; &nbsp; <span class="co1">// Launch libevent httpd:</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; <span class="kw4">struct</span> evhttp *httpd;</div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; event_init<span class="br0">&#40;</span><span class="br0">&#41;</span>;</div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; httpd = evhttp_start<span class="br0">&#40;</span><span class="st0">&quot;0.0.0.0&quot;</span>, <span class="nu0">8000</span><span class="br0">&#41;</span>;</div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; evhttp_set_gencb<span class="br0">&#40;</span>httpd, request_handler, <span class="kw2">NULL</span><span class="br0">&#41;</span>;</div>
</li>
<li class="li2">
<div class="de2">&nbsp; &nbsp; event_dispatch<span class="br0">&#40;</span><span class="br0">&#41;</span>;</div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; <span class="co1">// Not reached, event_dispatch() shouldn&#8217;t return</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; evhttp_free<span class="br0">&#40;</span>httpd<span class="br0">&#41;</span>;</div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; <span class="kw1">return</span> <span class="nu0">0</span>;</div>
</li>
<li class="li1">
<div class="de1"><span class="br0">&#125;</span></div>
</li>
</ol>
</div>
<p><br/></p>
<p>The maximum number of users is #defined, and similarly to the mochiweb server, it listens on port 8000 and expects users to connect with a path like so: <code>/test/&lt;userid&gt;</code>. Also hardcoded is the name of the erlang node it will connect to in order to receive messages, <code>httpdmaster@localhost</code>, and the erlang cookie, &#8220;secretcookie&#8221;. Change these accordingly.</p>
<p>Run the erlang node it will connect to first:<br />
<code>$ erl -setcookie secretcookie -sname httpdmaster@localhost</code></p>
<p>Compile and run like so:<br />
<code>$ gcc -o httpdcnode httpdcnode.c -lerl_interface -lei -levent<br />
$ ./httpdcnode</code></p>
<p>In the erlang shell, check you can see the hidden c-node:<br />
<code>erl&gt; nodes(hidden).<br />
[c1@localhost]</code></p>
<p>Now connect in your browser to <code>http://localhost:8000/test/123</code>. You should see the welcome message.</p>
<p>Now back to the erlang shell &#8211; send a message to the C node:</p>
<p><code>erl&gt; {any, c1@localhost} ! {123, &lt;&lt;"Hello Libevent World"&gt;&gt;}.</code></p>
<p><em>Note that we don&#8217;t have a Pid to use, so we use the alternate representation of {procname, node}. We use &#8216;any&#8217; as the process name, which is ignored by the C-node.</em></p>
<p><b>Now you&#8217;re able to deliver comet messages via Erlang, but all the http connections are managed by a libevent C program which acts as an Erlang node.</b></p>
<p>After removing the debug print statements, I connected 1M clients to the httpdcnode server using the same client as above, the machine showed a total of just under 10GB or memory used. The resident memory of the server process was stable at under 2GB:</p>
<p><a href="http://www.metabrew.com/wp-content/uploads/2008/11/mochimem-libevent.png"><img src="http://www.metabrew.com/wp-content/uploads/2008/11/mochimem-libevent.png" alt="" title="Memory of libevent-based server process, 1M connections" width="500" height="300" class="aligncenter size-full wp-image-176" /></a></p>
<p>So big savings compared to mochiweb when handling lots of connections &#8211; the resident memory per connection for the server process with libevent is just under 2KB. With everything connected, the server machine claims:<br />
<code>Mem:  32968672k total,  9636488k used, 23332184k free,      180k buffers</code><br />
So the kernel/tcp stack is consuming an additional 8KB per connection, which seems a little high, but I have no basis for comparison. </p>
<p>This libevent-cnode server needs a bit more work. It doesn&#8217;t sensibly handle multiple connections from the same user yet, and there&#8217;s no locking so a race condition exists if you disconnect at just when a message was going to be dispatched. </p>
<p>Even so, I think this could be generalized in such a way that would allow you to <strong>use Erlang for all the interesting stuff, and have a C+libevent process act as a dumb connection-pool</strong>. With a bit more wrapper code and callbacks into Erlang, you&#8217;d hardly need to know this was going on &#8211; the C program could be run as a driver or a C-node, and an Erlang wrapper could give you a decent api built on top of libevent. (see <a href="http://www.metabrew.com/article/erlang-libketama-driver-consistent-hashing/">this post</a> for an example Erlang C driver). I would like to experiment further with this.</p>
<h2>Final Thoughts</h2>
<p>I have enough data now to judge how much hardware would be needed if we deploy a large scale comet system for Last.fm. Even a worst case of 40KB per connection isn&#8217;t unreasonable &#8211; memory is pretty cheap at the moment, and 40GB to support a million users is not unreasonable. 10GB is even better. I will finish up the app I&#8217;m building and deploy it somewhere people can try it out. Along the way I&#8217;ll tidy up the erlang memcached client I&#8217;m using and release that (from jungerl, with modifications for consistent hashing and some bug fixes), and some other things. Stay tuned :)</p>
]]></content:encoded>
			<wfw:commentRss>http://www.metabrew.com/article/a-million-user-comet-application-with-mochiweb-part-3//feed</wfw:commentRss>
		<slash:comments>40</slash:comments>
		</item>
		<item>
		<title>Erlang libketama driver &#8211; Consistent Hashing</title>
		<link>http://www.metabrew.com/article/erlang-libketama-driver-consistent-hashing/</link>
		<comments>http://www.metabrew.com/article/erlang-libketama-driver-consistent-hashing/#comments</comments>
		<pubDate>Sun, 28 Sep 2008 13:41:54 +0000</pubDate>
		<dc:creator>RJ</dc:creator>
				<category><![CDATA[programming]]></category>
		<category><![CDATA[c]]></category>
		<category><![CDATA[driver]]></category>
		<category><![CDATA[erlang]]></category>
		<category><![CDATA[hashing]]></category>
		<category><![CDATA[ketama]]></category>
		<category><![CDATA[memcached]]></category>

		<guid isPermaLink="false">http://www.metabrew.com/?p=36</guid>
		<description><![CDATA[All the data I need from memcached is assigned to servers using a consistent hashing mechanism, implemented as libketama &#8211; a shared library written in C. We use a php extension to wrap this, and also have a pure java implementation. Rather than port the algorithm to Erlang, I wrote a an Erlang driver. There [...]]]></description>
			<content:encoded><![CDATA[<p>All the data I need from memcached is assigned to servers using a consistent hashing mechanism, <a href="http://www.last.fm/user/RJ/journal/2007/04/10/rz_libketama_-_a_consistent_hashing_algo_for_memcache_clients">implemented as libketama</a> &#8211; a shared library written in C. We use a php extension to wrap this, and also have a pure java implementation. Rather than port the algorithm to Erlang, I wrote a an Erlang driver.</p>
<p>There are 3 things covered here:</p>
<ul>
<li>A small driver program written in C (using libketama)</li>
<li>Some basic testing from the shell using Perl and xxd</li>
<li>The Erlang gen_server that calls it</li>
</ul>
<h2>C driver program</h2>
<div class="dean_ch" style="white-space: wrap;">
<ol>
<li class="li1">
<div class="de1"><span class="coMULTI">/* &nbsp;Expects a one-byte length header, followed by a key (&lt;255bytes)</span></div>
</li>
<li class="li1">
<div class="de1"><span class="coMULTI">&nbsp;* &nbsp;Returns an ip:port string with 1 byte len header</span></div>
</li>
<li class="li1">
<div class="de1"><span class="coMULTI">&nbsp;*</span></div>
</li>
<li class="li1">
<div class="de1"><span class="coMULTI">&nbsp;*/</span></div>
</li>
<li class="li2">
<div class="de2"><span class="co2">#include &lt;ketama.h&gt;</span></div>
</li>
<li class="li1">
<div class="de1"><span class="co2">#include &lt;stdio.h&gt;</span></div>
</li>
<li class="li1">
<div class="de1"><span class="co2">#include &lt;stdlib.h&gt;</span></div>
</li>
<li class="li1">
<div class="de1"><span class="co2">#include &lt;unistd.h&gt;</span></div>
</li>
<li class="li1">
<div class="de1"><span class="co2">#include &lt;string.h&gt;</span></div>
</li>
<li class="li2">
<div class="de2">&nbsp;</div>
</li>
<li class="li1">
<div class="de1"><span class="kw4">typedef</span> <span class="kw4">unsigned</span> <span class="kw4">char</span> byte;</div>
</li>
<li class="li1">
<div class="de1">&nbsp;</div>
</li>
<li class="li1">
<div class="de1"><span class="kw4">int</span> read_exact<span class="br0">&#40;</span>byte *buf, <span class="kw4">int</span> len<span class="br0">&#41;</span></div>
</li>
<li class="li1">
<div class="de1"><span class="br0">&#123;</span></div>
</li>
<li class="li2">
<div class="de2">&nbsp; &nbsp; <span class="kw4">int</span> i, got = <span class="nu0">0</span>;</div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; <span class="kw1">do</span> <span class="br0">&#123;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">if</span><span class="br0">&#40;</span><span class="br0">&#40;</span>i=read<span class="br0">&#40;</span><span class="nu0">0</span>,buf+got, len-got<span class="br0">&#41;</span><span class="br0">&#41;</span>&lt;=<span class="nu0">0</span><span class="br0">&#41;</span> <span class="kw1">return</span> i;</div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; got += i;</div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; <span class="br0">&#125;</span> <span class="kw1">while</span><span class="br0">&#40;</span>got&lt;len<span class="br0">&#41;</span>;</div>
</li>
<li class="li2">
<div class="de2">&nbsp; &nbsp; <span class="kw1">return</span> len;</div>
</li>
<li class="li1">
<div class="de1"><span class="br0">&#125;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp;</div>
</li>
<li class="li1">
<div class="de1"><span class="kw4">int</span> main<span class="br0">&#40;</span><span class="kw4">int</span> argc, <span class="kw4">char</span> **argv<span class="br0">&#41;</span></div>
</li>
<li class="li1">
<div class="de1"><span class="br0">&#123;</span></div>
</li>
<li class="li2">
<div class="de2">&nbsp; &nbsp; <span class="kw1">if</span><span class="br0">&#40;</span>argc==<span class="nu0">1</span><span class="br0">&#41;</span><span class="br0">&#123;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; <a href="http://www.opengroup.org/onlinepubs/009695399/functions/printf.html"><span class="kw3">printf</span></a><span class="br0">&#40;</span><span class="st0">&quot;Usage: %s &lt;ketama.servers file&gt;<span class="es0">\n</span>&quot;</span>, *argv<span class="br0">&#41;</span>;</div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">return</span> <span class="nu0">1</span>;</div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; <span class="br0">&#125;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp;</div>
</li>
<li class="li2">
<div class="de2">&nbsp; &nbsp; ketama_continuum c;</div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; ketama_roll<span class="br0">&#40;</span> &amp;c, *++argv <span class="br0">&#41;</span>;</div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; mcs *m;</div>
</li>
<li class="li1">
<div class="de1">&nbsp;</div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; byte len;</div>
</li>
<li class="li2">
<div class="de2">&nbsp; &nbsp; byte buffer<span class="br0">&#91;</span><span class="nu0">256</span><span class="br0">&#93;</span>;</div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; <span class="kw1">while</span> <span class="br0">&#40;</span> <span class="nu0">1</span> <span class="br0">&#41;</span> <span class="br0">&#123;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">if</span><span class="br0">&#40;</span> <span class="nu0">1</span> != read_exact<span class="br0">&#40;</span>&amp;len, <span class="nu0">1</span><span class="br0">&#41;</span> <span class="br0">&#41;</span> <span class="kw2">break</span>;</div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">if</span><span class="br0">&#40;</span> <span class="br0">&#40;</span><span class="kw4">int</span><span class="br0">&#41;</span>len &gt;= <span class="nu0">255</span> <span class="br0">&#41;</span> <span class="kw2">break</span>;</div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; read_exact<span class="br0">&#40;</span><span class="br0">&#40;</span>byte *<span class="br0">&#41;</span>&amp;buffer, <span class="br0">&#40;</span><span class="kw4">int</span><span class="br0">&#41;</span>len<span class="br0">&#41;</span>;</div>
</li>
<li class="li2">
<div class="de2">&nbsp; &nbsp; &nbsp; &nbsp; buffer<span class="br0">&#91;</span>len<span class="br0">&#93;</span> = <span class="st0">&#8216;<span class="es0">\0</span>&#8216;</span>;</div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; m = ketama_get_server<span class="br0">&#40;</span> <span class="br0">&#40;</span><span class="kw4">char</span> *<span class="br0">&#41;</span> &amp;buffer, c <span class="br0">&#41;</span>;</div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; sprintf<span class="br0">&#40;</span><span class="br0">&#40;</span><span class="kw4">char</span> *<span class="br0">&#41;</span>&amp;buffer, <span class="st0">&quot;%s&quot;</span>,m-&gt;ip<span class="br0">&#41;</span>;</div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw4">int</span> respleni = strlen<span class="br0">&#40;</span>m-&gt;ip<span class="br0">&#41;</span>;</div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw4">char</span> l = <span class="br0">&#40;</span>0xff &amp; respleni<span class="br0">&#41;</span>;</div>
</li>
<li class="li2">
<div class="de2">&nbsp; &nbsp; &nbsp; &nbsp; write<span class="br0">&#40;</span><span class="nu0">1</span>, &amp;l, <span class="nu0">1</span><span class="br0">&#41;</span>;</div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; write<span class="br0">&#40;</span><span class="nu0">1</span>, <span class="br0">&#40;</span><span class="kw4">char</span>*<span class="br0">&#41;</span>&amp;buffer, respleni<span class="br0">&#41;</span>;</div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; <span class="br0">&#125;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp;</div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; <span class="kw1">return</span> <span class="nu0">0</span>;</div>
</li>
<li class="li2">
<div class="de2"><span class="br0">&#125;</span></div>
</li>
</ol>
</div>
<p><br/></p>
<h2>Testing the driver with Perl and xxd</h2>
<p>Before writing the Erlang bit, it&#8217;d be nice to know the driver program does what we expect.  Will send the driver a 1-byte length header followed by the key, and expect a 1-byte length header and the value as a response. Say we&#8217;re hashing a memcached key &#8216;user:123&#8242; to a server, we can do what the Erlang port does with a bit of perl, and the &#8216;xxd&#8217; command to see output in binary.</p>
<p><code>perl -e '$key="user:123"; $len=pack("C",length($key)); print $len; print $key;' | xxd -b</code></p>
<pre>0000000: 00001000 01110101 01110011 01100101 01110010 00111010  .user:
0000006: 00110001 00110010 00110011                             123</pre>
<p>Note the first byte (00001000) printed before the key is the length of the key, 8. Now let&#8217;s send this to the driver program and check the response (provide a valid ketama.servers file):</p>
<p><code>perl -e '$key="user:123"; $len=pack("C",length($key)); print $len; print $key;' | ./ketama_erlang_driver /var/ketama.servers | xxd -b</code></p>
<pre>0000000: 00010000 00110001 00110000 00101110 00110000 00101110  .10.0.
0000006: 00110001 00101110 00110001 00110001 00111000 00111010  1.118:
000000c: 00110001 00110001 00110010 00110001 00110001           11211</pre>
<p>The first byte of the response (00010000) is 16, which is the length of the server address returned by the driver, &#8220;10.0.1.118:11211&#8243; &#8211; It does what we expect, onwards&#8230;</p>
<h2>The Erlang bit</h2>
<div class="dean_ch" style="white-space: wrap;">
<ol>
<li class="li1">
<div class="de1">-<span class="kw2">module</span><span class="br0">&#40;</span>ketama<span class="br0">&#41;</span>.</div>
</li>
<li class="li1">
<div class="de1">-<span class="kw2">behaviour</span><span class="br0">&#40;</span>gen_server<span class="br0">&#41;</span>.</div>
</li>
<li class="li1">
<div class="de1">-<span class="kw2">export</span><span class="br0">&#40;</span><span class="br0">&#91;</span><span class="kw3">start_link</span>/<span class="nu0">0</span>, <span class="kw3">start_link</span>/<span class="nu0">1</span>, <span class="kw3">start_link</span>/<span class="nu0">2</span>, getserver/<span class="nu0">1</span><span class="br0">&#93;</span><span class="br0">&#41;</span>.</div>
</li>
<li class="li1">
<div class="de1">-<span class="kw2">export</span><span class="br0">&#40;</span><span class="br0">&#91;</span>init/<span class="nu0">1</span>, handle_call/<span class="nu0">3</span>, handle_cast/<span class="nu0">2</span>, handle_info/<span class="nu0">2</span>,</div>
</li>
<li class="li2">
<div class="de2">&nbsp; &nbsp; &nbsp;terminate/<span class="nu0">2</span>, code_change/<span class="nu0">3</span><span class="br0">&#93;</span><span class="br0">&#41;</span>.</div>
</li>
<li class="li1">
<div class="de1">&nbsp;</div>
</li>
<li class="li1">
<div class="de1">-record<span class="br0">&#40;</span>state, <span class="br0">&#123;</span>port<span class="br0">&#125;</span><span class="br0">&#41;</span>.</div>
</li>
<li class="li1">
<div class="de1">&nbsp;</div>
</li>
<li class="li1">
<div class="de1"><span class="kw3">start_link</span><span class="br0">&#40;</span><span class="br0">&#41;</span> -&gt;</div>
</li>
<li class="li2">
<div class="de2">&nbsp; &nbsp; <span class="kw3">start_link</span><span class="br0">&#40;</span><span class="st0">&quot;/web/site/GLOBAL/ketama.servers&quot;</span><span class="br0">&#41;</span>.</div>
</li>
<li class="li1">
<div class="de1">&nbsp;</div>
</li>
<li class="li1">
<div class="de1"><span class="kw3">start_link</span><span class="br0">&#40;</span><span class="re0">ServersFile</span><span class="br0">&#41;</span> -&gt;</div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; <span class="kw3">start_link</span><span class="br0">&#40;</span><span class="re0">ServersFile</span>, <span class="st0">&quot;/usr/bin/ketama_erlang_driver&quot;</span><span class="br0">&#41;</span>.</div>
</li>
<li class="li1">
<div class="de1">&nbsp;</div>
</li>
<li class="li2">
<div class="de2"><span class="kw3">start_link</span><span class="br0">&#40;</span><span class="re0">ServersFile</span>, <span class="re0">BinPath</span><span class="br0">&#41;</span> -&gt;</div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; <span class="me1">gen_server</span>:<span class="kw3">start_link</span><span class="br0">&#40;</span><span class="br0">&#123;</span>local, ?<span class="re0">MODULE</span><span class="br0">&#125;</span>, ?<span class="re0">MODULE</span>, <span class="br0">&#91;</span><span class="re0">ServersFile</span>, <span class="re0">BinPath</span><span class="br0">&#93;</span>, <span class="br0">&#91;</span><span class="br0">&#93;</span><span class="br0">&#41;</span>.</div>
</li>
<li class="li1">
<div class="de1">&nbsp;</div>
</li>
<li class="li1">
<div class="de1">getserver<span class="br0">&#40;</span><span class="re0">Key</span><span class="br0">&#41;</span> -&gt;</div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; <span class="me1">gen_server</span>:<span class="kw3">call</span><span class="br0">&#40;</span>?<span class="re0">MODULE</span>, <span class="br0">&#123;</span>getserver, <span class="re0">Key</span><span class="br0">&#125;</span><span class="br0">&#41;</span>.</div>
</li>
<li class="li2">
<div class="de2">&nbsp;</div>
</li>
<li class="li1">
<div class="de1"><span class="co1">%%</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp;</div>
</li>
<li class="li1">
<div class="de1">init<span class="br0">&#40;</span><span class="br0">&#91;</span><span class="re0">ServersFile</span>, <span class="re0">BinPath</span><span class="br0">&#93;</span><span class="br0">&#41;</span> -&gt;</div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; <span class="re0">Exe</span> = <span class="re0">BinPath</span> ++ <span class="st0">&quot; &quot;</span> ++ <span class="re0">ServersFile</span>,</div>
</li>
<li class="li2">
<div class="de2">&nbsp; &nbsp; <span class="re0">Port</span> = open_port<span class="br0">&#40;</span><span class="br0">&#123;</span>spawn, <span class="re0">Exe</span><span class="br0">&#125;</span>, <span class="br0">&#91;</span>binary, <span class="br0">&#123;</span>packet, <span class="nu0">1</span><span class="br0">&#125;</span>, use_stdio<span class="br0">&#93;</span><span class="br0">&#41;</span>,</div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; <span class="br0">&#123;</span>ok, #state<span class="br0">&#123;</span>port=<span class="re0">Port</span><span class="br0">&#125;</span><span class="br0">&#125;</span>.</div>
</li>
<li class="li1">
<div class="de1">&nbsp;</div>
</li>
<li class="li1">
<div class="de1">handle_call<span class="br0">&#40;</span><span class="br0">&#123;</span>getserver, <span class="re0">Key</span><span class="br0">&#125;</span>, _<span class="re0">From</span>, #state<span class="br0">&#123;</span>port=<span class="re0">Port</span><span class="br0">&#125;</span> = <span class="re0">State</span><span class="br0">&#41;</span> -&gt;</div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; <span class="re0">Port</span> ! <span class="br0">&#123;</span>self<span class="br0">&#40;</span><span class="br0">&#41;</span>, <span class="br0">&#123;</span>command, <span class="re0">Key</span><span class="br0">&#125;</span><span class="br0">&#125;</span>,</div>
</li>
<li class="li2">
<div class="de2">&nbsp; &nbsp; <span class="kw1">receive</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; <span class="br0">&#123;</span><span class="re0">Port</span>, <span class="br0">&#123;</span>data, <span class="re0">Data</span><span class="br0">&#125;</span><span class="br0">&#125;</span> -&gt;</div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="br0">&#123;</span>reply, <span class="re0">Data</span>, <span class="re0">State</span><span class="br0">&#125;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">after</span> <span class="nu0">1000</span> -&gt; <span class="co1">% if it takes this long, you have serious issues.</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="br0">&#123;</span>stop, ketama_port_timeout, <span class="re0">State</span><span class="br0">&#125;</span></div>
</li>
<li class="li2">
<div class="de2">&nbsp; &nbsp; <span class="kw1">end</span>.</div>
</li>
<li class="li1">
<div class="de1">&nbsp;</div>
</li>
<li class="li1">
<div class="de1">handle_cast<span class="br0">&#40;</span>_<span class="re0">Msg</span>, <span class="re0">State</span><span class="br0">&#41;</span> -&gt; &nbsp; &nbsp;<span class="br0">&#123;</span>noreply, <span class="re0">State</span><span class="br0">&#125;</span>.</div>
</li>
<li class="li1">
<div class="de1">handle_info<span class="br0">&#40;</span><span class="br0">&#123;</span><span class="st0">&#8216;EXIT&#8217;</span>, <span class="re0">Port</span>, <span class="re0">Reason</span><span class="br0">&#125;</span>, #state<span class="br0">&#123;</span>port = <span class="re0">Port</span><span class="br0">&#125;</span> = <span class="re0">State</span><span class="br0">&#41;</span> -&gt;</div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; <span class="br0">&#123;</span>stop, <span class="br0">&#123;</span>port_terminated, <span class="re0">Reason</span><span class="br0">&#125;</span>, <span class="re0">State</span><span class="br0">&#125;</span>.</div>
</li>
<li class="li2">
<div class="de2">terminate<span class="br0">&#40;</span><span class="br0">&#123;</span>port_terminated, _<span class="re0">Reason</span><span class="br0">&#125;</span>, _<span class="re0">State</span><span class="br0">&#41;</span> -&gt; &nbsp; &nbsp;<span class="me1">ok</span>;</div>
</li>
<li class="li1">
<div class="de1">terminate<span class="br0">&#40;</span>_<span class="re0">Reason</span>, #state<span class="br0">&#123;</span>port = <span class="re0">Port</span><span class="br0">&#125;</span> = _<span class="re0">State</span><span class="br0">&#41;</span> -&gt; &nbsp; &nbsp; <span class="me1">port_close</span><span class="br0">&#40;</span><span class="re0">Port</span><span class="br0">&#41;</span>.</div>
</li>
<li class="li1">
<div class="de1">code_change<span class="br0">&#40;</span>_<span class="re0">OldVsn</span>, <span class="re0">State</span>, _<span class="re0">Extra</span><span class="br0">&#41;</span> -&gt; &nbsp; &nbsp; <span class="br0">&#123;</span>ok, <span class="re0">State</span><span class="br0">&#125;</span>.</div>
</li>
</ol>
</div>
<p><br/><br />
This code can be found in the erlang directory of the ketama source in svn:<br />
<code>svn://svn.audioscrobbler.net/misc/ketama/</code></p>
]]></content:encoded>
			<wfw:commentRss>http://www.metabrew.com/article/erlang-libketama-driver-consistent-hashing//feed</wfw:commentRss>
		<slash:comments>9</slash:comments>
		</item>
	</channel>
</rss>
