<?xml version="1.0"?>
<rss version="2.0">

<channel>
	<title>Planet Trapexit - Erlang/OTP News</title>
	<link>http://planet.trapexit.org</link>
	<language>en</language>
	<description>Planet Trapexit - Erlang/OTP News - http://planet.trapexit.org</description>

<item>
	<title>REALITY.SYS: Immer dieses Händewaschen…</title>
	<guid>http://www.brakmic.de/?p=459</guid>
	<link>http://www.brakmic.de/index.php/2011/06/04/immer-dieses-handewaschen/</link>
	<description>&lt;p&gt;Angesichts der, ähnlich SARS und Schweinepest, herbeigetrommelten Seuche &amp;#8220;EHEC&amp;#8221;, muss man sich ernsthaft die Frage stellen, ob wir in Sachen Hygiene nicht immer noch im Mittelalter stecken.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Hallo Leute&lt;/strong&gt;! Sich die Hände richtig zu waschen ist eine Sache, die man normalerweise als Kleinkind gelernt haben muss!&lt;/p&gt;
&lt;p&gt;Und die Betonung, dass man nach einem Toilettenbesuch die Hände waschen sollte, ist mehr als lächerlich. Es ist erschreckend, traurig und zeugt nur von unserer sterilen Lebensweise in dieser angeblich fortschrittlichen Welt. Ja, sterile Lebensweise, als Bezeichnung für unser Abgekoppelt-Sein von elementarsten menschlichen Ritualformen wie richtiges Waschen des eigenen Körpers, richtiger Umgang mit fremden Sachen (z.B. indem wir ausgestellte Waren nicht ohne Weiteres betatschen sollten!) etc.&lt;/p&gt;
&lt;p&gt;Wie blamabel muss es denn für uns sein, wenn man uns über die Medien darum bitten muss, sich die Hände richtig zu wachen und das Gemüse bzw. Fleisch vor dem Verzehr ebenfalls gewaschen zu haben.&lt;/p&gt;
&lt;p&gt;Aber nein, am leichtesten ist es doch, diesen &lt;em&gt;blöden Südeuropäern&lt;/em&gt; alles in die Schuhe zu schieben.&lt;span&gt; Ja, genau, die Spanier waren&amp;#8217;s&lt;/span&gt;, diese gemeinen Kerle. Sie haben uns all das eingebrockt und siehe da: wir müssen uns jetzt die Hände waschen!&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;</description>
	<pubDate>Sat, 04 Jun 2011 08:23:13 +0000</pubDate>
</item>
<item>
	<title>Process-one Blogs: “Google Social”: A paradox of its own</title>
	<guid>http://www.process-one.net/en/blogs/article/google_social_a_paradox_of_its_own/</guid>
	<link>http://www.process-one.net/en/blogs/article/google_social_a_paradox_of_its_own/</link>
	<description>&lt;p&gt;Yesterday was a sad day for the Internet, as an open and decentralized content repository.&lt;/p&gt; &lt;p align=&quot;center&quot;&gt;&lt;img alt=&quot;image&quot; height=&quot;375&quot; src=&quot;http://www.process-one.net/images/uploads/internet_open.jpg&quot; width=&quot;500&quot; /&gt;&lt;/p&gt;
&lt;p&gt;At the same moment, Twitter launch its follow button and Google extend its +1 button for websites. They both are attempting to catch up with Facebook success with the &quot;like button&quot;.&lt;/p&gt;
&lt;p&gt;Of course, it feels ridiculous for content producers and consumers to expect to place more buttons on their pages in the hope to attract more traffic. The escalation in the battle for embedding buttons in web pages and the battle to get the users clicks is leading nowhere.&lt;/p&gt;
&lt;p&gt;I would rather however focus on Google move, the paradox and possibly the trap it could become for the company. I have read numerous times that Google initiative in &quot;social&quot; area had always been a failure. Even &lt;a href=&quot;http://www.guardian.co.uk/technology/2011/jun/01/google-missed-out-on-the-friends-thing-eric-chmidt&quot;&gt;Eric Schmidt admitted at D9 conference that he had messed up on Google Social initiative&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;In my opinion, the main reason for failure is because social, as defined by today's market, is the complete opposite of Google model and view of the online world.&lt;/p&gt;
&lt;p&gt;&quot;Like buttons&quot; are replacements for the HTML links that authors used to place on their webpage to talk about topics they liked and point to relevant content. But as HTML links were purely open and available for search engine web spiders, the end result of the &quot;like button&quot; action is closed and proprietary. Facebook and Twitter are one of the biggest threat for Google: they are cutting Google supply of their raw material: the data to analyze, both content and relations (links).&lt;/p&gt;
&lt;p&gt;Google reactions yesterday has been to launch its own initiative, its own &quot;like button&quot; to gather its own data. However, doing so Google enter the trap of the closed Internet, in which they cannot prosper.&lt;/p&gt;
&lt;p&gt;Google's model has been to compete on algorithm, not data. They have beaten all other search engine using the same data set than anyone could use. They hire smart scientists to produce clever mathematical solution to the world data analysis problem. Algorithm competition is their reason to exist: organizing the world data, not owning it.&lt;/p&gt;
&lt;p&gt;Launching a &quot;like button&quot; is admitting that they were wrong in their core vision. Can the world be organized automatically by algorithm, from news to search ? It is an impossible equation to solve for them without deeper changes in Google's vision.&amp;nbsp;In other words, the social graph that Facebook wants to build is actually what the web is about, and should be at the heart of the open web. Copying that closed vision of the web is a mistake.&lt;/p&gt;
&lt;p&gt;What happen today looks like Yahoo!'s revenge (even if Yahoo! company is now out of the game): curation is the return of directories of content, with a larger number of curators and ranking depending on the proximity of the curators to yourself.&lt;/p&gt;
&lt;p&gt;Google simply cannot win with this definition of the &quot;social web&quot;. If social is about building competing subsets of Internet, defined by their user base, Google will decline on search, their core business. They cannot remain the entry point on the Internet for a large number of users, because they could not be able to bring them on every place you might want to go on the web.&lt;/p&gt;
&lt;p&gt;The solution for Google requires to change the view of the social web, not to build their own clone of social network. Google social should be about putting people at the heart of an open Internet, where companies keeps on competing on algorithms. They already have valuable tools to rely on: google reader can share publicly what you like to read, blogger to publish your thought. They can build &quot;Google People&quot; around them.&lt;/p&gt;
&lt;p&gt;I think that Google should keep on producing tools to make easy to share publicly what people would like to share, from status, to picture, though and links. This should be published in a well defined and documented way that will lead to a more open web. To be frank, such standards are emerging in many initiatives  (webfinger, salmon, foaf, ostatus, opensocial and many others). Google promotes or even design several of those web protocols.&lt;/p&gt;
&lt;p&gt;However, Google +1, until it leads to publicly available data on user profile, is a move in the wrong direction for Google. Let's hope for the Internet that their next move will open the data and bring the competition back to algorithms.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;Credits: Photo by &lt;a href=&quot;http://www.flickr.com/photos/balleyne/&quot;&gt;Blaise Alleyne&lt;/a&gt;&lt;/p&gt;</description>
	<pubDate>Thu, 02 Jun 2011 12:59:07 +0000</pubDate>
</item>
<item>
	<title>erlang.org RSS Feed: R14B03 released</title>
	<guid>http://www.erlang.org/news/18</guid>
	<link>http://www.erlang.org/news/18</link>
	<description>&lt;p&gt;&lt;p&gt;
	&lt;b&gt;Erlang/OTP R14B03&lt;/b&gt; has been released as planned on May 25:th 2011. It is the third R14 service release.&lt;/p&gt;
&lt;p&gt;
	See the release notes in the &lt;a href=&quot;http://www.erlang.org/download/otp_src_R14B03.readme&quot;&gt;readme file&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;
	Download the new release from the &lt;a href=&quot;http://www.erlang.org/download.html&quot;&gt;download page&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;
	&lt;strong&gt;Highlights:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
	&lt;li&gt;
		Diameter is a brand new application in this release. The application support the diameter protocol specified in RFC 3588 and is intended to provide an Authentication, Authorization and Accounting (AAA) framework for applications.&amp;nbsp;&lt;/li&gt;
	&lt;li&gt;
		The documentation for stdlib and kernel now uses type specifications from the source modules which should guarantee that the documentation and code are consistent with regard to the type information.&lt;/li&gt;
&lt;/ul&gt;
&lt;/p&gt;</description>
	<pubDate>Wed, 25 May 2011 16:00:00 +0000</pubDate>
</item>
<item>
	<title>Erlang Factory News: Countdown to the Erlang Factory London!</title>
	<guid>http://www.erlang-factory.com/news/list#60</guid>
	<link>http://www.erlang-factory.com/news/list#60</link>
	<description>&lt;p&gt;There are&lt;span&gt; only two weeks left&lt;/span&gt; to the Erlang Factory London. If you haven't registered yet, you can still do this by clicking &lt;a href=&quot;https://www.erlang-factory.com/conference/London2011/register&quot;&gt;here&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;We have confirmed speakers including &lt;span&gt;Robert Virding and Mike Williams &lt;/span&gt;(Inventors of Erlang),&lt;span&gt; Tony Falco&lt;/span&gt; (COO of Basho Technologies), &lt;span&gt;John Hughes&lt;/span&gt; (Inventor of QuickCheck) and &lt;span&gt;Eric Merritt&lt;/span&gt; (Author of Erlang and OTP in Action). Many more are listed on the &lt;a href=&quot;http://www.erlang-factory.com/conference/London2011/speakers&quot;&gt;Speakers Page&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;You are welcome to follow &lt;a href=&quot;http://twitter.com/erlangfactory&quot;&gt;@erlangfactory&lt;/a&gt; on Twitter.&lt;/p&gt;</description>
	<pubDate>Wed, 25 May 2011 09:01:32 +0000</pubDate>
</item>
<item>
	<title>Programming in the 21st Century: Constantly Create</title>
	<guid>http://prog21.dadgum.com/99.html</guid>
	<link>http://prog21.dadgum.com/99.html</link>
	<description>When I wrote &lt;a href=&quot;http://prog21.dadgum.com/58.html&quot;&gt;Flickr as a Business Simulator&lt;/a&gt;, I was thinking purely about making a product--photos--and getting immediate feedback from a real audience. Seeing how much effort it takes to build-up a following. Learning if what you think people will like and what they actually like are the same thing.
&lt;br /&gt;&lt;br /&gt;It works just as well for learning what it's like to be in any kind of creative profession, such as an author of fiction or a recording artist.
&lt;br /&gt;&lt;br /&gt;Go look at music reviews on Amazon, and you'll see people puzzling over why a band's latest release doesn't have the spark of their earlier material, pointing out filler songs on albums, complaining about inconsistency between tracks. Sometimes the criticisms are empty, but there's often a ring of truth. There's an underlying question of &lt;i&gt;why&lt;/i&gt;. How could a songwriter or band release material that isn't always at the pinnacle of perfection?
&lt;br /&gt;&lt;br /&gt;After years of posting photos to Flickr, I get it. I'm just going along, taking my odd photographs, when all of a sudden one resonates and breaks through and I watch the view numbers jump way up. Then I've got pressure: How can I follow that up? Sometimes I do, with a couple of winners in a row, but inevitably I can't stay at that level. Sometimes I take a break, not posting shots for a month or more, and then I lose all momentum.
&lt;br /&gt;&lt;br /&gt;When I'm at a low point, when I devolve into taking pictures of mundane subjects, pictures I know aren't good, I think about how I'm ever going to get out of that rut. Inevitably I do, though it's often a surprise when I go from a forgettable photo one day to something inspired the next.
&lt;br /&gt;&lt;br /&gt;The key for me is to keep going, to keep taking and posting photos. If I get all perfectionist then there's too much pressure, and I start second-guessing myself. If I give up when my quality drops off, then that's not solving anything. The steady progress of continual output, whether good or bad output, is part of the overall creative process.</description>
	<pubDate>Sun, 22 May 2011 06:00:00 +0000</pubDate>
</item>
<item>
	<title>Erlang Factory News: Early Bird Rate - Book before 22 May to save £100</title>
	<guid>http://www.erlang-factory.com/news/list#59</guid>
	<link>http://www.erlang-factory.com/news/list#59</link>
	<description>&lt;p&gt;&lt;a href=&quot;https://www.erlang-factory.com/conference/London2011/register&quot;&gt;Book&lt;/a&gt; your place now at the &lt;a href=&quot;http://www.erlang-factory.com/conference/London2011&quot;&gt;Erlang Factory London 2011&lt;/a&gt; at the Early Bird Rate of &amp;pound;495 for the conference and &amp;pound;1295 for the &lt;a href=&quot;http://www.erlang-factory.com/conference/London2011/university&quot;&gt;University&lt;/a&gt; and conference. This is a saving of &amp;pound;100 off the standard price of the conference!Only valid until the 22 May!&lt;/p&gt;
&lt;p&gt;Speakers so far include , &lt;a href=&quot;http://www.erlang-factory.com/conference/London2011/speakers/KostisSagonas&quot;&gt;Kostis Sagonas&lt;/a&gt;, the leader of the HIPE Team at Uppsala University, &lt;a href=&quot;http://www.erlang-factory.com/conference/London2011/speakers/MarkusKern&quot;&gt;Marcus Kern&lt;/a&gt;, CTO at MIG, &lt;a href=&quot;http://www.erlang-factory.com/conference/London2011/speakers/SteveVinoski&quot;&gt;Steve Vinosk&lt;/a&gt;i, distributed systems expert and &lt;a href=&quot;http://www.erlang-factory.com/conference/London2011/speakers/ScottLystigFritchie&quot;&gt;Scott Lystig Fritchie&lt;/a&gt;, senior software engineer at Basho Technologies. Many more speakers are still to be announced.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://www.erlang-factory.com/conference/London2011/register&quot;&gt;Book now to secure this fantastic rate!&lt;/a&gt;&lt;/p&gt;</description>
	<pubDate>Wed, 18 May 2011 09:16:37 +0000</pubDate>
</item>
<item>
	<title>Trapexit's Erlang Blog Filter: More Erlang Web Server Benchmarking</title>
	<guid>http://steve.vinoski.net/blog/?p=832</guid>
	<link>http://steve.vinoski.net/blog/2011/05/18/more-erlang-web-server-benchmarking/</link>
	<description>&lt;p&gt;In my &lt;a rel=&quot;nofollow&quot;&gt;previous blog entry&lt;/a&gt; I questioned the value of most web server benchmarking, particularly as related to Erlang. Typical benchmarks are misleading, inaccurate, and poorly executed. Perhaps worse, the intent of publishing them seems to be to assert that the &lt;em&gt;fastest&lt;/em&gt; web server (at least according to the tests performed) is of course also the &lt;em&gt;best&lt;/em&gt; web server. You&amp;#8217;d think the flaws of this fallacy would be so obvious that nobody would fall for it, but think again: watching the &lt;a rel=&quot;nofollow&quot; target=&quot;_blank&quot; href=&quot;http://www.delicious.com/tag/erlang&quot;&gt;delicious &amp;#8220;erlang&amp;#8221; tag&lt;/a&gt; over the past few days revealed the &lt;a rel=&quot;nofollow&quot; target=&quot;_blank&quot; href=&quot;http://www.ostinelli.net/a-comparison-between-misultin-mochiweb-cowboy-nodejs-and-tornadoweb/&quot;&gt;benchmarks my blog post referred to&lt;/a&gt; to be one of the most bookmarked Erlang-related pages during that timeframe.&lt;/p&gt;
&lt;p&gt;Not surprisingly, though, it looks like I&amp;#8217;m not the only one bothered by poor benchmarking practices. Over on his blog, &lt;a rel=&quot;nofollow&quot; target=&quot;_blank&quot; href=&quot;http://www.mnot.net/&quot;&gt;Mark Nottingham&lt;/a&gt; just published &lt;a rel=&quot;nofollow&quot; target=&quot;_blank&quot; href=&quot;http://www.mnot.net/blog/2011/05/18/http_benchmark_rules&quot;&gt;a brilliant set of rules for HTTP load testing&lt;/a&gt;. It&amp;#8217;s quite instructive to take your favorite set of published web server benchmarks and see just how many of Mark&amp;#8217;s rules they violate.&lt;/p&gt;
&lt;p&gt;Like I hinted last time, if you want benchmarks, you are best off by far if you run them yourself. That way, their relevance to the problems you&amp;#8217;re addressing will be much more likely, and you can run them in a similar, or even the same, environment on which you plan to deploy. You can also gear the benchmarks to much more closely resemble your applications and the loads you require them to handle. Doing the benchmarking work yourself will give you valuable hands-on experience with the servers and frameworks you&amp;#8217;re considering, allowing you to get a feel for important factors such as feature completeness and correctness, ease of development, flexibility, and ease of deployment and runtime management/monitoring, none of which can be gauged by someone else&amp;#8217;s performance benchmarks. Finally, by doing your own benchmarking you can also help ensure the validity and usefulness of your results by following Mark&amp;#8217;s load testing rules.&lt;/p&gt;</description>
	<pubDate>Wed, 18 May 2011 05:54:45 +0000</pubDate>
</item>
<item>
	<title>Erlang Inside: Motivated Reasoning and Erlang vs Python vs Node</title>
	<guid>http://erlanginside.com/?p=280</guid>
	<link>http://erlanginside.com/motivated-reasoning-and-erlang-vs-python-vs-node-280</link>
	<description>A comparison between Misultin, Mochiweb, Cowboy, Node.JS, and Tornado. Tests such as this one tend to focus on either raw parsing speed or total number of concurrent connections, and which is more important depends on the ultimate application. Or they perfectly tune the tester&amp;#8217;s favorite framework and use a stock configuration for the other competitors. [...]</description>
	<pubDate>Mon, 16 May 2011 15:51:08 +0000</pubDate>
</item>
<item>
	<title>Erlang Inside: Erlang in Haskell</title>
	<guid>http://erlanginside.com/?p=275</guid>
	<link>http://erlanginside.com/erlang-in-haskell-275</link>
	<description>Haskellers who want to experiment with the concepts of Erlang in Haskell can now do so with an experimental Erlang-like distributed computing framework called &amp;#8216;remote&amp;#8217;. It&amp;#8217;s quite basic but covers concurrency, selective receive of messages, local and global registration of processes, and trapping exits. Related Microsoft Research Paper.</description>
	<pubDate>Mon, 16 May 2011 03:49:18 +0000</pubDate>
</item>
<item>
	<title>Scattered Thoughts: mist</title>
	<guid>http://scattered-thoughts.net/one/1305/106859/184342</guid>
	<link>http://scattered-thoughts.net/one/1305/106859/184342</link>
	<description>&lt;p&gt;I have a new secret project. It's called Mist. The goal is to make it easy to build, use, distribute and maintain small-scale p2p apps. By small-scale I mean apps where each invocation has at most a few dozen users. You would use Mist to build the next etherpad, not the next bittorrent.
&lt;/p&gt;
&lt;p&gt;The main inspiration behind Mist is &lt;a href=&quot;http://one.laptop.org/about/software&quot;&gt;Sugar&lt;/a&gt;. Sugar supports seamless sharing of activities (applications) across local mesh networks or the internet, even if some of the peers don't even have the activity installed. Where Sugar is focused on providing a working system here and now, I am more interested in how distributed programming can be made easier in the future. I also want to build applications that I can use myself so my initial target is Ubuntu/Gnome.
&lt;/p&gt;
&lt;p&gt;Starting from September, I'm going to be doing just enough freelance work to support full time work on Mist. I'm aware that this is a huge project and probably doomed to failure so there a few key principles that I hope will keep me on track. Wherever possible I will reuse existing code and protocols. Mist will be built out of small components each of which are useful by themselves. Where practical Mist will be compatible with Sugar, so that any improvements can be folded back into the Sugar codebase. Finally, Mist is a prototype. The goal will be to get something up and running to experiment with different ways of building distributed apps.
&lt;/p&gt;
&lt;p&gt;So without further ado here is a misty outline of what Mist might one day look like:
&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;Seamless connectivity, serverless presence and activity discovery
    &lt;/p&gt;
    &lt;ul&gt;
      &lt;li&gt;
        &lt;p&gt;Mostly borrowed from Sugar
        &lt;/p&gt;
      &lt;/li&gt;
      &lt;li&gt;
        &lt;p&gt;Apps communicate over &lt;a href=&quot;http://telepathy.freedesktop.org/wiki/&quot;&gt;Telepathy&lt;/a&gt; tubes
        &lt;/p&gt;
      &lt;/li&gt;
      &lt;li&gt;
        &lt;p&gt;Use Sugar &lt;a href=&quot;http://wiki.laptop.org/go/Presence_Service&quot;&gt;presence service&lt;/a&gt; for activity discovery
        &lt;/p&gt;
      &lt;/li&gt;
      &lt;li&gt;
        &lt;p&gt;LAN / ad-hoc networks supported by &lt;a href=&quot;http://wiki.laptop.org/go/Telepathy_Salut&quot;&gt;Telepathy Salut&lt;/a&gt; and &lt;a href=&quot;http://en.wikipedia.org/wiki/Avahi&quot;&gt;Avahi&lt;/a&gt;
        &lt;/p&gt;
      &lt;/li&gt;
      &lt;li&gt;
        &lt;p&gt;Centralized communication supported using &lt;a href=&quot;http://wiki.laptop.org/go/Telepathy_Gabble&quot;&gt;Telepathy Gabble&lt;/a&gt; to talk to Jabber servers
        &lt;/p&gt;
      &lt;/li&gt;
      &lt;li&gt;
        &lt;p&gt;Decentralized communication supported using a Telepathy plugin for &lt;a href=&quot;https://github.com/jamii/erl-telehash&quot;&gt;erl-telehash&lt;/a&gt;
        &lt;/p&gt;
        &lt;ul&gt;
          &lt;li&gt;
            &lt;p&gt;Use telehash peers for &lt;a href=&quot;http://en.wikipedia.org/wiki/Interactive_Connectivity_Establishment&quot;&gt;ICE&lt;/a&gt;
            &lt;/p&gt;
          &lt;/li&gt;
          &lt;li&gt;
            &lt;p&gt;Use link-local XMMP to talk
            &lt;/p&gt;
          &lt;/li&gt;
          &lt;li&gt;
            &lt;p&gt;Use telehash taps to implement Avahi compatible mDNS
            &lt;/p&gt;
          &lt;/li&gt;
        &lt;/ul&gt;
      &lt;/li&gt;
      &lt;li&gt;
        &lt;p&gt;Use pgp for authentication and end-to-end encryption
        &lt;/p&gt;
      &lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Securely portable applications
    &lt;/p&gt;
    &lt;ul&gt;
      &lt;li&gt;
        &lt;p&gt;Use an existing system. Some options which will need to be researched:
        &lt;/p&gt;
        &lt;ul&gt;
          &lt;li&gt;
            &lt;p&gt;&lt;a href=&quot;http://wiki.laptop.org/go/Bitfrost&quot;&gt;Bitfrost&lt;/a&gt; is specific to the XO laptop and probably too hard to port
            &lt;/p&gt;
          &lt;/li&gt;
          &lt;li&gt;
            &lt;p&gt;Browsers provide well-tested sandboxes and mobile code but limit access to hardware. Perhaps ChromeOS or Firefox Chromeless?
            &lt;/p&gt;
          &lt;/li&gt;
          &lt;li&gt;
            &lt;p&gt;Java apps can possibly be installed securely. Looking at the Android source may be instructive.
            &lt;/p&gt;
          &lt;/li&gt;
        &lt;/ul&gt;
      &lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Development tools for distributed apps
    &lt;/p&gt;
    &lt;ul&gt;
      &lt;li&gt;
        &lt;p&gt;Want to experiment with &lt;a href=&quot;http://www.bloom-lang.net/&quot;&gt;Bloom&lt;/a&gt;. I have some ideas for static analysis of bloom programs that might be fun.
        &lt;/p&gt;
      &lt;/li&gt;
      &lt;li&gt;
        &lt;p&gt;Expose libraries/APIs for common patterns eg leader election, operational transform
        &lt;/p&gt;
      &lt;/li&gt;
      &lt;li&gt;
        &lt;p&gt;Support serverless syncing of contacts, bookmarks, mail etc using DHT with enforced reciprocal storage (pretty sure I've read a paper on this somewhere)
        &lt;/p&gt;
      &lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The immediate plan for the next few months (during which I'm still working full time) is:
&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;Get erl-telehash working (currently has no test suite and doesn't support taps, _ring/_line or rate limiting)
    &lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Get ICE working between telehash peers
    &lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Write a Telepathy backend using erl-telehash and link-local XMPP
    &lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Extend Empathy to use the telehash Telepathy backend
    &lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If you are wondering where Mist gets its name from - it's not the cloud.
&lt;/p&gt;</description>
	<pubDate>Wed, 11 May 2011 09:40:59 +0000</pubDate>
</item>
<item>
	<title>Trapexit's Erlang Blog Filter: Erlang Web Server Benchmarking</title>
	<guid>http://steve.vinoski.net/blog/?p=780</guid>
	<link>http://steve.vinoski.net/blog/2011/05/09/erlang-web-server-benchmarking/</link>
	<description>&lt;p&gt;Over on his blog, &lt;a rel=&quot;nofollow&quot; target=&quot;_blank&quot; href=&quot;http://www.ostinelli.net/&quot;&gt;Roberto Ostinelli&lt;/a&gt; published &lt;a rel=&quot;nofollow&quot; target=&quot;_blank&quot; href=&quot;http://www.ostinelli.net/a-comparison-between-misultin-mochiweb-cowboy-nodejs-and-tornadoweb/&quot;&gt;&amp;#8220;A comparison between Misultin, Mochiweb, Cowboy, NodeJS and Tornadoweb.&amp;#8221;&lt;/a&gt; I was going to write a reply comment there, but it got pretty long so I decided to publish it here instead. I&amp;#8217;m going to ignore the non-Erlang web servers it discusses and focus entirely on Erlang. I&amp;#8217;m not trying to really pick specifically on Roberto here, but rather I decided to finally write something I&amp;#8217;ve been meaning to write for awhile now about Erlang web servers and benchmarking.&lt;/p&gt;
&lt;p&gt;First, I second the &lt;a rel=&quot;nofollow&quot; target=&quot;_blank&quot; href=&quot;http://www.ostinelli.net/a-comparison-between-misultin-mochiweb-cowboy-nodejs-and-tornadoweb/#comment-32413&quot;&gt;request made by one commenter&lt;/a&gt; for including &lt;a rel=&quot;nofollow&quot; target=&quot;_blank&quot; href=&quot;https://github.com/klacke/yaws&quot;&gt;Yaws&lt;/a&gt; in the measurements. Roberto, if you need help with the code or setup, just let me know. If one insists on writing these kinds of benchmarks, which as you&amp;#8217;ll learn if you read this whole entry is something I question, the least he or she could do is include Yaws since it&amp;#8217;s the granddaddy of all Erlang web servers.&lt;/p&gt;
&lt;p&gt;Based on the benchmark code Roberto published, I wrote the following simple Yaws module to conform to the problem statement and registered it in my Yaws configuration as a &amp;#8220;/&amp;#8221; &lt;a rel=&quot;nofollow&quot; target=&quot;_blank&quot; href=&quot;http://yaws.hyber.org/appmods.yaws&quot;&gt;appmod&lt;/a&gt;:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;
&lt;pre&gt;-module(yaws_bench).
-export([out/1]). out(Arg) -&amp;gt; [{status, 200}, {content, &quot;text/xml&quot;, case yaws_api:queryvar(Arg, &quot;value&quot;) of {ok, Value} -&amp;gt; [&quot;&amp;lt;http_test&amp;gt;&amp;lt;value&amp;gt;&quot;, Value, &quot;&amp;lt;/value&amp;gt;&amp;lt;/http_test&amp;gt;&quot;]; _ -&amp;gt; &quot;&amp;lt;http_test&amp;gt;&amp;lt;error&amp;gt;no value specified&amp;lt;/error&amp;gt;&amp;lt;/http_test&amp;gt;&quot; end}].&lt;/pre&gt;
&lt;p&gt;&lt;/p&gt;&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;I then measured it on my Ubuntu 10.10 two-core system using Roberto&amp;#8217;s published &lt;code&gt;httperf&lt;/code&gt; command against the &lt;a rel=&quot;nofollow&quot; target=&quot;_blank&quot; href=&quot;https://github.com/ostinelli/misultin&quot;&gt;misultin&lt;/a&gt; and &lt;a rel=&quot;nofollow&quot; target=&quot;_blank&quot; href=&quot;https://github.com/mochi/mochiweb&quot;&gt;Mochiweb&lt;/a&gt; code he published, and found that Yaws definitely holds its own, even though it&amp;#8217;s a full-featured web server and does not claim to be just a lightweight library offering (sometimes partial) HTTP support as some frameworks do. For some tests Yaws outperforms misultin, and for others it doesn&amp;#8217;t. This is interesting, considering that neither Klacke nor I have made any attempts at performance improvements in Yaws recently.&lt;/p&gt;
&lt;p&gt;Second, the benchmarks do not compare apples to apples. Both Mochiweb and Yaws, for example, produce replies that are larger in size than misultin&amp;#8217;s replies, primarily because they both include &lt;code&gt;Server&lt;/code&gt; and &lt;code&gt;Date&lt;/code&gt; headers. As I&amp;#8217;ve learned from years of helping maintain Yaws, date calculations can noticeably and surprisingly impact Erlang web server performance, yet simply leaving &lt;code&gt;Date&lt;/code&gt; headers out isn&amp;#8217;t an option for real-world apps since HTTP 1.1 pretty much requires them (&lt;a rel=&quot;nofollow&quot; target=&quot;_blank&quot; href=&quot;http://www.w3.org/Protocols/rfc2616/rfc2616-sec13.html#sec13.2.3&quot;&gt;section 13.2.3&lt;/a&gt; of &lt;a rel=&quot;nofollow&quot; target=&quot;_blank&quot; href=&quot;http://www.w3.org/Protocols/rfc2616/rfc2616.html&quot;&gt;RFC 2616&lt;/a&gt; states, &lt;em&gt;&amp;#8220;HTTP/1.1 requires origin servers to send a Date header, if possible, with every response, giving the time at which the response was generated&amp;#8230;&amp;#8221;&lt;/em&gt;). Caches use &lt;code&gt;Date&lt;/code&gt; headers for several reasons, for example in the absence of cache-control headers to help heuristically calculate content expiration. Even ignoring the date calculation requirements, just creating and delivering larger replies due to the presence of the &lt;code&gt;Server&lt;/code&gt; header will negatively impact any comparisons based on request/second measurements.&lt;/p&gt;
&lt;p&gt;Third, the benchmarking approach includes no application &amp;#8220;think time.&amp;#8221; How many real-world apps just blast request after request down a connection without any intervening time to handle replies? If the goal is to measure something akin to real-world apps, then the benchmarks should at least be using something like httperf&amp;#8217;s &lt;code&gt;--wsess&lt;/code&gt; option to simulate client think time. And unfortunately doing that is hard to get right for generic benchmarks, since different client apps will have different think times.&lt;/p&gt;
&lt;p&gt;On a related note, what exactly &lt;em&gt;is&lt;/em&gt; the goal of these benchmarks? To imply that faster is better? That&amp;#8217;s unfortunately a commonly-held fallacy. Given that the blog entry states that the target is dynamic applications, then consider the fact that the performance of a real-world dynamic application is often dominated by something other than the web server &amp;mdash; perhaps some back-end service from which page data is being fetched, for example. A real-world setup greatly concerned with performance is likely to have &lt;a rel=&quot;nofollow&quot; target=&quot;_blank&quot; href=&quot;http://nginx.net/&quot;&gt;nginx&lt;/a&gt; out in front, probably with a local cache, to handle fast-path requests, shunting only those requests it can&amp;#8217;t fulfill off to the slower back-end server. Such benchmarking games are therefore often misguided as far as real-world dynamic apps are concerned because they end up measuring something that isn&amp;#8217;t even in the critical path in a real setup.&lt;/p&gt;
&lt;p&gt;I don&amp;#8217;t agree with &lt;a rel=&quot;nofollow&quot; target=&quot;_blank&quot; href=&quot;http://www.ostinelli.net/a-comparison-between-misultin-mochiweb-cowboy-nodejs-and-tornadoweb/#comment-32432&quot;&gt;Kyle Drake&amp;#8217;s comment&lt;/a&gt; on Roberto&amp;#8217;s blog about code ugliness, since the Erlang code posted there is very clear and would look like &amp;#8220;garbage&amp;#8221; only to someone who doesn&amp;#8217;t know the language. But I do agree with the sentiment, which is that for dynamic apps, what often matters is what kind of code, and how much, you have to write and maintain to support your app. Given that Erlang web servers tend to make use of the underlying Erlang/OTP facilities for &lt;a rel=&quot;nofollow&quot; target=&quot;_blank&quot; href=&quot;http://erlang.org/doc/man/erlang.html#decode_packet-3&quot;&gt;HTTP parsing&lt;/a&gt; and &lt;a rel=&quot;nofollow&quot; target=&quot;_blank&quot; href=&quot;http://erlang.org/doc/man/gen_tcp.html&quot;&gt;socket handling&lt;/a&gt;, then all things considered you&amp;#8217;re just not going to get a huge variation in performance among them, assuming they&amp;#8217;re written halfway decently. What matters for dynamic apps are the stability of the web server/library and the programming model it offers. These are what Roberto should really be benchmarking, but of course that&amp;#8217;s basically impossible since stability would take a long time to prove, and programming model is a matter of taste that can&amp;#8217;t be conveniently measured using artificial benchmarking tools. This reminds me of one of my old columns on this very issue as applied to enterprise middleware, entitled &amp;#8220;&lt;a rel=&quot;nofollow&quot;&gt;The Performance Presumption&lt;/a&gt;&amp;#8221; (PDF); the short version is that people often measure performance simply because performance is relatively easy to measure. The lesson is that you shouldn&amp;#8217;t rely on generic benchmarks, but rather you should take the time to create specific benchmarks that mimic the app you want to develop, and base your decisions on the results of that exercise.&lt;/p&gt;
&lt;p&gt;On top of all that, I don&amp;#8217;t really understand the desire to keep writing new Erlang web frameworks for performance reasons. As I stated earlier, if a framework uses Erlang&amp;#8217;s built-in packet decoding and socket handling, it won&amp;#8217;t perform a great deal better than any other Erlang web framework. OTOH, if someone writes a new framework with the hope of providing a really nice new programming model &amp;mdash; &lt;a rel=&quot;nofollow&quot; target=&quot;_blank&quot; href=&quot;https://github.com/basho/webmachine&quot;&gt;webmachine&lt;/a&gt; is a fantastic example of this &amp;mdash; then they shouldn&amp;#8217;t be &amp;#8220;proving&amp;#8221; how good the programming model is by trying to show how fast it is. Ever seen webmachine being advertised via performance benchmarks? Neither have I.&lt;/p&gt;
&lt;p&gt;Let&amp;#8217;s face it, the Erlang web development community isn&amp;#8217;t large enough to support numerous web servers and frameworks. I&amp;#8217;m sure some will disagree, but publishing artificial benchmarks designed to &amp;#8220;prove&amp;#8221; which is best IMO results mostly in just fragmenting the community. If you really have an itch to write a fast Erlang web server, you&amp;#8217;d help the community much more by contributing to an existing one, including the Erlang &lt;a rel=&quot;nofollow&quot; target=&quot;_blank&quot; href=&quot;http://erlang.org/doc/man/inets.html&quot;&gt;inets&lt;/a&gt; web server included in Erlang/OTP and now powering &lt;a rel=&quot;nofollow&quot; target=&quot;_blank&quot; href=&quot;http://erlang.org/&quot;&gt;the Erlang website&lt;/a&gt;. For Yaws, Klacke and I often take patches and suggestions from our users, and we gladly welcome solid contributions intended to improve Yaws performance. If you&amp;#8217;re just dying to show off your chops, note that improving performance in a long-lived and highly stable codebase like Yaws without breaking anyone&amp;#8217;s code is far more challenging than writing another new server that basically doesn&amp;#8217;t differ much from what already exists.&lt;/p&gt;
&lt;p&gt;Or perhaps better yet, contribute to the Erlang core. IMO the next major performance improvements in Erlang web servers will come not from minor tweaks in handling binaries or such things, but rather via radical improvements in the &lt;a rel=&quot;nofollow&quot; target=&quot;_blank&quot; href=&quot;https://github.com/erlang/otp/blob/dev/erts/emulator/drivers/common/inet_drv.c&quot;&gt;Erlang TCP driver&lt;/a&gt; or even from developing a whole new HTTP-specific driver. Unlike a war of artificial benchmarks among Erlang web servers, these approaches have a great chance to improve the lot of all Erlang web systems.&lt;/p&gt;</description>
	<pubDate>Tue, 10 May 2011 02:01:01 +0000</pubDate>
</item>
<item>
	<title>erlang.org RSS Feed: ACM SIGPLAN Erlang Workshop in Tokyo on September 23, 2011</title>
	<guid>http://www.erlang.org/news/17</guid>
	<link>http://www.erlang.org/news/17</link>
	<description>&lt;p&gt;&lt;p&gt;
	&lt;strong&gt;ACM SIGPLAN Erlang Workshop &lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;
	The Tenth ACM SIGPLAN Erlang Workshop will take place in Tokyo, Japan, on September 23, 2011. Please see the call for papers &lt;a href=&quot;http://www.erlang.org/workshop/2011/&quot;&gt;here&lt;/a&gt; .&lt;/p&gt;
&lt;/p&gt;</description>
	<pubDate>Fri, 06 May 2011 23:08:27 +0000</pubDate>
</item>
<item>
	<title>Erlang Factory News: Book your place at the Erlang Factory London Now and save £100!</title>
	<guid>http://www.erlang-factory.com/news/list#58</guid>
	<link>http://www.erlang-factory.com/news/list#58</link>
	<description>&lt;p&gt;&lt;a href=&quot;https://www.erlang-factory.com/conference/London2011/register&quot;&gt;Book&lt;/a&gt; your place now at the &lt;a href=&quot;http://www.erlang-factory.com/conference/London2011&quot;&gt;Erlang Factory London 2011&lt;/a&gt; at the Early Bird Rate of &amp;pound;495 for the conference and &amp;pound;1295 for the &lt;a href=&quot;http://www.erlang-factory.com/conference/London2011/university&quot;&gt;University&lt;/a&gt; and conference. This is a saving of &amp;pound;100 off the standard price of the conference! This is only available for a limited time!&lt;/p&gt;
&lt;p&gt;Speakers so far include , &lt;a href=&quot;http://www.erlang-factory.com/conference/London2011/speakers/KostisSagonas&quot;&gt;Kostis Sagonas&lt;/a&gt;, the leader of the HIPE Team at Uppsala University, &lt;a href=&quot;http://www.erlang-factory.com/conference/London2011/speakers/MarkusKern&quot;&gt;Marcus Kern&lt;/a&gt;, CTO at MIG, &lt;a href=&quot;http://www.erlang-factory.com/conference/London2011/speakers/SteveVinoski&quot;&gt;Steve Vinosk&lt;/a&gt;i, distributed systems expert and &lt;a href=&quot;http://www.erlang-factory.com/conference/London2011/speakers/ScottLystigFritchie&quot;&gt;Scott Lystig Fritchie&lt;/a&gt;, senior software engineer at Basho Technologies. Many more speakers are still to be announced.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://www.erlang-factory.com/conference/London2011/register&quot;&gt;Book now to secure this fantastic rate!&lt;/a&gt;&lt;/p&gt;</description>
	<pubDate>Wed, 04 May 2011 10:31:39 +0000</pubDate>
</item>
<item>
	<title>Anders Conbere's Journal: Learning to Program is Easy</title>
	<guid>tag:anders.conbere.org,2011-05-03:1304473332.0</guid>
	<link>http://anders.conbere.org/blog/2011/05/03/learning_to_program_is_easy</link>
	<description>&lt;p&gt;Years ago I spent a summer teaching science classes to kids aged 6 to 12 at the Oregon Museum of Science and Industry. This program wasn't particularly well managed, mostly a disaster, but quite a bit of fun. The most fun I had was helping teach a class on game programming to 5th and 6th graders. The class used a pretty nice implementation of Logo called MicroWorlds which benefited from having a simple GUI and IDE for the kids to use. Maybe today we'd use something like &lt;a href=&quot;http://scratch.mit.edu/&quot;&gt;Scratch&lt;/a&gt; or &lt;a href=&quot;http://www.squeakland.org/&quot;&gt;EToys&lt;/a&gt;. Either way the class began with a simple introduction to making things happen in this virtual world, and some of the basics of the command structure. And within minutes there was a volley of hands that shot into the air each with it's very own bug.&lt;/p&gt;
&lt;p&gt;That might not sounds like much, but by the time you've engaged someone to the point that they've typed in some commands and arrived at an unexpected occurrence... you've basically got them hook line and sinker. So that day and some of the next was often spent detailing the use of variables, the intricacies of syntax, and what rules exist and why they make their life easier. By the end of the week we would have flies narrowly dodging venus fly traps, and robots that had to defeat monsters.&lt;/p&gt;
&lt;p&gt;I'm not saying every kid produced a work of genius, and some had more difficulties than others. But the nature of programming does not have to be a complex and difficult venture. Simply mapping input to a behavior gets you pretty far. This is a big part of the idea of Scheme. How do we build a language that is simple enough that our english majors can accomplish something in it, but powerful enough that our computer science students can develop complex ideas with it. The point is that most people can be taught to create at least rudimentary programs in just a few hours of tutorial.&lt;/p&gt;
&lt;p&gt;And this really brings me to the reason I'm writing this post. And that is, that while programming might be easy, making applications, architecting large projects, putting all the various pieces together in a way that gives you the flexibility to accomplish what you &lt;em&gt;might&lt;/em&gt; need to, but the structure to finishing what you &lt;em&gt;have&lt;/em&gt; to, is amazingly difficult. And two different skills. You can teach someone to use python like a calculator, and to write functions that help them manage their bills. But the ability to take that knowledge and expand upon it to write a personal finance applications is much more difficult to teach.&lt;/p&gt;
&lt;p&gt;Some of this is abstraction. Abstraction is difficult for humans, and I say this knowing that of all the creatures we've ever encountered we're likely the most capable abstractors in existence. But while that might be true, or brains are wired to work inside a rather constrained and physical world. There's a test that's often mentioned in literature where they ask the participants to accomplish a mathematical task using variable names, and then ask it again relating it to cards. In the first case most participants answer incorrectly, while in the next they do it with ease. The great difficult of mathematics as well as programming arrises from the depths of abstraction.&lt;/p&gt;
&lt;p&gt;Just like Mathematics might be the deep study one abstract layer after another, complex application design mirrors this. While in mathematics at the bottom might be something like Category Theory from which derives Algebra from which derive the infinity of number systems from which derive Real and Complex numbers from which derive Real Analysis and then the Calculuses and on down the chain. A Complex application might have a User Interface which abstracts a Programable Interface which calls down into a Data Layer that then talks to a Persistence Layer that talks to the Operating System on down to the bare metal. Most of the extremely competent application architects I know might be considered a software version of a &quot;tall, thin man&quot;. Someone who is familiar with all the ramifications of his design from the top layers to the cost at the bottom layers.&lt;/p&gt;
&lt;p&gt;What I'm trying to say is that Programming might be easy. But the understanding of both the ramifications of the current environment (what can be done with the current language and tools) and the cost of the various layers below it, is hard. And that teaching THAT is much more difficult task than teaching someone how to use a fancy calculator. Books like SICP attempt to accomplish this task by dealing with abstraction in it's own right, by merging programing and mathematics and enabling a kind of cross pollination there of. But even that can leave a student with little understanding of how to put all the pieces together. I write this because my focus since college has been into the art of programming. And I'm beginning to understand where the limitations of my studies have been, and where I'm headed. And my new goal of trying to find more people to look at my code and find new ways of expressing those problems.&lt;/p&gt;</description>
	<pubDate>Wed, 04 May 2011 01:42:12 +0000</pubDate>
</item>
<item>
	<title>Erlang Factory News: Only 5 Places left at the Erlang Factory London!</title>
	<guid>http://www.erlang-factory.com/news/list#57</guid>
	<link>http://www.erlang-factory.com/news/list#57</link>
	<description>&lt;p&gt;There are only 5 chances left to &lt;a href=&quot;https://www.erlang-factory.com/conference/London2011/register&quot;&gt;book&lt;/a&gt; your place at the &lt;a href=&quot;http://www.erlang-factory.com/conference/London2011&quot;&gt;Erlang Factory London 2011&lt;/a&gt; at the Very Early Bird Rate of &amp;pound;395 for the conference and &amp;pound;1195 for the &lt;a href=&quot;http://www.erlang-factory.com/conference/London2011/university&quot;&gt;University&lt;/a&gt; and conference. This is a saving of &amp;pound;200 off the standard price of the conference!When they are gone, they are gone!&lt;/p&gt;
&lt;p&gt;Speakers so far include , &lt;a href=&quot;http://www.erlang-factory.com/conference/London2011/speakers/KostisSagonas&quot;&gt;Kostis Sagonas&lt;/a&gt;, the leader of the HIPE Team at Uppsala University, &lt;a href=&quot;http://www.erlang-factory.com/conference/London2011/speakers/MarkusKern&quot;&gt;Marcus Kern&lt;/a&gt;, CTO at MIG, &lt;a href=&quot;http://www.erlang-factory.com/conference/London2011/speakers/SteveVinoski&quot;&gt;Steve Vinosk&lt;/a&gt;i, distributed systems expert and &lt;a href=&quot;http://www.erlang-factory.com/conference/London2011/speakers/ScottLystigFritchie&quot;&gt;Scott Lystig Fritchie&lt;/a&gt;, senior software engineer at Basho Technologies. Many more speakers are still to be announced.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://www.erlang-factory.com/conference/London2011/register&quot;&gt;Book now to secure this fantastic rate!&lt;/a&gt;&lt;/p&gt;</description>
	<pubDate>Tue, 03 May 2011 15:02:00 +0000</pubDate>
</item>
<item>
	<title>erlang.org RSS Feed: The Erlang Factory London is back!</title>
	<guid>http://www.erlang.org/news/16</guid>
	<link>http://www.erlang.org/news/16</link>
	<description>&lt;p&gt;&lt;p&gt;
	The Erlang Factory London is back! The dates you need for your diary are 6th, 7th and 8th June for the Erlang University courses and 9th and 10th June for the Erlang Factory Conference.&lt;br /&gt;
	&lt;br /&gt;
	There are 10 places left at the very Early bird rate of &amp;pound;395 which is a saving of &amp;pound;200! &lt;a href=&quot;http://www.erlang-factory.com/conference/London2011&quot;&gt;Book now&amp;nbsp;&lt;/a&gt; to get your place!&lt;br /&gt;
	&lt;br /&gt;
	&amp;nbsp;&lt;/p&gt;
&lt;/p&gt;</description>
	<pubDate>Tue, 03 May 2011 09:10:31 +0000</pubDate>
</item>
<item>
	<title>Programming in the 21st Century: Impressed by Slow Code</title>
	<guid>http://prog21.dadgum.com/98.html</guid>
	<link>http://prog21.dadgum.com/98.html</link>
	<description>At one time I was interested in--even enthralled by--low-level optimization.
&lt;br /&gt;&lt;br /&gt;Beautiful and clever tricks abound. Got a function call followed by a return statement? Replace the pair with a single jump instruction. Once you've realized that &quot;load effective address&quot; operations are actually doing math, then they can subsume short sequences of adds and shifts. On processors with fast &quot;count leading zero bits&quot; instructions, entire loops can be replaced with a couple of lines of linear code.
&lt;br /&gt;&lt;br /&gt;I spent a long time doing that before I realized it was a mechanical process.
&lt;br /&gt;&lt;br /&gt;I don't necessarily mean mechanical in the &quot;a good compiler can do the same thing&quot; sense, but that it's a raw engineering problem to take a function and make it faster. Take a simple routine that potentially loops through a lot of data, like a case insensitive string comparison. The first step is to get as many instructions out of the loop as possible. See if what remains can be rephrased using fewer or more efficient instructions. Can any of the calculations be replaced with a small table? Is there a way to process multiple elements at the same time using vector instructions?
&lt;br /&gt;&lt;br /&gt;The truth is that there's no magic in taking a well-understood, working function, analyzing it, and rewriting it in a way that involves doing slightly or even dramatically less work at run-time. If I ended up with a routine that was a bottleneck, I know I could take the time to make it faster. Or someone else could. Or if it was small enough I could post it to an assembly language programming forum and come back in a couple of days when the dust settled.
&lt;br /&gt;&lt;br /&gt;What's much more interesting is speeding up something complex, a program where all the time isn't going into a couple of obvious hotspots. 
&lt;br /&gt;&lt;br /&gt;All of a sudden, that view through the low-level magnifying glass is misleading. Yes, that's clearly an N-squared algorithm right there, but it may not matter at all. (It might only get called with with low values of N, for example.) This loop here contains many extraneous instructions, but that's hardly a big picture view. None of this helps with understanding the overall data flow, how much computation is really being done, and where the potential for simplification lies.
&lt;br /&gt;&lt;br /&gt;Working at that level, it makes sense to use a language that keeps you from thinking about exactly how your code maps to the underlying hardware. It can take a bit of faith to set aside deeply ingrained instincts about performance and concerns with low-level benchmarks, but I've seen Python programs that ended up faster than C. I've seen complex programs running under the Erlang virtual machine that are done executing before my finger is off the return key.
&lt;br /&gt;&lt;br /&gt;And that's what's impressive: code that is so easy to label as slow upon first glance, code containing functions that can--in isolation--be definitively proven to be dozens or hundreds of times slower than what's possible on a given CPU, and yet the overall program is decidedly one of high performance.
&lt;br /&gt;&lt;br /&gt;(If you liked this, you might enjoy &lt;a href=&quot;http://prog21.dadgum.com/35.html&quot;&gt;Timidity Does Not Convince&lt;/a&gt;.)</description>
	<pubDate>Sat, 30 Apr 2011 06:00:00 +0000</pubDate>
</item>
<item>
	<title>Learn You Some Erlang: Building an Application With OTP</title>
	<guid>http://learnyousomeerlang.com/building-applications-with-otp</guid>
	<link>http://learnyousomeerlang.com/building-applications-with-otp</link>
	<description>This chapter lets us make practical use of the OTP behaviours seen so far. We do this by writing a process pool application that will let us handle resources and tasks. We explore ideas behind process trees, onion layer theories for processes and general views of how OTP can be used to write software.</description>
	<pubDate>Fri, 29 Apr 2011 12:00:00 +0000</pubDate>
</item>
<item>
	<title>Erlang Factory News: Erlang Factory London Open for Registration - Save £200</title>
	<guid>http://www.erlang-factory.com/news/list#56</guid>
	<link>http://www.erlang-factory.com/news/list#56</link>
	<description>&lt;p&gt;Be one of the first 50 people to &lt;a href=&quot;https://www.erlang-factory.com/conference/London2011/register&quot;&gt;book&lt;/a&gt; your place at the &lt;a href=&quot;http://www.erlang-factory.com/conference/London2011&quot;&gt;Erlang Factory London 2011&lt;/a&gt; at the Very Early Bird Rate of &amp;pound;395 for the conference and &amp;pound;1195 for the &lt;a href=&quot;http://www.erlang-factory.com/conference/London2011/university&quot;&gt;University&lt;/a&gt; and conference. This is a saving of &amp;pound;200 off the standard price of the conference!&lt;/p&gt;
&lt;p&gt;Speakers so far include , &lt;a href=&quot;http://www.erlang-factory.com/conference/London2011/speakers/KostisSagonas&quot;&gt;Kostis Sagonas&lt;/a&gt;, the leader of the HIPE Team at Uppsala University, &lt;a href=&quot;http://www.erlang-factory.com/conference/London2011/speakers/MarkusKern&quot;&gt;Marcus Kern&lt;/a&gt;, CTO at MIG, &lt;a href=&quot;http://www.erlang-factory.com/conference/London2011/speakers/SteveVinoski&quot;&gt;Steve Vinosk&lt;/a&gt;i, distributed systems expert and &lt;a href=&quot;http://www.erlang-factory.com/conference/London2011/speakers/ScottLystigFritchie&quot;&gt;Scott Lystig Fritchie&lt;/a&gt;, senior software engineer at Basho Technologies. Many more speakers are still to be announced.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://www.erlang-factory.com/conference/London2011/register&quot;&gt;Book now to secure this fantastic rate!&lt;/a&gt;&lt;/p&gt;</description>
	<pubDate>Wed, 20 Apr 2011 09:16:52 +0000</pubDate>
</item>
<item>
	<title>Programming in the 21st Century: Follow the Vibrancy</title>
	<guid>http://prog21.dadgum.com/97.html</guid>
	<link>http://prog21.dadgum.com/97.html</link>
	<description>Back in 1999 or 2000, I started reading a now-defunct Linux game news site. I thought the combination of enthusiastic people wanting to write video games and the excitement surrounding both Linux and open source would result in a vibrant, creative community.
&lt;br /&gt;&lt;br /&gt;Instead there were endless emulators and uninspired rewrites of stale old games.
&lt;br /&gt;&lt;br /&gt;I could theorize about why there was such a lack of spark, a lack of motivation to create anything distinctive and exciting. Perhaps most of the projects were intended to fulfill coding itches, not personal visions. I don't know. But I lost interest, and I stopped following that site
&lt;br /&gt;&lt;br /&gt;When I wanted to &lt;a href=&quot;http://prog21.dadgum.com/22.html&quot;&gt;modernize my programming skills&lt;/a&gt;, I took a long look at Lisp. It's a beautiful and powerful language, but I was put off by the community. It was a justifiably smug community, yes, but it was an empty smugness. Where were the people using this amazing technology to build impressive applications? Why was everyone so touchy and defensive? That doesn't directly point at the language being flawed--not by any means--but it seemed an indication that &lt;i&gt;something&lt;/i&gt; wasn't right, that maybe there was a reason that people driven to push boundaries and create new experiences weren't drawn to the tremendous purported advantages of Lisp. So I moved on.
&lt;br /&gt;&lt;br /&gt;Vibrancy is an indicator of worthwhile technology. If people are excited, if there's a community of developers more concerned with building things than advocating or justifying, then that's a good place to be. &quot;Worthwhile&quot; may not mean the best or fastest, but I'll take enthusiasm and creativity over either of those.
&lt;br /&gt;&lt;br /&gt;(If you liked this, you might enjoy &lt;a href=&quot;http://prog21.dadgum.com/46.html&quot;&gt;The Pure Tech Side is the Dark Side&lt;/a&gt;.)</description>
	<pubDate>Wed, 20 Apr 2011 06:00:00 +0000</pubDate>
</item>
<item>
	<title>Scattered Thoughts: telehash: router</title>
	<guid>http://scattered-thoughts.net/one/1303/237475/636804</guid>
	<link>http://scattered-thoughts.net/one/1303/237475/636804</link>
	<description>&lt;p&gt;Now that we have all the necessary datastructures we can build the router itself. Most of the routing table logic is handled by the bit_tree and bucket modules. The router just ties these together and handles I/O.
&lt;/p&gt;
&lt;p&gt;Before actually running the routing table the router has to find out its own address, as it is seen from the outside world. It does this by sending +end signals to a list of known telehash nodes (eg telehash.org:42424).
&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;record(bootstrap, { % the state of the router when bootstrapping
          timeout, % give up if no address received before this time
          addresses % list of addresses contacted to find out our address
         }).

bootstrap(Addresses, Timeout) -&amp;gt;
    ?INFO([bootstrapping]),
    State = #bootstrap{timeout=Timeout, addresses=Addresses},
    {ok, _Pid} = gen_server:start_link(?MODULE, State, []).
                          
init(State) -&amp;gt;
    switch:listen(),
    case State of
        #bootstrap{timeout=Timeout, addresses=Addresses} -&amp;gt;
            Telex = telex:end_signal(util:random_end()),
            lists:foreach(fun (Address) -&amp;gt; switch:send(Address, Telex) end, Addresses),
            erlang:send_after(Timeout, self(), giveup);
        #state{} -&amp;gt;
            ok
    end,
    {ok, State}.
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then we listen until we either get a reply with a _to field or run out of time.
&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;handle_info({switch, {recv, From, Telex}}, #bootstrap{addresses=Addresses}=Bootstrap) -&amp;gt;
    % bootstrapping, waiting to receive a message telling us our own address
    case {lists:member(From, Addresses), telex:get(Telex, '_to')} of
        {true, {ok, Binary}} -&amp;gt;
            try util:to_end(util:binary_to_address(Binary)) of
                End -&amp;gt;
                    Self = util:to_bits(End),
                    Table = touched(From, Self, empty_table(Self)),
                    dialer:dial(End, [From], ?ROUTER_DIAL_TIMEOUT),
                    refresh(Self, Table),
                    ?INFO([bootstrap, finished, {self, Binary}, {from, From}]),
                    {noreply, #state{self=Self, pinged=sets:new(), table=Table}}
            catch
                _ -&amp;gt;
                    ?WARN([bootstrap, bad_self, {self, Binary}, {from, From}]),
                    {noreply, Bootstrap}
            end;
        _ -&amp;gt;
            {noreply, Bootstrap}
    end;

handle_info(giveup, #bootstrap{}=Bootstrap) -&amp;gt;
    % failed to bootstrap, die
    ?INFO([giveup, {state, Bootstrap}]),
    {stop, {shutdown, gaveup}, Bootstrap};
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Once we know our own address we can fill in the state record and start managing the routing table.
&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;-record(state, { % the state of the router in normal operation
          self, % the bits of the routers own end
          pinged, % set of addresses which have been pinged and not yet replied/timedout
          table % the routing table, a bit_tree containing buckets of nodes
         }).
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;One of the jobs of the router is to remove unresponsive nodes from the routing table. To check if a node is responsive we just a random +end signal and wait for a reply. If the node is unresponsive it gets marked as stale and we try to find a suitable replacement. The node won't actually be dropped from the table until a replacement is found - this prevents the table from getting flushed if our network connection goes down.
&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;ping(To) -&amp;gt;
    Telex = telex:end_signal(util:random_end()),
    % do this in a message to self to avoid some awkward control flow
    self() ! {pinging, To},
    switch:send(To, Telex),
    erlang:send_after(?ROUTER_PING_TIMEOUT, self(), {timeout, Address}).

timedout(Address, Self, Table) -&amp;gt;
    bit_tree:update(
      fun (_Suffix, _Depth, _Gap, Bucket) -&amp;gt;
              case bucket:timedout(Address, Bucket) of
                  {node, Node, Update} -&amp;gt;
                      % try to touch this node, might be suitable replacement
                      ping(Node),
                      Update;
                  Update -&amp;gt;
                      Update
              end
      end,
      util:to_bits(Address),
      Self,
      Table
     ).

handle_info({pinging, Address}, #state{pinged=Pinged}=State) -&amp;gt;
    % do this in a message to self to avoid some awkward control flow
    ?INFO([recording_ping, {address, Address}]),
    Pinged2 = sets:add_element(Address, Pinged),
    {noreply, State#state{pinged=Pinged2}};
handle_info({timeout, Address}, #state{self=Self, pinged=Pinged, table=Table}=State) -&amp;gt;
    case lists:member(Address, Pinged) of
        true -&amp;gt;
            % ping timedout
            ?INFO([timeout, {address, Address}]),
            Table2 = timedout(Address, Self, Table),
            {ok, State#state{table=Table2}};
        false -&amp;gt;
            % address already replied
            {ok, State}
    end;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;One of the rules of the router is that it should never pass on information about a node that it hasn't personally confirmed to exist. Once we receive a message from a node we know that it exists (later we will implement _ring/_line to protect against address spoofing):
&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;touched(Address, Self, Table) -&amp;gt;
    bit_tree:update(
      fun (Suffix, _Depth, Gap, Bucket) -&amp;gt;
              May_split = (Gap &amp;lt; ?K), % !!! or (Depth &amp;lt; ?ROUTER_TABLE_EXPANSION)
              bucket:touched(Address, Suffix, now(), Bucket, May_split)
      end,
      util:to_bits(Address),
      Self,
      Table
     ).
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;On receiving a .see command we record all the contained addresses as potential nodes and ping them to try to confirm their existence.
&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;seen(Address, Self, Table) -&amp;gt;
    bit_tree:update(
      fun (Suffix, _Depth, _Gap, Bucket) -&amp;gt;
              case bucket:seen(Address, Suffix, now(), Bucket) of
                  {node, Node, Update} -&amp;gt;
                      % check if this node is stale
                      ping(Node),
                      Update;
                  Update -&amp;gt;
                      Update
              end
      end,
      util:to_bits(Address),
      Self,
      Table
     ).
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;On receiving a +end signal we reply with a .see command containing the nearest K addresses which we have confirmed to exist.
&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;see(To, End, Table) -&amp;gt;
    Telex = telex:see_command(nearest(?K, End, Table)),
    switch:send(To, Telex).

nearest(N, End, Table) when N&amp;gt;=0 -&amp;gt;
    Bits = util:to_bits(End),
    iter:take(
      N,
      iter:flatten(
        iter:map(
          fun ({_Prefix, Bucket}) -&amp;gt; bucket:by_dist(End, Bucket) end,
          bit_tree:iter(Bits, Table)))).
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;On receiving a message we have handle the above three cases, which gets a little ugly.
&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;handle_info({switch, {recv, From, Telex}}, #state{self=Self, pinged=Pinged, table=Table}=State) -&amp;gt;
    % this counts as a reply
    Pinged2 = sets:del_element(From, Pinged),
    % touched the sender
    % !!! eventually will check _line here
    ?INFO([touched, {node, From}]),
    Table2 = touched(From, Self, Table),
    % maybe seen some nodes
    Table3 =
        case telex:get(Telex, '.see') of
            {ok, Binaries} -&amp;gt;
                try [util:binary_to_address(Bin) || Bin &amp;lt;- Binaries] of
                    Addresses -&amp;gt;
                        ?INFO([seen, {nodes, Addresses}, {from, From}]),
                        lists:foldl(fun (Address, Table_acc) -&amp;gt; seen(Address, Self, Table_acc) end, Table2, Addresses)
                catch
                    _ -&amp;gt;
                        ?INFO([bad_seen, {nodes, Binaries}, {from, From}]),
                        Table2
                end;
            _ -&amp;gt;
                Table2
        end,
    % maybe send some nodes back
    case telex:get(Telex, '+end') of
        {ok, Hex} -&amp;gt;
            try util:hex_to_end(Hex) of
                End -&amp;gt;
                    ?INFO([see, {'end', End}, {from, From}]),
                    see(From, End, Table3)
            catch
                _ -&amp;gt;
                    ?WARN([bad_see, {'end', Hex}, {from, From}])
            end;
        _ -&amp;gt;
            ok
    end,
    {noreply, State#state{pinged=Pinged2, table=Table2}};
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The last responsibility of the router is to periodically refresh buckets which haven't recently seen any activity.
&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;handle_info(refresh, #state{self=Self, table=Table}=State) -&amp;gt;
    ?INFO([refreshing_table]),
    refresh(Self, Table),
    {noreply, State};
handle_info({dialed, _, _}, State) -&amp;gt;
    % response from a bucket refresh, we don't care
    {noreply, State};

dialed(Address, Self, Table) -&amp;gt;
    bit_tree:update(
      fun (_Suffix, _Depth, _Gap, Bucket) -&amp;gt;
              bucket:dialed(now(), Bucket)
      end,
      util:to_bits(Address),
      Self,
      Table
     ).

needs_refresh(Bucket, Now) -&amp;gt;
    case bucket:last_dialed(Bucket) of
        never -&amp;gt;
            true;
        Last -&amp;gt;
            (timer:now_diff(Now, Last) div 1000) &amp;lt; ?ROUTER_REFRESH_TIME
    end.

refresh(Self, Table) -&amp;gt;
    Now = now(),
    iter:foreach(
      fun ({Prefix, Bucket}) -&amp;gt;
              case needs_refresh(Bucket, Now) of
                  true -&amp;gt;
                      ?INFO([refreshing_bucket, {prefix, Prefix}, {bucket, Bucket}]),
                      To = util:random_end(Prefix),
                      From = nearest(?K, To, Table),
                      dialer:dial(To, From, ?ROUTER_DIAL_TIMEOUT);
                  false -&amp;gt;
                      ok
              end
      end,
      bit_tree:iter(Self, Table)
     ),
    erlang:send_after(?ROUTER_REFRESH_TIME, self(), refresh),
    ok.
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;That's it. As usual the (untested) code is in the &lt;a href=&quot;https://github.com/jamii/erl-telehash&quot;&gt;repo&lt;/a&gt;. The next post will probably deal with taps.
&lt;/p&gt;</description>
	<pubDate>Tue, 19 Apr 2011 18:24:35 +0000</pubDate>
</item>
<item>
	<title>Scattered Thoughts: telehash: gen_event woes</title>
	<guid>http://scattered-thoughts.net/one/1303/234437/654230</guid>
	<link>http://scattered-thoughts.net/one/1303/234437/654230</link>
	<description>&lt;p&gt;I ran into some tricky bugs caused by a misconception I had about gen_event. Since this is not explicitly stated in the gen_event documentation I will say it here: gen_event does NOT spawn individual processes for each handler. Each handler is run sequentially in the event manager process.
&lt;/p&gt;
&lt;p&gt;Now obviously the documentation is not at fault here. I assumed that each handler got its own process solely because the callbacks resembled gen_server. However, a little googling reveals that several other people made the same mistake so I thought it was worth mentioning.
&lt;/p&gt;
&lt;p&gt;Here is how I found this out. I was working on the router implementation for telehash. When I tested the bootstrapping algorithm everything looked fine until the first dial, after which nothing else happened. Straight away I suspected a bug in the dialer, but repeating the exact same call in the console worked fine. After a few deadends I opened pman to look for anything suspicious but couldn't find the dialer process (because it doesn't exist, it's an event handler). I assumed that it was somehow crashing silently and wasted an hour or so reading and rereading the code and stepping through various calls in the debugger. No matter what I tried the dialer worked absolutely perfectly unless it was called by the router.
&lt;/p&gt;
&lt;p&gt;Eventually I noticed that the switch_event process was blocking inside a receive and the whole thing unravelled. The dialer is an event handler so when started it calls:
&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;gen_event:add_handler(switch_event, dialer, State)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;which is a synchronous call to the switch_event process. The router event handler is running inside the switch_event process so when the router tries to dial it deadlocks.
&lt;/p&gt;
&lt;p&gt;The moral of this story is &lt;b&gt;RTFM&lt;/b&gt;.
&lt;/p&gt;
&lt;p&gt;This is easily fixed by changing
&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;dialer:dial(End, [Address])
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;to
&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;spawn(fun () -&amp;gt; dialer:dial(End, [Address]) end)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;but there were more problems. Most of the event handlers used erlang:send_after to handle timeouts but since they all run in the same process they all receive each others timeouts. Also, every event handler is run sequentially so the switch_event process becomes a huge bottleneck.
&lt;/p&gt;
&lt;p&gt;The solution I settled on was to change each event handler into a gen_server and write a simple event handler that just forwards events to its owner. By using gen_event:add_sup_handler and listening for event handler exits we can keep the two in sync.
&lt;/p&gt;</description>
	<pubDate>Tue, 19 Apr 2011 17:33:57 +0000</pubDate>
</item>
<item>
	<title>Erlang Factory News: Erlang Factory SF Bay Area 2011 - A Great Success</title>
	<guid>http://www.erlang-factory.com/news/list#55</guid>
	<link>http://www.erlang-factory.com/news/list#55</link>
	<description>&lt;p&gt;On the 24th - 25th March 2011, over 170 people gathered in the SF Bay Area for the 3rd Erlang Factory. With two Keynotes this year delivered by, Kostis Sagonas and Dan Ingalls, 10 extra talks compared to last year and a record-breaking number of delegates, this year's conference was nothing short of a tremendous success. &lt;br /&gt;&lt;br /&gt;We would like to thank everyone that attended and keep your eyes out for the videos that will be released soon!&lt;/p&gt;</description>
	<pubDate>Tue, 19 Apr 2011 14:01:55 +0000</pubDate>
</item>
<item>
	<title>Erlang Factory News: Only 5 Places left at the Erlang Factory SF Bay Area!</title>
	<guid>http://www.erlang-factory.com/news/list#54</guid>
	<link>http://www.erlang-factory.com/news/list#54</link>
	<description>&lt;p&gt;We only have 5 places left at the Erlang Factory SF Bay Area so if you haven't booked yet, don't miss out and &lt;a href=&quot;https://www.erlang-factory.com/conference/SFBay2011/register&quot;&gt;book now!&lt;/a&gt; The OTP Express course has now sold out but there are a few places left on the other courses at the &lt;a href=&quot;http://www.erlang-factory.com/conference/SFBay2011/university&quot;&gt;University&lt;/a&gt;.&lt;/p&gt;</description>
	<pubDate>Tue, 19 Apr 2011 14:01:55 +0000</pubDate>
</item>
<item>
	<title>Erlang Factory News: Erlang Factory London 2011 - Talk Submission Open!</title>
	<guid>http://www.erlang-factory.com/news/list#53</guid>
	<link>http://www.erlang-factory.com/news/list#53</link>
	<description>&lt;p&gt;Do you have  something you want to share with the programming world? A  great  project, something new or innovative then we want to hear from  you. If  you want to give a presentation at the Erlang Factory, you can  find  more information how to do that, &lt;a href=&quot;http://www.erlang-factory.com/conference/London2011/submit_talk&quot;&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The submission deadline is the 11th April 2011.&lt;/p&gt;</description>
	<pubDate>Tue, 19 Apr 2011 14:01:55 +0000</pubDate>
</item>
<item>
	<title>Erlang Factory News: Early Bird Rate - 1 Week left to save over $200!</title>
	<guid>http://www.erlang-factory.com/news/list#52</guid>
	<link>http://www.erlang-factory.com/news/list#52</link>
	<description>&lt;div&gt;There is only 1 week left to book your place at the Early Bird Rate of $690 so don't miss out!&lt;br /&gt;
&lt;p&gt;You will get the chance to, see some &lt;a href=&quot;http://www.erlang-factory.com/conference/SFBay2011/speakers&quot;&gt;fantastic names&lt;/a&gt; from the Erlang world including, Joe Armstrong, Kostis Sagonas, Damien   Katz, and Dan Ingalls, network with your fellow programmers and find  out  what&amp;rsquo;s new not only in the world of Erlang but in the world of   programming.&lt;/p&gt;
&lt;p&gt;Register now to receive the Early Bird Rate and save over $200!&lt;/p&gt;
&lt;/div&gt;</description>
	<pubDate>Tue, 19 Apr 2011 14:01:55 +0000</pubDate>
</item>
<item>
	<title>Erlang Factory News: Erlang Factory SF Bay Area - Save over $200</title>
	<guid>http://www.erlang-factory.com/news/list#51</guid>
	<link>http://www.erlang-factory.com/news/list#51</link>
	<description>&lt;div&gt;We are offering a fantastic saving of over $200 when you register for the conference before the 1st of March 2011.
&lt;p&gt;You will get the chance to, see some &lt;a href=&quot;http://www.erlang-factory.com/conference/SFBay2011/speakers&quot;&gt;fantastic names&lt;/a&gt; from the Erlang world including, Joe Armstrong, Kostis Sagonas, Damien  Katz, and Dan Ingalls, network with your fellow programmers and find out  what&amp;rsquo;s new not only in the world of Erlang but in the world of  programming.&lt;/p&gt;
&lt;p&gt;Register now to receive the Early Bird Rate and save over $200!&lt;/p&gt;
&lt;/div&gt;</description>
	<pubDate>Tue, 19 Apr 2011 14:01:55 +0000</pubDate>
</item>
<item>
	<title>Programming in the 21st Century: Revisiting &quot;Tricky When You Least Expect It&quot;</title>
	<guid>http://prog21.dadgum.com/96.html</guid>
	<link>http://prog21.dadgum.com/96.html</link>
	<description>Since writing &lt;a href=&quot;http://prog21.dadgum.com/71.html&quot;&gt;Tricky When You Least Expect It&lt;/a&gt; in June 2010, I've gotten a number of responses offering better solutions to the &lt;tt&gt;angle_diff&lt;/tt&gt; problem. The final version I presented in the original article was this:
&lt;pre&gt;angle_diff(Begin, End) -&amp;gt;
   D = End - Begin,
   DA = abs(D),
   case {DA &amp;gt; 180, D &amp;gt; 0} of
      {true, true} -&amp;gt; DA - 360;
      {true, _}    -&amp;gt; 360 - DA;
      _ -&amp;gt; D
   end.
&lt;/pre&gt;But, maybe surprisingly, this function can be written in two lines:
&lt;pre&gt;angle_diff(Begin, End) -&amp;gt;
   (End - Begin + 540) rem 360 - 180.
&lt;/pre&gt;The key is to shift the difference into the range -180 to 180 before the modulo operation. The &quot;- 180&quot; at the end adjusts it back. One quirk of Erlang is that the modulo operator (rem) gives a negative result if the first value is negative. That's easily fixed by adding 360 to the difference (180 + 360 = 540) to ensure that it's always positive. (Remember that adding 360 to an angle gives the same angle.)
&lt;br /&gt;&lt;br /&gt;So how did I miss this simpler solution? I got off track by by thinking I needed an absolute value, and things went downhill from there. I'd like to think if I could rewind and re-attempt the problem from scratch, then I'd see the error of my ways, but I suspect I'd miss it the second time, too. And that's what I was getting at when I wrote &quot;Tricky When You Least Expect It&quot;: that you never know when it will take some real thought to solve a seemingly simple problem.
&lt;br /&gt;&lt;br /&gt;(Thanks to Samuel Tardieu, Benjamin Newman, and Greg Rosenblatt, who all sent almost identical solutions.)</description>
	<pubDate>Fri, 15 Apr 2011 06:00:00 +0000</pubDate>
</item>
<item>
	<title>Erlang Inside: Memory Models in Erlang vs Java</title>
	<guid>http://erlanginside.com/?p=272</guid>
	<link>http://erlanginside.com/erlang-vs-java-memory-model-272</link>
	<description>A view of Erlang (focused on the memory model) from a &amp;#8220;Java Code Geek&amp;#8221; &amp;#8211; http://www.javacodegeeks.com/2011/04/erlang-vs-java-memory-architecture.html</description>
	<pubDate>Wed, 13 Apr 2011 11:58:36 +0000</pubDate>
</item>
<item>
	<title>Programming in the 21st Century: Caught-Up with 20 Years of UI Criticism</title>
	<guid>http://prog21.dadgum.com/95.html</guid>
	<link>http://prog21.dadgum.com/95.html</link>
	<description>Interaction designers have leveled some harsh criticisms at the GUI status-quo over the last 20+ years. The mouse is an inefficient input device. The desktop metaphor is awkward and misguided. Users shouldn't be exposed to low-level details like the raw file-system and having to save their work.
&lt;br /&gt;&lt;br /&gt;And they were right.
&lt;br /&gt;&lt;br /&gt;But instead of better human/computer interaction, we got faster processors and hotter processors and multiple processors and entire processors devoted to 3D graphics. None of which are bad, mind you, but it was always odd to see such tremendous advances in hardware while the researchers promoting more pleasant user experiences wrote books that were eagerly read by people who enjoyed smirking at the wrong-headedness of an entire industry--yet who weren't motivated enough to do anything about it. Or so it seemed.
&lt;br /&gt;&lt;br /&gt;It's miraculous that in 2011, the biggest selling computers are mouse-free, run programs that take over the entire screen without the noise of a faux-desktop, and the entire concept of &quot;saving&quot; has been rendered obsolete.
&lt;br /&gt;&lt;br /&gt;Clearly, someone listened.
&lt;br /&gt;&lt;br /&gt;(If you liked this, you might enjoy &lt;a href=&quot;http://prog21.dadgum.com/74.html&quot;&gt;Free Your Technical Aesthetic from the 1970s&lt;/a&gt;.)</description>
	<pubDate>Mon, 11 Apr 2011 06:00:00 +0000</pubDate>
</item>
<item>
	<title>REALITY.SYS: Adlige Posse</title>
	<guid>http://www.brakmic.de/?p=450</guid>
	<link>http://www.brakmic.de/index.php/2011/04/10/adlige-posse/</link>
	<description>&lt;p&gt;&lt;a href=&quot;http://www.spiegel.de/flash/flash-25408.html&quot; target=&quot;_blank&quot;&gt;Eigentlich ist Guttenberg ist Sachen Öffentlichkeit sehr konsequent&lt;/a&gt;: immer wenn es darum ging, sich in Pose zu setzen, waren die Kameras willkommen (selbst eine Talk-Show aus Afghanistan durfte sich das &lt;em&gt;gemeine Volk&lt;/em&gt; antun). Später aber, als es darum ging, für eigene Fehler gerade zu stehen, wurde zensiert was das Zeug hält. Zuerst versuchte er, vor &amp;#8220;ausgewählten&amp;#8221; Presse-Vertretern seine erste Stellungnahme zu den Plagiatsvorwürfen zu geben, während zur gleichen Zeit eine Bundespresse-Konferenz abgehalten wurde. Danach verbot er die Live-Übertragung seines Rücktritts aus dem Bundesverteidigungsministerium, so dass das &lt;em&gt;gemeine Volk&lt;/em&gt; nur durch das zwischengeschaltete Handy einer NTV-Reporterin die &amp;#8220;live Stimme&amp;#8221; des ehemaligen Dr./Ministers hören konnte. Und jetzt erleben wir einen &lt;a href=&quot;http://www.spiegel.de/politik/deutschland/0,1518,756126,00.html&quot; target=&quot;_blank&quot;&gt;weiteren Zensurversuch&lt;/a&gt;. &lt;em&gt;Typisch adliges&lt;/em&gt; aber sicherlich nicht &amp;#8220;edles&amp;#8221; Verhalten: immer schöne Posen halten, aber kaum Leistung erbringen. Dass solche &amp;#8220;erlauchten&amp;#8221; Kreise wenig von&lt;a href=&quot;http://de.guttenplag.wikia.com/wiki/GuttenPlag_Wiki&quot; target=&quot;_blank&quot;&gt; Fleißarbeit, Mühsal und Ehrlichkeit&lt;/a&gt; halten, dasselbe aber ihren Wählern einbläuen, muss hier nicht ausführlich erörtert werden. ABER: so hat es vielleicht in den Jahrhunderten zuvor funktioniert. In dieser Zeit, und das hat mittlerweile auch ein Herr KTG begriffen, reicht eine bloße Pose nicht mehr aus, um die Massen zu begeistern. Nicht einmal die Royal-versessenen Briten erlauben ihren blaublütigen Posern mehr zu sein, als Yellow-Press-Volontäre! Herr KTG ist nur ein typisches Beispiel für die Oberflächlichkeit der ehemaligen Herrschenden dieses Landes, die wohl immer noch davon träumen, durch bloßes Posing und &amp;#8220;gute&amp;#8221; Manieren das Volk zu steuern. Diese Zeiten sind für den größten Teil Deutschlands aber schon längst vorbei. Die feudale Matrix funktioniert nicht mehr. KTG, du bist raus!&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;http://deinweckruf.files.wordpress.com/2011/03/guttenberg-der-blender.jpg&quot; alt=&quot;&quot; width=&quot;614&quot; height=&quot;451&quot; /&gt;&lt;/p&gt;</description>
	<pubDate>Sun, 10 Apr 2011 17:03:14 +0000</pubDate>
</item>
<item>
	<title>Process-one Blogs: Sea Beyond 2011 Talk 7: Jukka Alakontiola on Nokia Push Notifications</title>
	<guid>http://www.process-one.net/en/blogs/article/sea_beyond_2011_talk_7_jukka_alakontiola_on_nokia_push_notifications/</guid>
	<link>http://www.process-one.net/en/blogs/article/sea_beyond_2011_talk_7_jukka_alakontiola_on_nokia_push_notifications/</link>
	<description>&lt;p&gt;Nokia Push Notification system has been developed by Nokia to send notifications on mobile directly.&lt;/p&gt; &lt;p&gt;Jukka Alakontiola presents the architecture of Nokia push and speaks about mobile related XMPP optimisations.&lt;/p&gt;
&lt;p&gt;Do not miss this video, it features lots of great insights on mobile realtime services.&lt;/p&gt;
&lt;p&gt;Here is the video of his presentation:&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;You can see the slides here:&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;Do not miss the&amp;nbsp;&lt;a href=&quot;http://www.process-one.net/en/blogs/article/sea_beyond_2011_video_summary/&quot;&gt;event summary and the other videos from Sea Beyond event&lt;/a&gt;.&lt;/p&gt;</description>
	<pubDate>Thu, 07 Apr 2011 12:53:26 +0000</pubDate>
</item>
<item>
	<title>Scattered Thoughts: telehash: buckets</title>
	<guid>http://scattered-thoughts.net/one/1301/514243/595154</guid>
	<link>http://scattered-thoughts.net/one/1301/514243/595154</link>
	<description>&lt;p&gt;The other half of the routing table is the buckets which store node addresses.
&lt;/p&gt;
&lt;p&gt;Usual disclaimer: none of this is properly tested yet.
&lt;/p&gt;
&lt;p&gt;The Kademlia paper has much to say on the issue of routing, most of it contradictory. My takeaway from many readings and from browsing the source code of various different implementations is that the following points are the most important:
&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;each bucket should contain at most &lt;b&gt;K&lt;/b&gt; nodes
    &lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;we should only ever report node addresses which we have personally confirmed exist
    &lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;responsive nodes should never be removed from buckets
    &lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;nodes should never be removed from buckets unless a suitable replacement exists
    &lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The first three points make the routing table very resistant to flooding and spoofing. In particular, they prevent a common attack for p2p networks where some bad guy floods the routing tables of all the other nodes so that all traffic is routed through nodes controlled by the bad guy. The last point prevents nodes from flushing their routing tables if their own network connection goes down.
&lt;/p&gt;
&lt;p&gt;I think the implementation I have come up with is fairly clean, if a little lengthy. Like the bit_tree I want the bucket to be completely pure. All side effects will be handled by the router itself. The main data structures are explained pretty well by the comments:
&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;-define(K, ?DIAL_DEPTH).

-record(node, {
          address, % node #address{} record
          'end', % node end
          suffix, % the remaining bits of the nodes end left over from the bit_tree
          status, % one of [live, stale, cache]
          last_seen % for live/stale nodes, the time of the last received message. for cache nodes the time of the last .see reference to the node
         }).

-record(bucket, {
          nodes, % gb_tree mapping addresses to {Status, Last_seen}
          % remaining fields are pq's of nodes sorted by their last_seen field
          live, % nodes currently expected to be alive
          stale, % nodes which have not replied recently
          cache % potential nodes which we have not yet verified
         }). % invariant: pq_maps:size(live) + pq_maps:size(stale) &amp;lt;= ?K
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The bucket is a two-stage data structure. This allows us the keep nodes of different statuses sorted by the last_seen time but still be able to get/delete nodes just knowing the address. The &lt;b&gt;get_node&lt;/b&gt; function should make it clear how this works:
&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;get_node(Address,
         #bucket{nodes=Nodes, live=Live, stale=Stale, cache=Cache}) -&amp;gt;
    case gb_trees:lookup(Address, Nodes) of
        {value, {Status, Last_seen}} -&amp;gt;
            case Status of
                live -&amp;gt;
                    {ok, pq_maps:get({Last_seen, Address}, Live)};
                stale -&amp;gt;
                    {ok, pq_maps:get({Last_seen, Address}, Stale)};
                cache -&amp;gt;
                    {ok, pq_maps:get({Last_seen, Address}, Cache)}
            end;
        none -&amp;gt;
            none
    end.
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This is only long because records are purely a compile time structure ie we can't write &lt;b&gt;Bucket#bucket.Status&lt;/b&gt; so we have to pattern match on &lt;b&gt;Status&lt;/b&gt; instead. We also define &lt;b&gt;add_node/2&lt;/b&gt;, &lt;b&gt;del_node/2&lt;/b&gt; and &lt;b&gt;update_node/2&lt;/b&gt;, which look pretty similar, as well as &lt;b&gt;to_list/1&lt;/b&gt;, &lt;b&gt;from_list/1&lt;/b&gt; and &lt;b&gt;sizes/1&lt;/b&gt;.
&lt;/p&gt;
&lt;p&gt;The router is going to react to various events by calling the appropriate bucket functions and possibly sending out messages based on the result. The first event it has to handle is a node becoming unresponsive. The bucket will mark this node as stale and return a cache node which the router can attempt to verify.
&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;% this address failed to reply in a timely manner
timedout(Address, Bucket) -&amp;gt;
    log:info([?MODULE, timing_out, Address, Bucket]),
    case get_node(Address, Bucket) of
        {ok, Node} -&amp;gt;
            case Node#node.status of
                live -&amp;gt;
                    % mark as stale, return a cache node that might be a suitable replacement
                    Bucket2 = update_node(Node#node{status=stale}, Bucket),
                    pop_cache_hi(Bucket2);
                _ -&amp;gt;
                    % if cache or stale already we don't care
                    ok(Bucket)
            end;
        none -&amp;gt;
            % wtf? we don't even know this node?
            % one way this could happen:
            % send N1, sendN1, timedout N1, add N2 (pushing N1 out of stale), timedout N1
            log:warning([?MODULE, unknown_node_timedout, Address, Bucket]),
            ok(Bucket)
    end.

% return most recently seen cache node, if any exist
pop_cache_hi(#bucket{cache=Cache}=Bucket) -&amp;gt;
    case pq_maps:pop_hi(Cache) of
        {_Key, Node, Cache2} -&amp;gt;
            {node, Node, ok(Bucket#bucket{cache=Cache2})};
        false -&amp;gt;
            ok(Bucket)
    end.
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The next event is receiving a &lt;b&gt;.see&lt;/b&gt; command. This may be as a result of a &lt;b&gt;+end&lt;/b&gt; sent by the router but is more likely to be part of a dialing process happening elsewhere. The beauty of Kademlia is that the router can populate the routing table just by listening in on dialing attempts.
&lt;/p&gt;
&lt;p&gt;For each node listed in the &lt;b&gt;.see&lt;/b&gt; command the router will call &lt;b&gt;seen&lt;/b&gt;. This adds the node to the cache and returns the least recently seen live node so the router can check that it is still responsive.
&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;% this address has been reported to exist by another node
seen(Address, Time, Suffix, Bucket) -&amp;gt;
    log:info([?MODULE, seeing, Address, Bucket]),
    case get_node(Address, Bucket) of
        {ok, Node} -&amp;gt;
            case Node#node.status of
                cache -&amp;gt;
                    % for cache nodes being in a .see is good enough
                    ok(update_node(Node#node{last_seen=Time}, Bucket));
                _ -&amp;gt;
                    % for live/stale nodes we require direct contact so ignore this
                    ok(Bucket)
            end;
        none -&amp;gt;
            % put node in cache, return a live node to ping
            Node = #node{
              address = Address,
              'end' = util:to_end(Address),
              suffix = Suffix,
              status = cache,
              last_seen = Time
             },
            Bucket2 = add_node(Node, Bucket),
            case peek_live_lo(Bucket) of
                none -&amp;gt; ok(Bucket2);
                {ok, Live_node} -&amp;gt; {node, Live_node, ok(Bucket2)}
            end
    end.

% return the oldest live node
peek_live_lo(#bucket{live=Live}) -&amp;gt;
    case pq_maps:peek_lo(Live) of
        none -&amp;gt; none;
        {_, Node} -&amp;gt; {ok, Node}
    end.
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Any time we receive a message we learn that the node sending it exists (or not - we'll deal with address spoofing in a later post) so we can potentially mark it as a live node. The &lt;b&gt;touched&lt;/b&gt; function checks if the node is already in the bucket or if it needs to be added.
&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;% this address has been verified as actually existing
touched(Address, Suffix, Time, Bucket, May_split) -&amp;gt;
    log:info([?MODULE, touching, Address, Bucket]),
    case get_node(Address, Bucket) of
        {ok, Node} -&amp;gt;
            case Node#node.status of
                live -&amp;gt;
                    % update last_seen time
                    ok(update_node(Node#node{last_seen=Time}, Bucket));
                stale -&amp;gt;
                    % update last_seen time and promote to live
                    ok(update_node(Node#node{last_seen=Time, status=live}, Bucket));
                cache -&amp;gt;
                    % potentially promote the node to live
                    Bucket2 = del_node(Node, Bucket),
                    new_node(Address, Suffix, Time, Bucket2, May_split)
            end;
        none -&amp;gt;
            % potentially add the node to live
            new_node(Address, Suffix, Time, Bucket, May_split)
    end.
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If the node needs to be added then &lt;b&gt;touched&lt;/b&gt; calls &lt;b&gt;new_node&lt;/b&gt; which decides if there is space in the bucket and, if so, adds the new node. If the bucket is full and &lt;b&gt;May_split&lt;/b&gt; is true then &lt;b&gt;new_node&lt;/b&gt; will split the bucket before adding the new node. Deciding whether or not splitting is allowed is the routers job.
&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;% assumes Address is not already in Bucket, otherwise crashes
new_node(Address, Suffix, Time, Bucket, May_split) -&amp;gt;
    Node = #node{
      address = Address,
      'end' = util:to_end(Address),
      suffix = Suffix,
      status = undefined,
      last_seen = Time
     },
    {Lives, Stales, _} = sizes(Bucket),
    if
        Lives + Stales &amp;lt; ?K -&amp;gt;
            % space left in live
            log:info([?MODULE, adding, Node, Bucket]),
            ok(add_node(Node#node{status=live}, Bucket));
        (Lives &amp;lt; ?K) and (Stales &amp;gt; 0) -&amp;gt;
            % space left in live if we push something out of stale
            log:info([?MODULE, adding, Node, Bucket]),
            Bucket2 = drop_stale(Bucket),
            ok(add_node(Node#node{status=live}, Bucket2));
        May_split and (Suffix /= []) -&amp;gt;
            % allowed to split the bucket to make space
            log:info([?MODULE, splitting, Node, Bucket]),
            {split, BucketF, BucketT} = split(Bucket),
            [Bit | Suffix2] = Suffix,
            case Bit of
                false -&amp;gt;
                    BucketF2 = new_node(Address, Suffix2, Time, BucketF, May_split),
                    {split, BucketF2, BucketT};
                true -&amp;gt;
                    BucketT2 = new_node(Address, Suffix2, Time, BucketT, May_split),
                    {split, BucketF, BucketT2}
            end;
        true -&amp;gt;
            % not allowed to split, will have to go in the cache
            log:info([?MODULE, caching, Node, Bucket]),
            ok(add_node(Node#node{status=cache}, bucket))
    end.

% drop the oldest stale node, crashes if none exist
drop_stale(#bucket{stale=Stale}=Bucket) -&amp;gt;
    {_Key, _Node, Stale2} = pq_maps:pop_one_hi(Stale),
    Bucket#bucket{stale=Stale2}.

split(Bucket) -&amp;gt;
    Nodes = to_list(Bucket),
    NodesF = [Node#node{suffix=Suffix2} || #node{suffix=[false|Suffix2]}=Node &amp;lt;- Nodes],
    NodesT = [Node#node{suffix=Suffix2} || #node{suffix=[true|Suffix2]}=Node &amp;lt;- Nodes],
    {split, from_list(NodesF), from_list(NodesT)}.
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Finally, upon receiving a &lt;b&gt;+end&lt;/b&gt; signal the router needs to reply with a &lt;b&gt;.see&lt;/b&gt; command listing the &lt;b&gt;K&lt;/b&gt; nearest nodes to the specified end. This will be done using a combination of &lt;b&gt;bit_tree:iter&lt;/b&gt; and &lt;b&gt;bucket:nearest&lt;/b&gt;.
&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;nearest(N, End, #bucket{live=Live, stale=Stale}) -&amp;gt;
    Nodes = pq_maps:to_list(Live) ++ pq_maps:to_list(Stale),
    Num_nodes = pq_maps:size(Live) + pq_maps:size(Stale),
    if
        Num_nodes =&amp;lt; N -&amp;gt;
            [Node#node.address || {_Key, Node} &amp;lt;- Nodes];
        true -&amp;gt;
            % !!! maybe should prefer to return live nodes even if further away
            Nodes_by_dist = [{util:distance(End, Node#node.'end'), Node} || {_Key, Node} &amp;lt;- pq_maps:to_list(Live)],
            {Closest, _} = lists:split(N, lists:sort(Nodes_by_dist)),
            [Node#node.address || {_Dist, Node} &amp;lt;- Closest]
    end.
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;As usual all the code is sitting in the &lt;a href=&quot;https://github.com/jamii/erl-telehash&quot;&gt;repo&lt;/a&gt;.
&lt;/p&gt;</description>
	<pubDate>Wed, 30 Mar 2011 19:44:03 +0000</pubDate>
</item>
<item>
	<title>Process-one Blogs: Sea Beyond 2011 Talk 6: Diana Cheng on OneSocialWeb</title>
	<guid>http://www.process-one.net/en/blogs/article/sea_beyond_2011_talk_6_diana_cheng_on_onesocialweb/</guid>
	<link>http://www.process-one.net/en/blogs/article/sea_beyond_2011_talk_6_diana_cheng_on_onesocialweb/</link>
	<description>&lt;p&gt;Diana Cheng, from Vodafone, introduces OneSocialWeb project.&lt;/p&gt; &lt;p&gt;Diana Cheng presents OneSocialWeb distributed social web initiative, primarily based on XMPP and Activitystreams standards.&lt;/p&gt;
&lt;p&gt;Here is the video of her presentation:&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;You can see the slides here:&lt;/p&gt;
&lt;p&gt;








&lt;/p&gt;
&lt;p&gt;Do not miss the &lt;a href=&quot;http://www.process-one.net/en/blogs/article/sea_beyond_2011_video_summary/&quot;&gt;event summary and the other videos from Sea Beyond event&lt;/a&gt;.&lt;/p&gt;</description>
	<pubDate>Tue, 29 Mar 2011 11:45:05 +0000</pubDate>
</item>
<item>
	<title>Erlang Factory News: Erlang Factory SF Bay Area 2011 - A Great Success</title>
	<guid>http://erlang-factory.com/news/rss/c6d8ecd4513002716858b76e2d848abd</guid>
	<link></link>
	<description>&lt;p&gt;On the 24th - 25th March 2011, over 170 people gathered in the SF Bay Area for the 3rd Erlang Factory. With two Keynotes this year delivered by, Kostis Sagonas and Dan Ingalls, 10 extra talks compared to last year and a record-breaking number of delegates, this year's conference was nothing short of a tremendous success. &lt;br /&gt;&lt;br /&gt;We would like to thank everyone that attended and keep your eyes out for the videos that will be released soon!&lt;/p&gt;</description>
	<pubDate>Tue, 29 Mar 2011 11:34:01 +0000</pubDate>
</item>
<item>
	<title>Trapexit's Erlang Blog Filter: London Erlang User Group Slides</title>
	<guid>http://steve.vinoski.net/blog/?p=762</guid>
	<link>http://steve.vinoski.net/blog/2011/03/28/london-erlang-user-group-slides/</link>
	<description>&lt;p&gt;While attending &lt;a rel=&quot;nofollow&quot; target=&quot;_blank&quot; href=&quot;http://qconlondon.com/&quot;&gt;QCon&lt;/a&gt; recently I also spoke at the &lt;a rel=&quot;nofollow&quot; target=&quot;_blank&quot; href=&quot;http://www.erlang-solutions.com/etc/usergroup/london&quot;&gt;London Erlang User Group&lt;/a&gt; meeting. I gave a quick talk about &lt;a rel=&quot;nofollow&quot; target=&quot;_blank&quot; href=&quot;http://www.erlang.org/doc/tutorial/nif.html&quot;&gt;Erlang Native Implemented Functions&lt;/a&gt; (NIFs) as used in my little &lt;a rel=&quot;nofollow&quot; target=&quot;_blank&quot; href=&quot;https://github.com/vinoski/erlsha2&quot;&gt;erlsha2&lt;/a&gt; implementation. Slides are &lt;a rel=&quot;nofollow&quot;&gt;here&lt;/a&gt; (pdf).&lt;/p&gt;</description>
	<pubDate>Mon, 28 Mar 2011 18:00:22 +0000</pubDate>
</item>
<item>
	<title>Programming in the 21st Century: If You're Not Gonna Use It, Why Are You Building It?</title>
	<guid>http://prog21.dadgum.com/94.html</guid>
	<link>http://prog21.dadgum.com/94.html</link>
	<description>Just about every image editing or photo editing program I've tried has a big collection of visual filters. There's one to make an image look like a mosaic, one to make it look like watercolors, and so on. Except for few of the most fundamental image adjustments, like saturation and sharpness, I never use any of them.
&lt;br /&gt;&lt;br /&gt;I have this suspicion that the programmers of these tools got hold of some image processing textbooks and implemented everything in them. If an algorithm had any tweakable parameters, then those were exposed to the user as sliders.
&lt;br /&gt;&lt;br /&gt;Honestly, that sounds like something I might have done in the past. The process of implementing those filters is purely technical--almost mechanical--yet it makes the feature list longer and more impressive. And they could be fun to code up. But no consideration is given to if those filters have any practical value.
&lt;br /&gt;&lt;br /&gt;Contrast this with apps like &lt;a href=&quot;http://instagr.am/&quot;&gt;Instagram&lt;/a&gt; and &lt;a href=&quot;http://hipstamaticapp.com/&quot;&gt;Hipstamatic&lt;/a&gt;. Those programs use your phone's camera to grab images, then apply built-in filters to them. They're fully automatic; you can't make any manual adjustments. And yet unlike all of those filter-laden photo editors I've used in the past, I'm completely hooked on Hipstamatic. It rekindled my interest in photography, and I can't thank the authors enough.
&lt;br /&gt;&lt;br /&gt;What's the difference between those apps and old-fashioned photo editors?
&lt;br /&gt;&lt;br /&gt;The Hipstamatic and Instagram filters were designed with clear goals in mind: to emulate certain retro-camera aesthetics, to serve as starting points and inspirations for photographs. Or more succinctly: they were built to be used.
&lt;br /&gt;&lt;br /&gt;If you find yourself creating something, and you don't understand how it will be used, and you don't plan on using it yourself, then it's time to take a few steps back and reevaluate what you're doing.
&lt;br /&gt;&lt;br /&gt;(If you liked this, you might like &lt;a href=&quot;http://prog21.dadgum.com/80.html&quot;&gt;Advice to Aimless, Excited Programmers&lt;/a&gt;.)</description>
	<pubDate>Sat, 26 Mar 2011 06:00:00 +0000</pubDate>
</item>
<item>
	<title>RJ's Blog: Erlang rebar tutorial: generating releases and upgrades</title>
	<guid>http://www.metabrew.com/article/erlang-rebar-tutorial-generating-releases-upgrades</guid>
	<link>http://www.metabrew.com/article/erlang-rebar-tutorial-generating-releases-upgrades</link>
	<description>&lt;p&gt;During my experiments with rebar, I made a simple example app for testing upgrades and releases. This article will walk you through using rebar to create an application, lay it out properly, package and deploy it, and create and install new versions without downtime.&lt;/p&gt;

&lt;p&gt;The code accompanying this article is in various branches of &lt;a href=&quot;https://github.com/RJ/erlang_rebar_example_project&quot;&gt;github.com/RJ/erlang_rebar_example_project&lt;/a&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;N.B.&lt;/strong&gt; The &lt;a href=&quot;http://www.erlang.org/doc/design_principles/des_princ.html&quot;&gt;OTP
Design Principles&lt;/a&gt; docs are a good place to start if you want an overview of the OTP approach to Erlang apps and releases. However, rebar isn&amp;#8217;t (yet) part of OTP, so consider that background reading. Rebar makes things much easier.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2 id=&quot;creating_the_project&quot;&gt;Creating the project&lt;/h2&gt;

&lt;p&gt;Build rebar:&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;console&quot;&gt;&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;cd&lt;/span&gt; ~/src
&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; git clone https://github.com/basho/rebar.git
&lt;span class=&quot;go&quot;&gt;Initialized empty Git repository in /tmp/rebar/.git/&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;remote: Counting objects: 2651, done.&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;remote: Compressing objects: 100% (1344/1344), done.&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;remote: Total 2651 (delta 1540), reused 2227 (delta 1174)&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Receiving objects: 100% (2651/2651), 622.99 KiB | 495 KiB/s, done.&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Resolving deltas: 100% (1540/1540), done.&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;cd &lt;/span&gt;rebar &lt;span class=&quot;o&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; make
&lt;span class=&quot;go&quot;&gt;...snip....&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;==&amp;gt; rebar (compile)&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Congratulations! You now have a self-contained script called &amp;quot;rebar&amp;quot; in&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;your current working directory. Place this script anywhere in your path&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;and you can use rebar to build OTP-compliant apps.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Now we&amp;#8217;ll make a project directory called &amp;#8220;dummy_proj&amp;#8221;, copy rebar into it, and use rebar to generate a skeleton application:&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;console&quot;&gt;&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; mkdir -p ~/src/dummy_proj/apps
&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;cd&lt;/span&gt; ~/src/dummy_proj/
&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; cp ../rebar/rebar .
&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;cd &lt;/span&gt;apps
&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; ../rebar create-app &lt;span class=&quot;nv&quot;&gt;appid&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;dummy_proj
&lt;span class=&quot;go&quot;&gt;==&amp;gt; dummy_proj (create-app)&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Writing src/dummy_proj.app.src&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Writing src/dummy_proj_app.erl&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Writing src/dummy_proj_sup.erl&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;To the skeleton, I added a basic gen_server called dummy_proj_server, which just keeps track of the number of times it was poked, i.e. it holds some state, for demonstration purposes.&lt;/p&gt;

&lt;p&gt;I also renamed dummy_proj_app.erl to just dummy_proj.erl, and added a start/0 function, which is useful when starting the application during developement, when not running from a generated release.&lt;/p&gt;

&lt;h3 id=&quot;compiling_with_rebar&quot;&gt;Compiling with rebar&lt;/h3&gt;

&lt;p&gt;You need a rebar.conf, place this in the top-level project directory:&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;erlang&quot;&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sub_dirs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;
            &lt;span class=&quot;s&quot;&gt;&amp;quot;apps/dummy_proj&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;s&quot;&gt;&amp;quot;rel&amp;quot;&lt;/span&gt;
           &lt;span class=&quot;p&quot;&gt;]}.&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;erl_opts&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;debug_info&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fail_on_warning&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]}.&lt;/span&gt;

&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;require_otp_vsn&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&amp;quot;R14&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;And now to compile, you do:&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;console&quot;&gt;&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; ./rebar compile
&lt;span class=&quot;go&quot;&gt;==&amp;gt; dummy_proj (compile)&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Compiled src/dummy_proj_sup.erl&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Compiled src/dummy_proj.erl&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Compiled src/dummy_proj_server.erl&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;==&amp;gt; rel (compile)&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;==&amp;gt; dummy_proj (compile)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Note that you now have .beam files in apps/dummy_proj/ebin/, and the .app.src generated apps/dummy_proj/ebin/dummy_proj.app for you, with a complete modules list.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;N.B.&lt;/strong&gt; I made a simple Makefile that calls &amp;#8216;rebar compile&amp;#8217;, because I&amp;#8217;m too used to typing make. Find it in the git repo.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3 id=&quot;running_your_app_development&quot;&gt;Running your app (development)&lt;/h3&gt;

&lt;p&gt;Here&amp;#8217;s how you can start the application (and sasl, for nice error reporting):&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;console&quot;&gt;&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; erl -pa apps/*/ebin -boot start_sasl -s dummy_proj
&lt;span class=&quot;go&quot;&gt;...snip...&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;=INFO REPORT==== 16-Mar-2011::14:17:04 ===&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Starting dummy_proj application...&lt;/span&gt;

&lt;span class=&quot;go&quot;&gt;=PROGRESS REPORT==== 16-Mar-2011::14:17:04 ===&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;          supervisor: {local,dummy_proj_sup}&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;             started: [{pid,&amp;lt;0.45.0&amp;gt;},&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;                       {name,dummy_proj_server},&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;                       {mfargs,{dummy_proj_server,start_link,[]} },&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;                       {restart_type,permanent},&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;                       {shutdown,5000},&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;                       {child_type,worker}]&lt;/span&gt;

&lt;span class=&quot;go&quot;&gt;=PROGRESS REPORT==== 16-Mar-2011::14:17:04 ===&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;         application: dummy_proj&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;          started_at: nonode@nohost&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Eshell V5.8.1  (abort with ^G)&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;1&amp;gt; dummy_proj_server:num_pokes().&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;0&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;2&amp;gt; dummy_proj_server:poke().     &lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;{ok,1}&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;3&amp;gt; dummy_proj_server:poke().&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;{ok,2}&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;4&amp;gt; dummy_proj_server:num_pokes().&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;2&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;5&amp;gt;  &lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Now you have a nice sensibly structured Erlang project that you can compile with rebar. Exit the VM with q(). and let&amp;#8217;s use rebar to package it up, so you can deploy it on a production box.&lt;/p&gt;

&lt;h2 id=&quot;generating_your_first_release&quot;&gt;Generating your first release&lt;/h2&gt;

&lt;p&gt;When you generate a release with rebar, and indeed if you use the erlang tools manually (not recommended, just use rebar), you end up with the whole Erlang VM and required libraries packaged up under one directory.&lt;/p&gt;

&lt;p&gt;This means you have a self-contained environment containing Erlang, the OTP libraries you need, and all your application code and dependencies. You can just tar it up, ship it over to another machine (of the same architecture, eg GNU/Linux 64-bit), and run it there.&lt;/p&gt;

&lt;h3 id=&quot;creating_a_node_config&quot;&gt;Creating a node config&lt;/h3&gt;

&lt;p&gt;Use rebar to create a default node configuration in a rel subdirectory:&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;console&quot;&gt;&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; mkdir rel
&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;cd &lt;/span&gt;rel/
&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; ../rebar create-node &lt;span class=&quot;nv&quot;&gt;nodeid&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;dummynode
&lt;span class=&quot;go&quot;&gt;==&amp;gt; rel (create-node)&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Writing reltool.config&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Writing files/erl&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Writing files/nodetool&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Writing files/dummynode&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Writing files/app.config&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Writing files/vm.args&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;You need to edit reltool.config a little; point to to your apps directory, and make sure the version number matches your .app.src file. You should also add dummy_app to the list of applications that are started as part of the release. Here&amp;#8217;s &lt;a href=&quot;https://github.com/RJ/erlang_rebar_example_project/blob/v1/rel/reltool.config&quot; target=&quot;v1rel&quot;&gt;reltool.conf from my v1 tag&lt;/a&gt;&lt;/p&gt;

&lt;h3 id=&quot;generating_the_release&quot;&gt;Generating the release&lt;/h3&gt;

&lt;p&gt;Back in the top level directory, just run:&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;console&quot;&gt;&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; ./rebar generate
&lt;span class=&quot;go&quot;&gt;==&amp;gt; rel (generate)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Now have a look in rel/dummynode. This is the release directory containing everything you need to run your application.&lt;/p&gt;

&lt;p&gt;We are going to be creating more releases later, so rename rel/dummynode to rel/dummynode_first, and then launch it using the handy script that rebar created for us:&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;console&quot;&gt;&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;cd &lt;/span&gt;rel/dummynode_first
&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; ./bin/dummynode console
&lt;span class=&quot;go&quot;&gt;...snip...&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Erlang R14B (erts-5.8.1) [source] [64-bit] [smp:8:8] [rq:8] [async-threads:5] [hipe] [kernel-poll:true&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;=INFO REPORT==== 16-Mar-2011::13:29:59 ===&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Starting dummy_proj application...&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Eshell V5.8.1  (abort with ^G)&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;(dummynode@127.0.0.1)1&amp;gt; &lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;(dummynode@127.0.0.1)1&amp;gt; dummy_proj_server:num_pokes().&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;0&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;(dummynode@127.0.0.1)2&amp;gt; dummy_proj_server:poke().     &lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;{ok,1}&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;(dummynode@127.0.0.1)3&amp;gt; dummy_proj_server:poke().&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;{ok,2}&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;(dummynode@127.0.0.1)4&amp;gt; dummy_proj_server:num_pokes().&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;2&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;(dummynode@127.0.0.1)5&amp;gt; &lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Now the release is running, we never want to have to restart it ever again, so open up another console because we want to leave that running whilst we work on version 2.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;N.B.&lt;/strong&gt; In a production environment, you would start with &amp;#8220;./bin/dummynode start&amp;#8221; so it runs in the background, and use &amp;#8220;dummynode attach&amp;#8221; to get a console.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Check the &lt;a href=&quot;https://github.com/RJ/erlang_rebar_example_project/blob/v1/&quot;&gt;'v1 branch'&lt;/a&gt; on github for code up to this point.&lt;/p&gt;

&lt;h2 id=&quot;upgrading_to_version_2&quot;&gt;Upgrading to Version 2&lt;/h2&gt;

&lt;p&gt;Add the poke_twice() function to dummy_proj_server.&lt;/p&gt;

&lt;p&gt;Change the version from &amp;#8220;1&amp;#8221; to &amp;#8220;2&amp;#8221;, in both apps/dummy_proj.app.src and rel/reltool.conf.&lt;/p&gt;

&lt;p&gt;Here&amp;#8217;s the github &lt;a href=&quot;https://github.com/RJ/erlang_rebar_example_project/compare/v1...v2#diff-3&quot;&gt;diff between v1...v2&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Erlang application version numbers can be any string - I tend to use a date format with letter: &amp;#8220;20110316a&amp;#8221;, but you can use any scheme you want. I tag releases in git with the same version as the erlang application. We&amp;#8217;ll just use &amp;#8220;1&amp;#8221;, &amp;#8220;2&amp;#8221;, &amp;#8220;3&amp;#8221; here for simplicity.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;N.B.&lt;/strong&gt; If you use &lt;code&gt;{vsn, git}&lt;/code&gt; as the version in your .app.src, rebar will get the version string from the closest git tag.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Now build the new version:&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;console&quot;&gt;&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; ./rebar compile
&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; ./rebar generate
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;So now you have rel/dummy_proj, containing a full release (VM included) of version 2. If you don&amp;#8217;t care about online-upgrades, you could just kill your version 1 VM, and start version 2 from this new release directory.&lt;/p&gt;

&lt;h3 id=&quot;writing_the_appup_upgrade_instructions&quot;&gt;Writing the .appup upgrade instructions&lt;/h3&gt;

&lt;p&gt;In order to make an upgrade, you must have a valid .appup file. This tells the erlang release_handler how to upgrade and downgrade between specific versions of your application.&lt;/p&gt;

&lt;p&gt;Rebar has a (relatively new) command called &amp;#8216;generate-appups&amp;#8217;. I&amp;#8217;ll show how it works, but ultimately we&amp;#8217;ll write our .appup manually, and keep it in our project directory (in git).&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;console&quot;&gt;&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; ./rebar generate-appups &lt;span class=&quot;nv&quot;&gt;previous_release&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;dummynode_first
&lt;span class=&quot;go&quot;&gt;==&amp;gt; rel (generate-appups)&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Generated appup for dummy_proj&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Appup generation complete&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; cat ./rel/dummynode/lib/dummy_proj-2/ebin/dummy_proj.appup
&lt;span class=&quot;gp&quot;&gt;%&lt;/span&gt;% appup generated &lt;span class=&quot;k&quot;&gt;for &lt;/span&gt;dummy_proj by rebar &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;2011/03/16 13:37:43&amp;quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;¬                                   
&lt;span class=&quot;go&quot;&gt;{&amp;quot;2&amp;quot;, [{&amp;quot;1&amp;quot;, [{update,dummy_proj_server,{advanced,[]}}]}], [{&amp;quot;1&amp;quot;, []}]}.¬&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Get rid of the autogenerated one, and create the appup file manually, in apps/dummy_proj/ebin/dummy_proj.appup:&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;erlang&quot;&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;2&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; 
    &lt;span class=&quot;c&quot;&gt;%% Upgrade instructions from 1 to 2&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;[{&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;1&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;load_module&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dummy_proj_server&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;    
    &lt;span class=&quot;p&quot;&gt;]}],&lt;/span&gt; 
    &lt;span class=&quot;c&quot;&gt;%% Downgrade instructions from 2 to 1&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;[{&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;1&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,[&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;load_module&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dummy_proj_server&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;    
    &lt;span class=&quot;p&quot;&gt;]}]&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;This .appup contains instructions for upgrading and downgrading between versions &amp;#8220;2&amp;#8221; and &amp;#8220;1&amp;#8221;. Typically the downgrade instructions are the reverse of the upgrade instructions. Since we just added a function to our server process, without changing any internal state, we can just use load_module instructions. The Appup Cookbook explains the various upgrade instructions in depth.&lt;/p&gt;

&lt;p&gt;Now generate again, overwriting the previous version 2. This will just make sure the .appup is part of the release directory:&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;console&quot;&gt;&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; ./rebar generate -f
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;And now, create the upgrade package:&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;console&quot;&gt;&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; ./rebar generate-upgrade &lt;span class=&quot;nv&quot;&gt;previous_release&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;dummynode_first
&lt;span class=&quot;go&quot;&gt;==&amp;gt; rel (generate-upgrade)&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;dummynode_2 upgrade package created&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;The generate-upgrade command will look for rel/dummynode as the current version, and rel/dummynode_first as the previous version. It should have created the upgrade .tar.gz in rel:&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;console&quot;&gt;&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; ls -lh rel/
&lt;span class=&quot;go&quot;&gt;total 15M&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;drwxr-xr-x 8 rj rj 4.0K 2011-03-16 13:42 dummynode&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;drwxr-xr-x 8 rj rj 4.0K 2011-03-16 13:29 dummynode_first&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;-rw-r--r-- 1 rj rj  14M 2011-03-16 13:45 dummynode_2.tar.gz&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;drwxr-xr-x 2 rj rj 4.0K 2011-03-16 13:11 files&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;-rw-r--r-- 1 rj rj  922 2011-03-16 13:36 reltool.config&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;h3 id=&quot;installing_the_upgrade_package&quot;&gt;Installing the upgrade package&lt;/h3&gt;

&lt;p&gt;You should still have the VM running from dummynode_first. Make sure you called poke(), so the internal state is something other than the default. This will help illustrate that the upgrade worked seamlessly.&lt;/p&gt;

&lt;p&gt;Copy the upgrade package to the releases directory of the running release:&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;console&quot;&gt;&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; cp rel/dummynode_2.tar.gz rel/dummynode_first/releases
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Now, at the Erlang console where version 1 is running, we use release_handler to check which releases are currently available, and install our new one:&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;console&quot;&gt;&lt;span class=&quot;go&quot;&gt;(dummynode@127.0.0.1)5&amp;gt; release_handler:which_releases().&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;[{&amp;quot;dummynode&amp;quot;,&amp;quot;1&amp;quot;,[],permanent}]&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;(dummynode@127.0.0.1)6&amp;gt; release_handler:unpack_release(&amp;quot;dummynode_2&amp;quot;).&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;{ok,&amp;quot;2&amp;quot;}&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;(dummynode@127.0.0.1)7&amp;gt; release_handler:install_release(&amp;quot;2&amp;quot;).&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;{ok,&amp;quot;1&amp;quot;,[]}   &lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;(dummynode@127.0.0.1)8&amp;gt; dummy_proj_server:num_pokes().&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;2&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;(dummynode@127.0.0.1)9&amp;gt; dummy_proj_server:poke_twice().&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;{ok,4}&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;(dummynode@127.0.0.1)10&amp;gt; dummy_proj_server:num_pokes(). &lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;4&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;(dummynode@127.0.0.1)11&amp;gt; release_handler:which_releases().&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;[{&amp;quot;dummynode&amp;quot;,&amp;quot;2&amp;quot;,&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;  [&amp;quot;kernel-2.14.1&amp;quot;,&amp;quot;stdlib-1.17.1&amp;quot;,&amp;quot;dummy_proj-2&amp;quot;,&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;   &amp;quot;sasl-2.1.9.2&amp;quot;,&amp;quot;compiler-4.7.1&amp;quot;,&amp;quot;crypto-2.0.1&amp;quot;,&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;   &amp;quot;syntax_tools-1.6.6&amp;quot;,&amp;quot;edoc-0.7.6.7&amp;quot;,&amp;quot;et-1.4.1&amp;quot;,&amp;quot;gs-1.5.13&amp;quot;,&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;   &amp;quot;hipe-3.7.7&amp;quot;,&amp;quot;inets-5.5&amp;quot;,&amp;quot;mnesia-4.4.15&amp;quot;,&amp;quot;observer-0.9.8.3&amp;quot;,&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;   &amp;quot;public_key-0.8&amp;quot;,&amp;quot;runtime_tools-1.8.4.1&amp;quot;,&amp;quot;ssl-4.0.1&amp;quot;,&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;   &amp;quot;tools-2.6.6.1&amp;quot;,&amp;quot;webtool-0.8.7&amp;quot;,&amp;quot;wx-0.98.7&amp;quot;,&amp;quot;xmerl-1.2.6&amp;quot;],&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;  current},&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt; {&amp;quot;dummynode&amp;quot;,&amp;quot;1&amp;quot;,[],permanent}]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;The upgrade worked; you can see that the num_pokes() was preserved, and that the new poke_twice() function is available.&lt;/p&gt;

&lt;p&gt;release_handler shows our version 2 as &amp;#8220;current&amp;#8221;, and the original version 1 as &amp;#8220;permanent&amp;#8221;. This means that although version 2 is running right now, if you restart the VM, version &amp;#8220;1&amp;#8221; will be booted up.&lt;/p&gt;

&lt;p&gt;If you are happy with the upgrade, make it permanent, meaning it will boot instead of version 1 if you restart the VM:&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;console&quot;&gt;&lt;span class=&quot;go&quot;&gt;(dummynode@127.0.0.1)12&amp;gt; release_handler:make_permanent(&amp;quot;2&amp;quot;).&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Check the &lt;a href=&quot;https://github.com/RJ/erlang_rebar_example_project/blob/v2/&quot;&gt;'v2 branch'&lt;/a&gt; on github for code up to this point.&lt;/p&gt;

&lt;h2 id=&quot;version_3_and_beyond&quot;&gt;Version 3 and beyond&lt;/h2&gt;

&lt;p&gt;The upgrade from v1 to v2 was simple: we just added a fun without changing the internal #state{} record.&lt;/p&gt;

&lt;p&gt;Erlang .appup files can do all sorts of clever stuff, allowing you to rewire your running applications during the upgrade process.&lt;/p&gt;

&lt;p&gt;The &lt;a href=&quot;http://www.erlang.org/doc/design_principles/appup_cookbook.html&quot;&gt;Appup
Cookbook&lt;/a&gt; details the various commands you can put in your .appup.&lt;/p&gt;

&lt;p&gt;Let&amp;#8217;s do an upgrade with a more complex appup - we&amp;#8217;ll change the #state record in the dummy_proj_server process.&lt;/p&gt;

&lt;p&gt;For version 3, we&amp;#8217;ll track prods as well as pokes, which will require another field in the state record.&lt;/p&gt;

&lt;p&gt;Here&amp;#8217;s the github &lt;a href=&quot;https://github.com/RJ/erlang_rebar_example_project/compare/v2...v3#diff-2&quot;&gt;diff between v2...v3&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Check out the addition to .appup for this release:&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;erlang&quot;&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;3&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; 
    &lt;span class=&quot;c&quot;&gt;%% Upgrade instructions&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;[{&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;2&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;update&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;dummy_app_server&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;advanced&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;from2to3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]}}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;]}],&lt;/span&gt; 
    &lt;span class=&quot;c&quot;&gt;%% Downgrade instructions&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;[{&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;2&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,[&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;update&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;dummy_app_server&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;advanced&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;from3to2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]}}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;]}]&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;This {update..} directive will result in the code_change function being called on the dummy_app_server. The purpose of code_change is to change the State from the old (v2) format, to the new (v3) format.&lt;/p&gt;

&lt;p&gt;Although it&amp;#8217;s not strictly necessary, I pass &amp;#8216;from2to3&amp;#8217; as the &amp;#8216;Extra&amp;#8217; field in the code_change call. This can be pattern matched on, and makes it clear in your code_change code exactly what version upgrade is expected.&lt;/p&gt;

&lt;h3 id=&quot;packaging_and_upgrading_to_v3&quot;&gt;Packaging and upgrading to v3&lt;/h3&gt;

&lt;p&gt;Move the generated release dir for v2:&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;console&quot;&gt;&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; mv rel/dummynode rel/dummynode_2
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Compile and generate for v3, then create the upgrade package:&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;console&quot;&gt;&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; ./rebar compile
&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; ./rebar generate
&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; ./rebar generate-upgrade &lt;span class=&quot;nv&quot;&gt;previous_release&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;dummynode_2
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;N.B.&lt;/strong&gt; You need to provide the full, standalone generated release dir as the previous_release, you can&amp;#8217;t use dummynode_first, even though that contains version 2 of your release.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;As before, copy the upgrade package to the releases directory of the running release:&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;console&quot;&gt;&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; cp rel/dummynode_3.tar.gz rel/dummynode_first/releases
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Now, at the Erlang console where you upgraded v1 to v2:&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;console&quot;&gt;&lt;span class=&quot;go&quot;&gt;(dummynode@127.0.0.1)12&amp;gt; release_handler:unpack_release(&amp;quot;dummynode_3&amp;quot;).&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;{ok,&amp;quot;3&amp;quot;}&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;(dummynode@127.0.0.1)13&amp;gt; release_handler:install_release(&amp;quot;3&amp;quot;).&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;{ok,&amp;quot;2&amp;quot;,[]}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;h3 id=&quot;congratulations&quot;&gt;Congratulations&lt;/h3&gt;

&lt;p&gt;Now you can deploy hot-code-upgrades the proper OTP way. Ideal for complex or large upgrades that change internal state or do require special upgrade hooks. Read the Appup Cookbook a few times, and &lt;strong&gt;test your upgrade packages in a staging environment before deploying&lt;/strong&gt;. You can just tar up and copy the live environment to your staging box, to get an exact clone of the production system to test upgrades against.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Warning: a current issue with downgrades&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;To downgrade, you just install a previous release. However, there is currently a bug where release_handler chokes during downgrades to the first version, because of a discrepancy in the naming of .boot files in the release. release_handler has start.boot hardcoded, but rebar will generate appname.boot, with start.boot as a symlink. If you need to do downgrades, test this carefully before deploying; you may need to manually rename the boot file.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2 id=&quot;cowboying_out_quick_fixes&quot;&gt;Cowboying out quick fixes&lt;/h2&gt;

&lt;p&gt;Appup files and generating releases is rather heavyweight. Here&amp;#8217;s an overview of the process I&amp;#8217;m using on &lt;a href=&quot;https://www.irccloud.com/&quot;&gt;IRCCloud&lt;/a&gt; at the moment&lt;/p&gt;

&lt;h4 id=&quot;complex_upgrades_are_proper_releases_with_appup&quot;&gt;Complex upgrades are proper releases, with .appup&lt;/h4&gt;

&lt;p&gt;No way around it; bit of a pain to create and test, but glorious when you pull off a complex upgrade with zero downtime. Releases are tagged in git with the datetime and letter version, eg: v20110324a. If I do a second release that day, v20110324b.&lt;/p&gt;

&lt;h4 id=&quot;hotfixes_preserve_my_sanity&quot;&gt;&amp;#8216;Hotfixes&amp;#8217; preserve my sanity&lt;/h4&gt;

&lt;p&gt;If I make a quick fix that simply requires reloading a module with no risk of instability, I do the following:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Reset code to currently deployed tag, egv 20110324a&lt;/li&gt;

&lt;li&gt;Write the fix&lt;/li&gt;

&lt;li&gt;Commit as v200110324a-hotfix1&lt;/li&gt;

&lt;li&gt;Build this version of the specific module that&amp;#8217;s changed, and copy it into the production environment, eg to: /somewhere/dummyapp/lib/dummyapp-20110324a/ebin/&lt;/li&gt;

&lt;li&gt;Reload the module, eg by using l(module_name). at the shell.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;It&amp;#8217;s of vital importance to have a repeatable process, so you know exactly which version of code (ie, the git tag) is currently running in production. If you can&amp;#8217;t be sure, then it&amp;#8217;s much harder to write successful upgrade code later on.&lt;/p&gt;

&lt;p&gt;This process gives a reasonable balance between periodic &amp;#8216;proper&amp;#8217; releases, during which any complex changes are made that rewire internal state, and quick fixes that just require a module reload.&lt;/p&gt;

&lt;h2 id=&quot;pitfalls_to_avoid&quot;&gt;Pitfalls to avoid&lt;/h2&gt;

&lt;p&gt;In the IRCCloud app, there&amp;#8217;s one place that I don&amp;#8217;t use a supervisor, but wish I had; the user process acts as a sort of supervisor for connection processes, because I needed exponential backoff / more control over restarting crashed children.&lt;/p&gt;

&lt;p&gt;Don&amp;#8217;t do that. release_handler isn&amp;#8217;t aware of the child processes I spawn myself, so I can&amp;#8217;t use the normal appup process to call code_change.&lt;/p&gt;</description>
	<pubDate>Sat, 26 Mar 2011 00:00:00 +0000</pubDate>
	<author>rj@metabrew.com (Richard Jones)</author>
</item>
<item>
	<title>erlang.org RSS Feed: Mailing Lists Operating</title>
	<guid>http://www.erlang.org/news/15</guid>
	<link>http://www.erlang.org/news/15</link>
	<description>&lt;p&gt;&lt;p&gt;
	The mailing lists at erlang.org are now back online after fixing a subtle Python &amp;quot;gotcha&amp;quot; configuration error.&lt;/p&gt;
&lt;p&gt;
	Please report any posts that you feel slipped into the void, or double posts or whatnot!&lt;/p&gt;
&lt;/p&gt;</description>
	<pubDate>Fri, 25 Mar 2011 18:52:58 +0000</pubDate>
</item>
<item>
	<title>Scattered Thoughts: telehash: bit_trees revisited</title>
	<guid>http://scattered-thoughts.net/one/1301/8124/662205</guid>
	<link>http://scattered-thoughts.net/one/1301/8124/662205</link>
	<description>&lt;p&gt;It has been suggested that the bit_trees presented in the last post are overly complicated. Indeed, in the cold light of the morning there is absolutely no need for that zipper. Without further ado, here is the much simpler version.
&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;% implements the tree part of kademlias k-buckets
% a bit_tree maps ends (lists of bits) to buckets
% as far as the bit_tree is concerned the buckets are completely opaque
% the bit_tree also calculates various numbers needed for splitting decisions

-module(bit_tree).

-include(&quot;conf.hrl&quot;).

-export([empty/2, update/4, iter/2]).

% a bit_tree is either a leaf or a branch
-record(leaf, {
          size, % size of bucket
          bucket % some opaque bucket of stuff
         }).
-record(branch, {
          size, % size(childF) + size(childT)
          childF, % tree containing nodes whose next bit is false
          childT % tree containing nodes whose next bit is true
         }).

% --- api ---

empty(Size, Bucket) -&amp;gt;
    #leaf{size=Size, bucket=Bucket}.
                
update(Fun, Bits, Self, Tree) when is_function(Fun), is_list(Bits), is_list(Self) -&amp;gt;
    update(Fun, Bits, {self, Self}, 0, Tree).

update(Fun, Bits, Gap, Depth, #leaf{bucket=Bucket}) -&amp;gt;
    Gap_size =
        case Gap of
            {gap, G} -&amp;gt; G;
            {self, _} -&amp;gt; 0
        end,
    bucket_update_to_tree(Fun(Bits, Depth, Gap_size, Bucket));
update(Fun, Bits, Self, Depth, #branch{childF=ChildF, childT=ChildT}) -&amp;gt;
    [Next|Bits2] = Bits,
    Self2 =
        case Self of
            {gap, _} -&amp;gt; Self;
            {self, [Next|Rest]} -&amp;gt; {self, Rest};
            {self, [false|_]} -&amp;gt; {gap, tree_size(ChildF)};
            {self, [true|_]} -&amp;gt; {gap, tree_size(ChildT)}
        end,
    Depth2 = Depth+1,
    case Next of
        true -&amp;gt;
            ChildT2 = update(Fun, Bits2, Self2, Depth2, ChildT),
            Size = tree_size(ChildF) + tree_size(ChildT2),
            #branch{size=Size, childF=ChildF, childT=ChildT2};
        false -&amp;gt;
            ChildF2 = update(Fun, Bits2, Self2, Depth2, ChildF),
            Size = tree_size(ChildF2) + tree_size(ChildT),
            #branch{size=Size, childF=ChildF2, childT=ChildT}
    end.

% iterate through buckets in ascending order of xor distance to Bits
iter(Bits, Tree) -&amp;gt;
    iter(Bits, Tree, fun() -&amp;gt; done end).
                             
iter(_Bits, #leaf{bucket=Bucket}, Iter) -&amp;gt;
    fun () -&amp;gt;
            {Bucket, Iter}
    end;
iter([Bit|Bits], #branch{childF=ChildF, childT=ChildT}, Iter) -&amp;gt;
    case Bit of
        true -&amp;gt;
            iter(Bits, ChildT, iter(Bits, ChildF, Iter));
        false -&amp;gt;
            iter(Bits, ChildF, iter(Bits, ChildT, Iter))
    end.

% --- internal functions ---

tree_size(#leaf{size=Size}) -&amp;gt;
    Size;
tree_size(#branch{size=Size}) -&amp;gt;
    Size.

bucket_update_to_tree({ok, Size, Bucket}) -&amp;gt;
    #leaf{size=Size, bucket=Bucket};
bucket_update_to_tree({split, SplitF, SplitT}) -&amp;gt;
    ChildF = bucket_update_to_tree(SplitF),
    ChildT = bucket_update_to_tree(SplitT),
    #branch{size=tree_size(ChildF)+tree_size(ChildT), childF=ChildF, childT=ChildT}.

% --- end ---
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And the corresponding test code.
&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;% simple buckets used for testing bit_tree

-module(test_bucket).

-include(&quot;conf.hrl&quot;).

-export([bits/1, add/3, split/1, add_to_tree/2, make_tree/1, distance/2, list_from/2]).

-define(MAX_SIZE, 3).
-define(BITS, ?END_BITS).

bits(Int) -&amp;gt;
    util:to_bits(&amp;lt;&amp;lt;Int:?BITS&amp;gt;&amp;gt;).

add(Suffix, Int, Bucket) -&amp;gt;
    split([{Suffix, Int} | Bucket]).

split(Bucket) -&amp;gt;
    if
        length(Bucket) &amp;gt; ?MAX_SIZE -&amp;gt;
            BucketF = [{Suffix2, Int2} || {[false | Suffix2], Int2} &amp;lt;- Bucket],
            BucketT = [{Suffix2, Int2} || {[true | Suffix2], Int2} &amp;lt;- Bucket],
            {split, split(BucketF), split(BucketT)};
        true -&amp;gt;
            {ok, length(Bucket), Bucket}
    end.

add_to_tree(Int, Tree) -&amp;gt;
    bit_tree:update(
      fun (Suffix, _Depth, _Gap_size, Bucket) -&amp;gt;
              add(Suffix, Int, Bucket)
      end,
      bits(Int),
      bits(Int), % dont care about gap for now
      Tree).

make_tree(Ints) -&amp;gt;
    Tree = bit_tree:empty(0, []),
    lists:foldl(fun add_to_tree/2, Tree, Ints).

distance(IntA, IntB) -&amp;gt;
    util:distance({'end', &amp;lt;&amp;lt;IntA:?BITS&amp;gt;&amp;gt;}, {'end', &amp;lt;&amp;lt;IntB:?BITS&amp;gt;&amp;gt;}).

% output *should* be in ascending order
list_from(Int, Tree) -&amp;gt;
    List = util:iter_to_list(bit_tree:iter(bits(Int), Tree)),
    lists:map(
      fun (Bucket) -&amp;gt;
              lists:sort([{distance(Int, Elem), Elem} || {_,Elem} &amp;lt;- Bucket])
      end,
      List).
&lt;/code&gt;&lt;/pre&gt;</description>
	<pubDate>Thu, 24 Mar 2011 23:08:44 +0000</pubDate>
</item>
<item>
	<title>Scattered Thoughts: telehash: bit_trees</title>
	<guid>http://scattered-thoughts.net/one/1300/995285/442278</guid>
	<link>http://scattered-thoughts.net/one/1300/995285/442278</link>
	<description>&lt;p&gt;The next step in building a switch is managing a routing table. Actually, the next step is handling sessions via _ring/_line but I'm still mulling over the protocol so we'll skip to the routing table.
&lt;/p&gt;
&lt;p&gt;I'll add the usual 'I don't understand Kademlia and I don't test my code' disclaimer in here.
&lt;/p&gt;
&lt;p&gt;Routing in the Kademlia paper is described using what can best be called the 'mash everything together and be vague about the details' pattern. I want my switch to be a bit cleaner than that so I've split it into three modules. The first of these is the bit_tree.
&lt;/p&gt;
&lt;p&gt;The bit_tree is a suffix tree which maps ends (lists of bits) to buckets. The bit_tree neither knows nor cares what a bucket is and for now you don't either. The utility of this tree comes down to one important property: the floor of the log (base 2) of the XOR distance between two ends is the height of the smallest sub-tree which contains both of them. Got that? For example, if log(distance(EndA,EndB)) == 7.234... then the height of the smallest sub-tree containing both EndA and EndB is 7 nodes. This makes it easy to locate the nearest known nodes to a specified end, something we are supposed to do in response to a &lt;b&gt;.see&lt;/b&gt; command.
&lt;/p&gt;
&lt;p&gt;So here is a bog-standard binary suffix tree:
&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;% a bit_tree is either a leaf or a branch
-record(leaf, {
          size, % size of bucket
          bucket % some opaque bucket of stuff
         }).
-record(branch, {
          size, % size(childF) + size(childT)
          childF, % tree containing nodes whose next bit is false
          childT % % tree containing nodes whose next bit is true
         }).
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;When adding nodes to a bucket we need to keep track of certain numbers which will be used by the router to decide when to split buckets. Some of these are quite complicated so to make this easier we will work with a zipper-like structure instead of using &lt;b&gt;leaf&lt;/b&gt; and &lt;b&gt;branch&lt;/b&gt; directly. If you know what a zipper is the code in this post will make sense. If you don't know what a zipper is, go find out. When you come back the code in this post will make sense.
&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;% zipper-esque structure marking a position in a bit_tree
-record(finger, {
          sizer, % a size function for buckets
          tree, % current sub-tree
          self, % the path *to* self (the nodes own end). either {down, Down_bits} or {up, Up_bits, Down_bits, Gap}
                % where Gap is the size of the largest tree containing self but not touching this finger
          depth, % the number of bits away from the root tree
          zipper % a list of {Bit, Tree} pairs marking branches NOT taken
         }).
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The finger keeps track of where the nodes own end is located in the tree in order to calculate something I have termed the gap - the size of the largest sub-tree containing the nodes own end but not touching the finger.
&lt;/p&gt;
&lt;p&gt;The empty bit_tree is easy to define:
&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;empty(Self, Bucket, Sizer) -&amp;gt;
    #finger{
       sizer = Sizer,
       tree = #leaf{size=Sizer(Bucket), bucket=Bucket},
       self = {down, Self},
       depth = 0,
       zipper = []
      }.
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Moving around within the tree is a little more complicated but if you already went away and read about zippers it should feel familiar. Most of the work is in keeping track of the gap.
&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;extend(Bits, #finger{tree=#leaf{}}=Finger) -&amp;gt; % must always end on a leaf
    {Bits, Finger};
extend([Next | Bits],
       #finger{
         tree = #branch{childF=ChildF, childT=ChildT},
         self = Self,
         depth = Depth,
         zipper = Zipper
        }=Finger) -&amp;gt;
    {Branch_taken, Branch_missed} =
        case Next of
            false -&amp;gt; {ChildF, ChildT};
            true -&amp;gt; {ChildT, ChildF}
        end,
    Self2 =
        case Self of
            {up, Up, Down, Gap} -&amp;gt;
                % already stepped out of gap
                {up, [not(Next)|Up], Down, Gap};
            {down, [Bit|Down]} when Bit == Next -&amp;gt;
                % still in the gap
                {down, Down};
            {down, [Bit|Down]} when Bit /= Next -&amp;gt;
                % leaving gap, check its size
                {up, [not(Next)], [Bit|Down], tree_size(Branch_missed)}
        end,
    Depth2 = Depth+1,
    Zipper2 = [{not(Next), Branch_missed} | Zipper],
    Finger2 = Finger#finger{
      tree = Branch_taken,
      self = Self2,
      depth = Depth2,
      zipper = Zipper2
     },
    extend(Bits, Finger2).
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;retract(0, Finger) -&amp;gt;
    Finger;
retract(N,
        #finger{
          tree = Tree,
          self = Self,
          depth = Depth,
          zipper = [{Last,Branch}|Zipper]
         }=Finger) when N&amp;gt;0 -&amp;gt;
    Size = tree_size(Tree) + tree_size(Branch),
    Tree2 =
        case Last of
            false -&amp;gt; #branch{size=Size, childF=Branch, childT=Tree};
            true -&amp;gt; #branch{size=Size, childF=Tree, childT=Branch}
        end,
    Self2 =
        case Self of
            {down, Down} -&amp;gt;
                % already in gap
                {down, [Last|Down]};
            {up, [], Down, _Gap} -&amp;gt;
                % just entered gap
                {down, [Last|Down]};
            {up, [Bit|Up], Down, Gap} -&amp;gt;
                % still outside gap
                true = (Bit==Last), % assert
                {up, Up, Down, Gap}
        end,
    Depth2 = Depth-1,
    Finger2 =
        Finger#finger{
          tree=Tree2,
          self=Self2,
          depth=Depth2,
          zipper=Zipper
         },
    retract(N-1, Finger2).
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;b&gt;extend&lt;/b&gt; and &lt;b&gt;retract&lt;/b&gt; functions are only used internally. We export a much simpler function, &lt;b&gt;move_to&lt;/b&gt;, which moves the finger to point at the bucket corresponding to the specified end.
&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;move_to(Bits, #finger{depth=Depth}=Finger) when length(Bits) == ?END_BITS -&amp;gt;
    % !!! naive version
    extend(Bits, retract(Depth, Finger)).
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We could make this more efficient by only retracting until the finger meets &lt;b&gt;Bits&lt;/b&gt; partway up. For now I don't expect performance of the bit_tree to be an issue.
&lt;/p&gt;
&lt;p&gt;Now that we can find buckets we can modify them. Deciding when to split buckets is not the concern of the bit_tree so we delegate it to the caller.
&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;update(Fun,
       #finger{
         sizer=Sizer,
         tree=#leaf{bucket=Bucket}
        }=Finger) -&amp;gt;
    Tree = bucket_update_to_tree(Sizer, Fun(Bucket)),
    Finger#finger{tree=Tree}.

bucket_update_to_tree(Sizer, {ok, Bucket}) -&amp;gt;
    #leaf{size=Sizer(Bucket), bucket=Bucket};
bucket_update_to_tree(Sizer, {split, SplitF, SplitT}) -&amp;gt;
    ChildF = bucket_update_to_tree(Sizer, SplitF),
    ChildT = bucket_update_to_tree(Sizer, SplitT),
    #branch{size=tree_size(ChildF)+tree_size(ChildT), childF=ChildF, childT=ChildT}.
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In order to handle &lt;b&gt;.see&lt;/b&gt; commands the &lt;b&gt;iter&lt;/b&gt; function is used to return buckets in order of distance from the specified end. Here we are making use of the aforementioned nice properties of the bit_tree in order to efficiently return the buckets in order.
&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;% iterate through buckets in ascending order of xor distance to (current position ++ Suffix)
iter(Suffix, #finger{tree=Tree, zipper=Zipper}) -&amp;gt;
    iter_buckets(Tree, Suffix, iter_zipper(Zipper, Suffix)).

% iterate through buckets in ascending order of xor distance to (current position ++ Suffix)
iter_zipper([], _Suffix) -&amp;gt;
    fun () -&amp;gt;
            done
    end;
iter_zipper([{Bit, Tree} | Zipper], Suffix) -&amp;gt;
    iter_buckets(Tree, Suffix, iter_zipper(Zipper, [not(Bit)|Suffix])).

% iterate through buckets in ascending order of xor distance to Bits, then hand over to Iter
iter_buckets(#leaf{bucket=Bucket}, _Bits, Iter) -&amp;gt;
    fun () -&amp;gt;
            {Bucket, Iter}
    end;
iter_buckets(#branch{childF=ChildF, childT=ChildT}, [Bit|Bits], Iter) -&amp;gt;
    case Bit of
        true -&amp;gt;
            iter_buckets(ChildT, Bits, iter_buckets(ChildF, Bits, Iter));
        false -&amp;gt;
            iter_buckets(ChildF, Bits, iter_buckets(ChildT, Bits, Iter))
    end.
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;It will typically be called like this:
&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;{Suffix, Tree2} = bit_tree:move_to(util:to_bits(End), Tree),
bit_tree:iter(Suffix, Tree2)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Splitting the routing table into separate structures like this makes for easier testing. The bit_tree can be tested independently using really simple buckets where the elements are just integers and the buckets split when they reach more than three elements.
&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;% simple buckets used for testing bit_tree

-module(test_bucket).

-include(&quot;conf.hrl&quot;).

-export([bits/1, add/3, split/1, move_to/2, add_to_tree/2, make_tree/2, distance/2, move_list_from/2, list_from/3]).

-define(MAX_SIZE, 3).
-define(BITS, ?END_BITS).

bits(Int) -&amp;gt;
    util:to_bits(&amp;lt;&amp;lt;Int:?BITS&amp;gt;&amp;gt;).

add(Suffix, Int, Bucket) -&amp;gt;
    split([{Suffix, Int} | Bucket]).

split(Bucket) -&amp;gt;
    if
        length(Bucket) &amp;gt; ?MAX_SIZE -&amp;gt;
            BucketF = [{Suffix2, Int2} || {[false | Suffix2], Int2} &amp;lt;- Bucket],
            BucketT = [{Suffix2, Int2} || {[true | Suffix2], Int2} &amp;lt;- Bucket],
            {split, split(BucketF), split(BucketT)};
        true -&amp;gt;
            {ok, Bucket}
    end.

move_to(Int, Tree) -&amp;gt;
    bit_tree:move_to(bits(Int), Tree).

add_to_tree(Int, Tree) -&amp;gt;
    {Suffix, Tree2} = move_to(Int, Tree),
    bit_tree:update(fun (Bucket) -&amp;gt; add(Suffix, Int, Bucket) end, Tree2).

make_tree(Int, Ints) -&amp;gt;
    Tree = bit_tree:empty(bits(Int), [], fun (Bucket) -&amp;gt; length(Bucket) end),
    lists:foldl(fun add_to_tree/2, Tree, Ints).

distance(IntA, IntB) -&amp;gt;
    util:distance({'end', &amp;lt;&amp;lt;IntA:?BITS&amp;gt;&amp;gt;}, {'end', &amp;lt;&amp;lt;IntB:?BITS&amp;gt;&amp;gt;}).

% output *should* be in ascending order
move_list_from(Int, Tree) -&amp;gt;
    {Suffix, Tree2} = bit_tree:move_to(bits(Int), Tree),
    list_from(Int, Suffix, Tree2).

list_from(Int, Suffix, Tree) -&amp;gt;
    List = util:iter_to_list(bit_tree:iter(Suffix, Tree)),
    lists:map(
      fun (Bucket) -&amp;gt;
              lists:sort([{distance(Int, Elem), Elem} || {_,Elem} &amp;lt;- Bucket])
      end,
      List).
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We can play around with the test buckets a bit:
&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;25&amp;gt; Tree = test_bucket:make_tree(47, lists:seq(1,1000)).
{finger,#Fun&amp;lt;test_bucket.1.121651971&amp;gt;,
        {leaf,1,[{[false,false,false],1000}]},
        {up,[false,true,false,false,false,false,false,true,true,
             true,true,true,true,true,true,true,true,true,true,true,true,
             true,true|...],
            [true,true,true,true,true,true,true,true,true,true,true,
             true,true,true,true,true,true,true,true,true,true,true|...],
            0},
        157,
        [{false,{branch,8,
                        {branch,4,
                                {leaf,2,[{[true],993},{[false],992}]},
                                {leaf,2,[{[true],995},{[false],994}]}},
                        {branch,4,
                                {leaf,2,[{[true],997},{[false],996}]},
                                {leaf,2,[{[true],999},{[false],998}]}}}},
         {true,{leaf,0,[]}},
         {false,{branch,32,
                        {branch,16,
                                {branch,8,
                                        {branch,4,
                                                {leaf,2,[{[true],961},{[...],...}]},
                                                {leaf,2,[{[...],...},{...}]}},
                                        {branch,4,
                                                {leaf,2,[{[...],...},{...}]},
                                                {leaf,2,[{...}|...]}}},
                                {branch,8,
                                        {branch,4,{leaf,2,[{[...],...},{...}]},{leaf,2,[{...}|...]}},
                                        {branch,4,{leaf,2,[{...}|...]},{leaf,2,[...]}}}},
                        {branch,16,
                                {branch,8,
                                        {branch,4,{leaf,2,[{[...],...},{...}]},{leaf,2,[{...}|...]}},
                                        {branch,4,{leaf,2,[{...}|...]},{leaf,2,[...]}}},
                                {branch,8,
                                        {branch,4,{leaf,2,[{...}|...]},{leaf,2,[...]}},
                                        {branch,4,{leaf,2,[...]},{leaf,2,...}}}}}},
         {false,{branch,64,
                        {branch,32,
                                {branch,16,
                                        {branch,8,
                                                {branch,4,{leaf,2,...},{leaf,...}},
                                                {branch,4,{leaf,...},{...}}},
                                        {branch,8,{branch,4,{leaf,...},{...}},{branch,4,{...},...}}},
                                {branch,16,
                                        {branch,8,{branch,4,{leaf,...},{...}},{branch,4,{...},...}},
                                        {branch,8,{branch,4,{...},...},{branch,4,...}}}},
                        {branch,32,
                                {branch,16,
                                        {branch,8,{branch,4,{leaf,...},{...}},{branch,4,{...},...}},
                                        {branch,8,{branch,4,{...},...},{branch,4,...}}},
                                {branch,16,
                                        {branch,8,{branch,4,{...},...},{branch,4,...}},
                                        {branch,8,{branch,4,...},{branch,...}}}}}},
         {false,{branch,128,
                        {branch,64,
                                {branch,32,
                                        {branch,16,
                                                {branch,8,{branch,...},{...}},
                                                {branch,8,{...},...}},
                                        {branch,16,{branch,8,{...},...},{branch,8,...}}},
                                {branch,32,
                                        {branch,16,{branch,8,{...},...},{branch,8,...}},
                                        {branch,16,{branch,8,...},{branch,...}}}},
                        {branch,64,
                                {branch,32,
                                        {branch,16,{branch,8,{...},...},{branch,8,...}},
                                        {branch,16,{branch,8,...},{branch,...}}},
                                {branch,32,
                                        {branch,16,{branch,8,...},{branch,...}},
                                        {branch,16,{branch,...},{...}}}}}},
         {false,{branch,256,
                        {branch,128,
                                {branch,64,
                                        {branch,32,{branch,16,{...},...},{branch,16,...}},
                                        {branch,32,{branch,16,...},{branch,...}}},
                                {branch,64,
                                        {branch,32,{branch,16,...},{branch,...}},
                                        {branch,32,{branch,...},{...}}}},
                        {branch,128,
                                {branch,64,
                                        {branch,32,{branch,16,...},{branch,...}},
                                        {branch,32,{branch,...},{...}}},
                                {branch,64,
                                        {branch,32,{branch,...},{...}},
                                        {branch,32,{...},...}}}}},
         {false,{branch,511,
                        {branch,255,
                                {branch,127,
                                        {branch,63,{branch,31,...},{branch,...}},
                                        {branch,64,{branch,...},{...}}},
                                {branch,128,
                                        {branch,64,{branch,...},{...}},
                                        {branch,64,{...},...}}},
                        {branch,256,
                                {branch,128,
                                        {branch,64,{branch,...},{...}},
                                        {branch,64,{...},...}},
                                {branch,128,{branch,64,{...},...},{branch,64,...}}}}},
         {true,{leaf,0,[]}},
         {true,{leaf,0,[]}},
         {true,{leaf,0,[]}},
         {true,{leaf,0,[]}},
         {true,{leaf,0,[]}},
         {true,{leaf,0,[]}},
         {true,{leaf,0,[]}},
         {true,{leaf,0,[]}},
         {true,{leaf,0,[]}},
         {true,{leaf,0,[]}},
         {true,{leaf,0,[]}},
         {true,{leaf,0,...}},
         {true,{leaf,...}},
         {true,{...}},
         {true,...},
         {...}|...]}
26&amp;gt; List = test_bucket:move_list_from(657, Tree).
[[{0,657},{1,656}],
 [{2,659},{3,658}],
 [{4,661},{5,660}],
 [{6,663},{7,662}],
 [{8,665},{9,664}],
 [{10,667},{11,666}],
 [{12,669},{13,668}],
 [{14,671},{15,670}],
 [{16,641},{17,640}],
 [{18,643},{19,642}],
 [{20,645},{21,644}],
 [{22,647},{23,646}],
 [{24,649},{25,648}],
 [{26,651},{27,650}],
 [{28,653},{29,652}],
 [{30,655},{31,654}],
 [{32,689},{33,688}],
 [{34,691},{35,690}],
 [{36,693},{37,692}],
 [{38,695},{39,694}],
 [{40,697},{41,696}],
 [{42,699},{43,698}],
 [{44,701},{45,700}],
 [{46,703},{47,702}],
 [{48,673},{49,672}],
 [{50,675},{51,...}],
 [{52,...},{...}],
 [{...}|...],
 [...]|...]
27&amp;gt; lists:flatten(List) == lists:sort(lists:flatten(List)).
true
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;As usual the full code is in the &lt;a href=&quot;http://github.com/jamii/erl-telehash&quot;&gt;repo&lt;/a&gt;.
&lt;/p&gt;</description>
	<pubDate>Thu, 24 Mar 2011 19:34:45 +0000</pubDate>
</item>
<item>
	<title>erlang.org RSS Feed: New Site Look</title>
	<guid>http://www.erlang.org/news/14</guid>
	<link>http://www.erlang.org/news/14</link>
	<description>&lt;p&gt;&lt;p&gt;
	Our demo site has now been launched as the regular erlang.org. We hope you like it. Report any problems.&lt;/p&gt;
&lt;/p&gt;</description>
	<pubDate>Thu, 24 Mar 2011 18:11:55 +0000</pubDate>
</item>
<item>
	<title>Trapexit's Erlang Blog Filter: Don&amp;#8217;t Lose Your ets Tables</title>
	<guid>http://steve.vinoski.net/blog/?p=722</guid>
	<link>http://steve.vinoski.net/blog/2011/03/23/dont-lose-your-ets-tables/</link>
	<description>&lt;p&gt;In my &lt;a rel=&quot;nofollow&quot; target=&quot;_blank&quot; href=&quot;http://qconlondon.com/london-2011/file?path=/qcon-london-2011/slides/SteveVinoski_LetItCrashExceptWhenYouShouldnt.pdf&quot;&gt;recent QCon talk&lt;/a&gt; I talked about accidentally crashing an Erlang process on a customer&amp;#8217;s subscription streaming video website running live in production. The code involved had not been used in production before, and the customer had decided somewhat unexpectedly to turn on a new feature that required it. The developer who wrote it had not tested it and had long since left the company.&lt;/p&gt;
&lt;p&gt;The purpose of the code was to monitor bandwidth and session usage for each video subscriber to make sure they weren&amp;#8217;t streaming more than they&amp;#8217;d paid for. Concerned about the viability of the code, a colleague and I logged into the customer site (with their permission, of course), chose a subscriber at random, and, in an Erlang shell, I interactively invoked a function in the code in question to check that subscriber&amp;#8217;s current bandwidth and session count. After a second check, we saw the numbers dropping, potentially indicating the subscriber was logging out, and we wanted to make sure all went well when the subscriber completely stopped streaming. After waiting a bit, I interactively called the function again, and &amp;mdash; &lt;strong&gt;BAM!&lt;/strong&gt; &amp;mdash; the process holding session state for all paying customers crashed.&lt;/p&gt;
&lt;p&gt;The original developer had used an Erlang &lt;code&gt;ets&lt;/code&gt; table, an in-memory data store, to hold the subscriber data, and wrote something like this for lookups:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;[SubscriberData] = ets:lookup(Table, Subscriber),&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;My interactive call from the shell looked up a nonexistent subscriber, so the result was the empty list &lt;code&gt;[]&lt;/code&gt; rather than &lt;code&gt;[SubscriberData]&lt;/code&gt;, which caused a pattern mismatch and a &lt;code&gt;badmatch&lt;/code&gt; exception. Uncaught, the exception crashed the process. Since the process owned the &lt;code&gt;ets&lt;/code&gt; table, when it went down it took the &lt;code&gt;ets&lt;/code&gt; table and all subscriber session data with it. It wasn&amp;#8217;t so bad, since all it meant was that for a few hours a few subscribers potentially got a bit more video than they&amp;#8217;d paid for, but still, it&amp;#8217;s not at all the kind of design Erlang&amp;#8217;s &lt;a rel=&quot;nofollow&quot; target=&quot;_blank&quot; href=&quot;http://www.erlang.org/download/armstrong_thesis_2003.pdf&quot;&gt;&amp;#8220;Let It Crash&amp;#8221; philosophy&lt;/a&gt; actually encourages. Crashing a process when something unexpected occurs is perfectly fine, since coding defensively introduces problems of its own, but you can still avoid losing your &lt;code&gt;ets&lt;/code&gt; tables like this relatively easily.&lt;/p&gt;
&lt;h3&gt;Name an Heir&lt;/h3&gt;
&lt;p&gt;When you create an &lt;code&gt;ets&lt;/code&gt; table you can also name a process to inherit the table should the creating process die:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;TableId = ets:new(my_table, [{heir, SomeOtherProcess, HeirData}]),&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If the creating process dies, the process &lt;code&gt;SomeOtherProcess&lt;/code&gt; will receive a message of the form&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;{'ETS-TRANSFER', TableId, OldOwner, HeirData}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;where &lt;code&gt;TableId&lt;/code&gt; is the table identifier returned from &lt;code&gt;ets:new&lt;/code&gt;, &lt;code&gt;OldOwner&lt;/code&gt; is the pid of the process that owned the table, and &lt;code&gt;HeirData&lt;/code&gt; is the data provided with the &lt;code&gt;heir&lt;/code&gt; option passed to &lt;code&gt;ets:new&lt;/code&gt;. Once it receives this message, &lt;code&gt;SomeOtherProcess&lt;/code&gt; owns the table.&lt;/p&gt;
&lt;h3&gt;Give It Away&lt;/h3&gt;
&lt;p&gt;Alternatively, you can create an &lt;code&gt;ets&lt;/code&gt; table and then give it to some other process to keep it:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;TableId = ets:new(my_table, []),
ets:give_away(TableId, SomeOtherProcess, GiftData),&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;If the creating process dies, the process &lt;code&gt;SomeOtherProcess&lt;/code&gt; will receive a message of the form&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;{'ETS-TRANSFER', TableId, OldOwner, GiftData}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;where &lt;code&gt;TableId&lt;/code&gt; is the table identifier returned from &lt;code&gt;ets:new&lt;/code&gt;, &lt;code&gt;OldOwner&lt;/code&gt; is the pid of the process that owned the table, and &lt;code&gt;GiftData&lt;/code&gt; is the data provided in the &lt;code&gt;ets:give_away&lt;/code&gt; call. Once it receives this message, &lt;code&gt;SomeOtherProcess&lt;/code&gt; owns the table.&lt;/p&gt;
&lt;h3&gt;Table Manager&lt;/h3&gt;
&lt;p&gt;Instead of naming an heir or giving a table away, you can just have your Erlang &lt;code&gt;supervisor&lt;/code&gt; process create a child process whose sole task is to own the table. This process creates the table as a named public table, thus allowing other processes to know its name and read/write it directly, with &lt;code&gt;ets&lt;/code&gt; built-in concurrency protection dealing with any concurrency issues. Since the owner process does nothing more than create the table and then wait to be told to shut down, the likelihood of it crashing and taking the table with it is practically nil. The drawback here, though, is that the process actually using the table may have to coordinate with the owner process to ensure the table is available, and worse, it ends up using what is essentially a global variable &amp;mdash; the table name &amp;mdash; which can make code harder to read and maintain.&lt;/p&gt;
&lt;h3&gt;A Combination Approach&lt;/h3&gt;
&lt;p&gt;A nice way of managing &lt;code&gt;ets&lt;/code&gt; tables, though, is to use a combination of the three previous techniques:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;The Erlang &lt;code&gt;supervisor&lt;/code&gt; creates a table manager process. Since all this process does is manage the table, the likelihood of it crashing is very low.&lt;/li&gt;
&lt;li&gt;The table manager links itself to the table user process and traps exits, allowing it to receive an &lt;code&gt;EXIT&lt;/code&gt; message if the table user process dies unexpectedly.&lt;/li&gt;
&lt;li&gt;The table manager creates a table, names itself (&lt;code&gt;self()&lt;/code&gt;) as the heir, and then gives it away to the table user process.&lt;/li&gt;
&lt;li&gt;If the table user process dies, the table manager is informed of the process death and also inherits the table back.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Once it inherits the table, the table manager can then for example wait until the &lt;code&gt;supervisor&lt;/code&gt; recreates the table user process, and then repeat the steps above to give the table to the new table user process. Other variations on this approach, like maybe a small pool of child process clones that cooperate to transfer the table between them in case of error, are of course also possible. Even though there are still process coordination issues here (but nothing difficult), I like this approach because it avoids global named tables and takes advantage of Erlang's supervision hierarchy.&lt;/p&gt;
&lt;p&gt;The title of my QCon talk was &quot;Let It Crash...Except When You Shouldn't.&quot; This scenario is an example of &quot;when you shouldn't&quot; &amp;mdash; losing &lt;code&gt;ets&lt;/code&gt; data due to a process crash is easily avoided.&lt;/p&gt;</description>
	<pubDate>Wed, 23 Mar 2011 15:01:51 +0000</pubDate>
</item>
<item>
	<title>Scattered Thoughts: telehash: dialing</title>
	<guid>http://scattered-thoughts.net/one/1300/720479/161992</guid>
	<link>http://scattered-thoughts.net/one/1300/720479/161992</link>
	<description>&lt;p&gt;The next step in building a telehash switch is being able to dial.
&lt;/p&gt;
&lt;p&gt;First a disclaimer: this post reflects my current understanding of TeleHash and Kademlia and is highly likely to be wrong. This code has only received minimal testing. Properly testing a p2p network is not something I'm entirely sure how to do yet. Expect to see more on that in later posts.
&lt;/p&gt;
&lt;p&gt;Each TeleHash node and each key in the DHT is identified by a 160 bit sha1 hash (aka end). In the original Kademlia paper the node ids are selected at random but in TeleHash they are the hashed address (IP:port) of the node. This means that malicious nodes don't get to choose where they are inserted in the DHT.
&lt;/p&gt;
&lt;p&gt;Kademlia routing is based on the XOR distance between ends. This forms a metric space over the set of ends.
&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;distance(A, B) -&amp;gt;
    {'end', EndA} = to_end(A),
    {'end', EndB} = to_end(B),
    Bytes = lists:zip(binary_to_list(EndA), binary_to_list(EndB)),
    Xor = list_to_binary([ByteA bxor ByteB || {ByteA, ByteB} &amp;lt;- Bytes]),
    &amp;lt;&amp;lt;Dist:?END_BITS&amp;gt;&amp;gt; = Xor,
    Dist.
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The Kademlia paper defines two constants, &lt;b&gt;K&lt;/b&gt; and &lt;b&gt;A&lt;/b&gt;. &lt;b&gt;K&lt;/b&gt; controls the amount of redundant storage in the DHT and &lt;b&gt;A&lt;/b&gt; controls the number of parallel requests issued by each node. To insert a key into the DHT a node must be able to locate the &lt;b&gt;K&lt;/b&gt; nodes whose IDs are closest to the key. This process is called dialing.
&lt;/p&gt;
&lt;p&gt;Dialing works roughly as follows. Each node keeps track of all the other nodes it has seen. Upon receiving a &lt;b&gt;+end&lt;/b&gt; signal a node will reply with a &lt;b&gt;.see&lt;/b&gt; command containing the &lt;b&gt;K&lt;/b&gt; nodes it is aware of which are closest to the specified end. To dial an end we send a &lt;b&gt;+end&lt;/b&gt; signal to each of the &lt;b&gt;K&lt;/b&gt; closest nodes we are aware of. Then to each node contained in the &lt;b&gt;.see&lt;/b&gt; replies we send &lt;b&gt;+end&lt;/b&gt; signals, and so on until we run out of nodes to contact.
&lt;/p&gt;
&lt;p&gt;Now this is nice and simple and will work but it generates a huge amount of load on the network. To reduce this Kademlia introduces two additional rules. First, we only send up to &lt;b&gt;A&lt;/b&gt; signals at a time and don't send any new signals until previous signals have either generated a reply or timed out. Second, we finish early if at any point we have received replies from &lt;b&gt;K&lt;/b&gt; nodes which are closer to the end than all the nodes we are waiting to contact. The Kademlia paper proves that under reasonable assumptions about the knowledge of each node this still has a very high chance to return the correct results.
&lt;/p&gt;
&lt;p&gt;The dialer process is an event handler which has two important data structures. The first stores the dialer configuration:
&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;-record(conf, {
          target, % the end to dial
          timeout, % the timeout for the entire dialing process
          ref, caller % reply details
         }).
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The second record stores the state of the dialing process. The principle around which the dialer is designed is that the state record is a reflection of the outside world and the sole job of the dialer is to keep this record up to date while maintaining the invariants in the comments. This is often the way that I write code and I feel that it needs it's own post once I can articulate it properly. It's certainly heavily informed both by the designs in Okasaki's &lt;a href=&quot;http://books.google.co.uk/books?id=SxPzSTcTalAC&amp;printsec=frontcover&amp;dq=okasaki+purely+functional&amp;hl=en&amp;ei=6GiHTdTcKY_RcfjR_ZkD&amp;sa=X&amp;oi=book_result&amp;ct=result&amp;resnum=1&amp;ved=0CC4Q6AEwAA#v=onepage&amp;q&amp;f=false&quot;&gt;Purely Functional Data Structures&lt;/a&gt; and by Conal Elliott's ideas about &lt;a href=&quot;http://conal.net/blog/posts/denotational-design-with-type-class-morphisms/&quot;&gt;denotational semantics and type class morphisms&lt;/a&gt;.
&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;-record(state, {
          fresh, % nodes which have not yet been contacted
          pinged, % nodes which have been contacted and have not replied
          waiting, % nodes in pinged which were contacted less than ?DIAL_TIMEOUT ago
          ponged, % nodes which have been contacted and have replied
          seen % all nodes which have been seen
         }). % invariant: pq:length(waiting) = ?A or pq:empty(fresh)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The dialer module exports the &lt;b&gt;dial&lt;/b&gt; function which creates the records and starts the event handler.
&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;dial(To, From, Timeout) -&amp;gt;
    log:info([?MODULE, dialing, To, From, Timeout]),
    Ref = erlang:make_ref(),
    Target = util:to_end(To),
    Conf = #conf{
      target = Target,
      timeout = Timeout,
      ref = Ref,
      caller = self()
     },
    Nodes = [{util:distance(Address, Target), Address}
             || Address &amp;lt;- From],
    State = #state{
      fresh=pq:from_list(Nodes),
      pinged=sets:new(),
      waiting=pq:empty(),
      ponged=pq:empty(),
      seen=sets:new()
     },
    ok = switch:add_handler(?MODULE, {Conf, State}),
    Ref.
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The aim is to handle events and maintain the state invariants until we are finished. How do we define finished?
&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;% is the dialing finished yet?
finished(#state{fresh=Fresh, waiting=Waiting, ponged=Ponged}) -&amp;gt;
    (pq:is_empty(Fresh) and pq:is_empty(Waiting)) % no way to continue
    or
    (case pq:length(Ponged) &amp;gt;= ?K of
         false -&amp;gt;
             false; % dont yet have K nodes
         true -&amp;gt;
             % finish if the K closest nodes we know are closer than all the nodes we haven't checked yet
             {Dist_fresh, _} = pq:peek(Fresh),
             {Dist_waiting, _} = pq:peek(Waiting),
             {Nodes, _} = pq:pop(Ponged, ?K),
             {Dist_ponged, _} = lists:last(Nodes),
             (Dist_ponged &amp;lt; Dist_fresh) and (Dist_ponged &amp;lt; Dist_waiting)
     end).
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;One of the invariants we aim to maintain is that either the &lt;b&gt;fresh&lt;/b&gt; queue is empty or the length of the &lt;b&gt;waiting&lt;/b&gt; queue is A. This ensures that we send out &lt;b&gt;+end&lt;/b&gt; signals whenever possible. This invariant is maintained by calling the &lt;b&gt;ping_nodes&lt;/b&gt; function after every event.
&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;% contact nodes from fresh until the waiting list is full
ping_nodes(#conf{target=Target}, #state{fresh=Fresh, waiting=Waiting, pinged=Pinged}=State) -&amp;gt;
    Num = ?A - pq:length(Waiting),
    {Nodes, Fresh2} = pq:pop(Fresh, Num),
    Telex = {struct, [{'+end', util:end_to_hex(Target)}]},
    lists:foreach(
      fun ({Dist, Address}=Node) -&amp;gt;
              log:info([?MODULE, ping, Dist, Address]),
              switch:send(Address, Telex),
              erlang:send_after(?DIAL_TIMEOUT, self(), {timeout, Node})
      end,
      Nodes),
    Waiting2 = pq:push(Nodes, Waiting),
    Pinged2 = sets:union(Pinged, sets:from_list(Nodes)),
    State#state{fresh=Fresh2, waiting=Waiting2, pinged=Pinged2}.
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We handle replies by moving the replying node from the &lt;b&gt;waiting&lt;/b&gt; queue to the &lt;b&gt;ponged&lt;/b&gt; queue and inserting the &lt;b&gt;.see&lt;/b&gt; nodes into the &lt;b&gt;fresh&lt;/b&gt; list. We cannot allow duplicate nodes so the &lt;b&gt;seen&lt;/b&gt; set is kept up to date. The &lt;b&gt;pinged&lt;/b&gt; set will be used later to ensure that we only accept replies from nodes we have already contacted and only accept one reply per node.
&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;% handle a reply from a node
ponged(Node, See, #state{fresh=Fresh, waiting=Waiting, pinged=Pinged, ponged=Ponged, seen=Seen}=State) -&amp;gt;
    Waiting2 = pq:delete(Node, Waiting),
    Pinged2 = sets:del_element(Node, Pinged),
    Ponged2 = pq:push_one(Node, Ponged),
    New_nodes = lists:filter(fun (See_node) -&amp;gt; not(sets:is_element(See_node, Seen)) end, See),
    Fresh2 = pq:push(New_nodes, Fresh),
    Seen2 = sets:union(Seen, sets:from_list(See)),
    State#state{fresh=Fresh2, waiting=Waiting2, pinged=Pinged2, ponged=Ponged2, seen=Seen2}.
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Once we are finished we need to return the results to the caller.
&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;% return results to the caller
return(#conf{ref=Ref, caller=Caller}, #state{ponged=Ponged}) -&amp;gt;
    {Nodes, _} = pq:pop(Ponged, ?K),
    log:info([?MODULE, returning, Nodes]),
    Result = [Address || {_Dist, Address} &amp;lt;- Nodes],
    Caller ! {dialed, Ref, Result}.
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Finally, after each event we call &lt;b&gt;continue&lt;/b&gt; to decide whether to finish and return results or to carry on sending signals.
&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;% either continue to dial or return results
% meant for use at the end of a gen_event callback
continue(Conf, State) -&amp;gt;
    case finished(State) of
        true -&amp;gt;
            return(Conf, State),
            remove_handler;
        false -&amp;gt;
            State2 = ping_nodes(Conf, State),
            {ok, {Conf, State2}}
    end.
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The functions above are glued together by a gen_event handler. The handler is attached to the switch gen_event manager and receives an event for each telex arriving at the switch.
&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;-behaviour(gen_event).
-export([init/1, handle_event/2, handle_call/2, handle_info/2, terminate/2, code_change/3]).
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;b&gt;init&lt;/b&gt; function is called when the handler is started. It sends out the first &lt;b&gt;+end&lt;/b&gt; signals and sets a timer that tells the handler when to give up dialling.
&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;init({#conf{timeout=Timeout}=Conf, State}) -&amp;gt;
    erlang:send_after(Timeout, self(), giveup),
    State2 = ping_nodes(Conf, State),
    {ok, {Conf, State2}}.
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The giveup timeout is simple to deal with.
&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;handle_info(giveup, {Conf, State}) -&amp;gt;
    log:info([?MODULE, giveup, Conf, State]),
    remove_handler;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;As are the timeouts from individual signals.
&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;handle_info({timeout, Node}, {Conf, #state{waiting=Waiting}=State}) -&amp;gt;
    log:info([?MODULE, timeout, Node]),
    State2 = State#state{waiting=pq:delete(Node, Waiting)},
    continue(Conf, State2);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The last callback is the messiest. This essentially just calls &lt;b&gt;ponged&lt;/b&gt; and &lt;b&gt;continue&lt;/b&gt;, but first has to sanity check the incoming message.
&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;handle_event({recv, Address, Telex}, {#conf{target=Target}=Conf, #state{pinged=Pinged}=State}) -&amp;gt;
    case telex:get(Telex, '.see') of
        {error, not_found} -&amp;gt;
            {ok, {Conf, State}};
        {ok, Address_binaries} -&amp;gt;
            Dist = util:distance(Address, Target),
            Node = {Dist, Address},
            case sets:is_element(Node, Pinged) of % !!! command ids would make a better check
                false -&amp;gt;
                    {ok, {Conf, State}};
                true -&amp;gt;
                    try [{util:distance(Target, Bin), util:binary_to_address(Bin)} || Bin &amp;lt;- Address_binaries] of
                        Nodes -&amp;gt;
                            log:info([?MODULE, pong, Node, Nodes]),
                            State2 = ponged(Node, Nodes, State),
                            continue(Conf, State2)
                    catch
                        _:Error -&amp;gt;
                            log:info([?MODULE, bad_see, Address, Telex, Error, erlang:get_stacktrace()]),
                            {ok, {Conf, State}}
                    end
            end
    end;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;That's pretty much it - we now (probably) have a working dialer. I spent a fair few hours teasing this apart but hopefully the end result is fairly simple to understand. The full code is in the &lt;a href=&quot;http://github.com/jamii/erl-telehash&quot;&gt;repo&lt;/a&gt; as always.
&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;4&amp;gt; switch:start_link().
{ok,&amp;lt;0.79.0&amp;gt;,&amp;lt;0.80.0&amp;gt;}
5&amp;gt; Root = {address, &quot;208.68.163.247&quot;, 42424}.
{address,&quot;208.68.163.247&quot;,42424}
6&amp;gt; dialer:dial_sync(Root, [Root], 10000).

=INFO REPORT==== 21-Mar-2011::14:48:06 ===
    pid: &amp;lt;0.35.0&amp;gt;
    dialer
    dialing
    {address,&quot;208.68.163.247&quot;,42424}
    [{address,&quot;208.68.163.247&quot;,42424}]
    10000

=INFO REPORT==== 21-Mar-2011::14:48:06 ===
    pid: &amp;lt;0.79.0&amp;gt;
    dialer
    ping
    0
    {address,&quot;208.68.163.247&quot;,42424}

=INFO REPORT==== 21-Mar-2011::14:48:06 ===
    pid: &amp;lt;0.80.0&amp;gt;
    switch_event
    send
    {address,&quot;208.68.163.247&quot;,42424}
    struct: [{&amp;lt;&amp;lt;&quot;_to&quot;&amp;gt;&amp;gt;,&amp;lt;&amp;lt;&quot;208.68.163.247:42424&quot;&amp;gt;&amp;gt;},
             {'+end',&amp;lt;&amp;lt;&quot;38666817e1b38470644e004b9356c1622368fa57&quot;&amp;gt;&amp;gt;}]

=INFO REPORT==== 21-Mar-2011::14:48:07 ===
    pid: &amp;lt;0.80.0&amp;gt;
    switch_event
    recv
    {address,&quot;208.68.163.247&quot;,42424}
    struct: [{&amp;lt;&amp;lt;&quot;_ring&quot;&amp;gt;&amp;gt;,18115},
             {&amp;lt;&amp;lt;&quot;.see&quot;&amp;gt;&amp;gt;,
              [&amp;lt;&amp;lt;&quot;204.232.205.180:42424&quot;&amp;gt;&amp;gt;,&amp;lt;&amp;lt;&quot;208.68.163.247:42424&quot;&amp;gt;&amp;gt;]},
             {&amp;lt;&amp;lt;&quot;_br&quot;&amp;gt;&amp;gt;,240},
             {&amp;lt;&amp;lt;&quot;_to&quot;&amp;gt;&amp;gt;,&amp;lt;&amp;lt;&quot;203.218.138.245:42424&quot;&amp;gt;&amp;gt;}]

=INFO REPORT==== 21-Mar-2011::14:48:07 ===
    pid: &amp;lt;0.79.0&amp;gt;
    dialer
    pong
    0: {address,&quot;208.68.163.247&quot;,42424}
    [{535375931004298447338698443374311161987273280591,
      {address,&quot;204.232.205.180&quot;,42424}},
     {0,{address,&quot;208.68.163.247&quot;,42424}}]

=INFO REPORT==== 21-Mar-2011::14:48:07 ===
    pid: &amp;lt;0.79.0&amp;gt;
    dialer
    ping
    0
    {address,&quot;208.68.163.247&quot;,42424}

=INFO REPORT==== 21-Mar-2011::14:48:07 ===
    pid: &amp;lt;0.79.0&amp;gt;
    dialer
    ping
    535375931004298447338698443374311161987273280591
    {address,&quot;204.232.205.180&quot;,42424}

=INFO REPORT==== 21-Mar-2011::14:48:07 ===
    pid: &amp;lt;0.80.0&amp;gt;
    switch_event
    send
    {address,&quot;208.68.163.247&quot;,42424}
    struct: [{&amp;lt;&amp;lt;&quot;_to&quot;&amp;gt;&amp;gt;,&amp;lt;&amp;lt;&quot;208.68.163.247:42424&quot;&amp;gt;&amp;gt;},
             {'+end',&amp;lt;&amp;lt;&quot;38666817e1b38470644e004b9356c1622368fa57&quot;&amp;gt;&amp;gt;}]

=INFO REPORT==== 21-Mar-2011::14:48:07 ===
    pid: &amp;lt;0.80.0&amp;gt;
    switch_event
    send
    {address,&quot;204.232.205.180&quot;,42424}
    struct: [{&amp;lt;&amp;lt;&quot;_to&quot;&amp;gt;&amp;gt;,&amp;lt;&amp;lt;&quot;204.232.205.180:42424&quot;&amp;gt;&amp;gt;},
             {'+end',&amp;lt;&amp;lt;&quot;38666817e1b38470644e004b9356c1622368fa57&quot;&amp;gt;&amp;gt;}]

=INFO REPORT==== 21-Mar-2011::14:48:07 ===
    pid: &amp;lt;0.80.0&amp;gt;
    switch_event
    recv
    {address,&quot;204.232.205.180&quot;,42424}
    struct: [{&amp;lt;&amp;lt;&quot;_ring&quot;&amp;gt;&amp;gt;,16506},
             {&amp;lt;&amp;lt;&quot;.see&quot;&amp;gt;&amp;gt;,
              [&amp;lt;&amp;lt;&quot;204.232.205.180:42424&quot;&amp;gt;&amp;gt;,&amp;lt;&amp;lt;&quot;208.68.163.247:42424&quot;&amp;gt;&amp;gt;]},
             {&amp;lt;&amp;lt;&quot;_br&quot;&amp;gt;&amp;gt;,162},
             {&amp;lt;&amp;lt;&quot;_to&quot;&amp;gt;&amp;gt;,&amp;lt;&amp;lt;&quot;203.218.138.245:42424&quot;&amp;gt;&amp;gt;}]

=INFO REPORT==== 21-Mar-2011::14:48:07 ===
    pid: &amp;lt;0.79.0&amp;gt;
    dialer
    pong
    535375931004298447338698443374311161987273280591: {address,
                                                       &quot;204.232.205.180&quot;,
                                                       42424}
    [{535375931004298447338698443374311161987273280591,
      {address,&quot;204.232.205.180&quot;,42424}},
     {0,{address,&quot;208.68.163.247&quot;,42424}}]

=INFO REPORT==== 21-Mar-2011::14:48:07 ===
    pid: &amp;lt;0.80.0&amp;gt;
    switch_event
    recv
    {address,&quot;208.68.163.247&quot;,42424}
    struct: [{&amp;lt;&amp;lt;&quot;_ring&quot;&amp;gt;&amp;gt;,18115},
             {&amp;lt;&amp;lt;&quot;.see&quot;&amp;gt;&amp;gt;,
              [&amp;lt;&amp;lt;&quot;204.232.205.180:42424&quot;&amp;gt;&amp;gt;,&amp;lt;&amp;lt;&quot;208.68.163.247:42424&quot;&amp;gt;&amp;gt;]},
             {&amp;lt;&amp;lt;&quot;_br&quot;&amp;gt;&amp;gt;,320},
             {&amp;lt;&amp;lt;&quot;_to&quot;&amp;gt;&amp;gt;,&amp;lt;&amp;lt;&quot;203.218.138.245:42424&quot;&amp;gt;&amp;gt;}]

=INFO REPORT==== 21-Mar-2011::14:48:07 ===
    pid: &amp;lt;0.79.0&amp;gt;
    dialer
    pong
    0: {address,&quot;208.68.163.247&quot;,42424}
    [{535375931004298447338698443374311161987273280591,
      {address,&quot;204.232.205.180&quot;,42424}},
     {0,{address,&quot;208.68.163.247&quot;,42424}}]

=INFO REPORT==== 21-Mar-2011::14:48:07 ===
    pid: &amp;lt;0.79.0&amp;gt;
    dialer
    returning
    [{0,{address,&quot;208.68.163.247&quot;,42424}},
     {535375931004298447338698443374311161987273280591,
      {address,&quot;204.232.205.180&quot;,42424}}]
{ok,[{address,&quot;208.68.163.247&quot;,42424},
     {address,&quot;204.232.205.180&quot;,42424}]}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;One last note: after I finished writing this I started thinking about what would happen if I run more than one dialer in parallel. Unlike Kademlia, TeleHash does not currently use command IDs so the dialer cannot tell if the response came in reply to its own command or in reply to the command of another dialer on the same node. It's the kind of bug that would be very rare in actual use but might be carefully exploited by a malicious node. Finding these kinds of bugs is going to be really hard.
&lt;/p&gt;</description>
	<pubDate>Mon, 21 Mar 2011 15:14:39 +0000</pubDate>
</item>
<item>
	<title>Learn You Some Erlang: Who Supervises The Supervisors?</title>
	<guid>http://learnyousomeerlang.com/supervisors</guid>
	<link>http://learnyousomeerlang.com/supervisors</link>
	<description>Right in time before the Bay Area Erlang Conference, the supervisors make their place in Learn You Some Erlang. We see how to set up an OTP supervisor, the restart strategies available, how to write children specifications and have a little demonstration where a band manager takes pleasure at firing band members.</description>
	<pubDate>Fri, 18 Mar 2011 12:30:00 +0000</pubDate>
</item>
<item>
	<title>Process-one Blogs: Sea Beyond 2011 Talk 5: Marek Foss on designing mobile collaboration software</title>
	<guid>http://www.process-one.net/en/blogs/article/sea_beyond_2011_talk_5_marek_foss_on_designing_mobile_collaboration_softwar/</guid>
	<link>http://www.process-one.net/en/blogs/article/sea_beyond_2011_talk_5_marek_foss_on_designing_mobile_collaboration_softwar/</link>
	<description>&lt;p&gt;Sea Beyond event was this year heavily focused on mobile real time applications.&lt;/p&gt; &lt;p&gt;Marek Foss, Chief Web Officer at ProcessOne, gave a talk at Sea Beyond 2011 event, sharing some useful design oriented patterns for collaborative real time mobile applications.&lt;/p&gt;
&lt;p&gt;Here is the video of his presentation:&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;You can see the slides here:&lt;/p&gt;
&lt;p&gt;








&lt;/p&gt;
&lt;p&gt;Do not miss the &lt;a href=&quot;http://www.process-one.net/en/blogs/article/sea_beyond_2011_video_summary/&quot;&gt;event summary and the other videos from Sea Beyond event&lt;/a&gt;.&lt;/p&gt;</description>
	<pubDate>Thu, 17 Mar 2011 15:46:51 +0000</pubDate>
</item>
<item>
	<title>Scattered Thoughts: telehash: basics</title>
	<guid>http://scattered-thoughts.net/one/1300/366038/597949</guid>
	<link>http://scattered-thoughts.net/one/1300/366038/597949</link>
	<description>&lt;p&gt;&lt;a href=&quot;http://telehash.org&quot;&gt;TeleHash&lt;/a&gt; is a p2p network based on the &lt;a href=&quot;http://en.wikipedia.org/wiki/Kademlia&quot;&gt;Kademlia DHT&lt;/a&gt; that provides addressing and NAT traversal. These are problems that every p2p app has to deal with, including my &lt;a href=&quot;https://github.com/jamii/dissertation&quot;&gt;poppi&lt;/a&gt;. Unfortunately there is no erlang implementation yet so I have to roll my own. The code so far lives &lt;a href=&quot;http://github.com/jamii/erl-telehash&quot;&gt;here&lt;/a&gt; In this first post I'll just cover the absolute basics - sending, receiving, encoding and decoding messages.
&lt;/p&gt;
&lt;p&gt;TeleHash messages (telexes) are utf8-encoded json packets sent over udp. Luckily, mochijson2 uses utf8 by default so encoding/decoding is trivial.
&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;encode(Telex) -&amp;gt;
    mochijson2:encode(Telex).

decode(Json) -&amp;gt;
    mochijson2:decode(Json).
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;b&gt;telex&lt;/b&gt; module also defines some convenience methods for working with json - &lt;b&gt;get/2&lt;/b&gt;, &lt;b&gt;set/3&lt;/b&gt;, &lt;b&gt;update/4&lt;/b&gt; - which are used like this:
&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;2&amp;gt; T = telex:decode(&quot;{\&quot;foo\&quot;:[\&quot;bar\&quot;, {\&quot;baz\&quot;:0}]}&quot;).
{struct,[{&amp;lt;&amp;lt;&quot;foo&quot;&amp;gt;&amp;gt;,[&amp;lt;&amp;lt;&quot;bar&quot;&amp;gt;&amp;gt;,{struct,[{&amp;lt;&amp;lt;&quot;baz&quot;&amp;gt;&amp;gt;,0}]}]}]}
3&amp;gt; telex:get(T, foo).
[&amp;lt;&amp;lt;&quot;bar&quot;&amp;gt;&amp;gt;,{struct,[{&amp;lt;&amp;lt;&quot;baz&quot;&amp;gt;&amp;gt;,0}]}]
4&amp;gt; telex:get(T, {foo,2}).
{struct,[{&amp;lt;&amp;lt;&quot;baz&quot;&amp;gt;&amp;gt;,0}]}
5&amp;gt; telex:get(T, {foo,2,baz}).
0
6&amp;gt; telex:set(T, {foo,2,baz}, 1).
{struct,[{&amp;lt;&amp;lt;&quot;foo&quot;&amp;gt;&amp;gt;,[&amp;lt;&amp;lt;&quot;bar&quot;&amp;gt;&amp;gt;,{struct,[{&amp;lt;&amp;lt;&quot;baz&quot;&amp;gt;&amp;gt;,1}]}]}]}
7&amp;gt; telex:set(T, bigger, true).
{struct,[{&amp;lt;&amp;lt;&quot;bigger&quot;&amp;gt;&amp;gt;,true},
         {&amp;lt;&amp;lt;&quot;foo&quot;&amp;gt;&amp;gt;,[&amp;lt;&amp;lt;&quot;bar&quot;&amp;gt;&amp;gt;,{struct,[{&amp;lt;&amp;lt;&quot;baz&quot;&amp;gt;&amp;gt;,0}]}]}]}
8&amp;gt; telex:update(T, {foo,2,baz}, fun (X) -&amp;gt; X + 10 end, -1).
{struct,[{&amp;lt;&amp;lt;&quot;foo&quot;&amp;gt;&amp;gt;,[&amp;lt;&amp;lt;&quot;bar&quot;&amp;gt;&amp;gt;,{struct,[{&amp;lt;&amp;lt;&quot;baz&quot;&amp;gt;&amp;gt;,10}]}]}]}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The next step is to be able to send and receive messages. The &lt;b&gt;switch&lt;/b&gt; module runs a gen_server which manages the udp socket and a gen_event which allows other processes to subscribe to incoming messages.
&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;-module(switch).

-include(&quot;conf.hrl&quot;).

-export([start_link/0, add_handler/2, add_sup_handler/2, send/2]).

-behaviour(gen_server).
-export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3]).

-record(state, {socket}).
-define(EVENT, switch_event).
-define(SERVER, switch_server).

% --- api ---

start_link() -&amp;gt;
    {ok, Gen_event} = gen_event:start_link({local, ?EVENT}),
    {ok, Gen_server} = gen_server:start_link({local, ?SERVER}, ?MODULE, [], []),
    {ok, Gen_event, Gen_server}.

add_handler(Module, Args) -&amp;gt;
    gen_event:add_handler(?EVENT, Module, Args).

add_sup_handler(Module, Args) -&amp;gt;
    gen_event:add_sup_handler(?EVENT, Module, Args).

send({address, _Host, _Port}=Address, Telex) -&amp;gt;
    gen_server:cast(?SERVER, {telex, Address, Telex}).

% --- gen_server callbacks ---

init([]) -&amp;gt;
    {ok, Socket} = gen_udp:open(?PORT),
    {ok, #state{socket=Socket}}.

handle_call(_Request, _From, State) -&amp;gt;
    {reply, ok, State}.

handle_cast({telex, {address, Host, Port}, Telex}, #state{socket=Socket}=State) -&amp;gt;
    gen_udp:send(Socket, Host, Port, telex:encode(Telex)),
    {noreply, State};
handle_cast(_Msg, State) -&amp;gt;
    {noreply, State}.

handle_info({udp, Socket, Host, Port, Msg}, #state{socket=Socket}=State) -&amp;gt;
    Event = {telex, {address, Host, Port}, telex:decode(Msg)},
    gen_event:notify(?EVENT, Event),
    {noreply, State};
handle_info(_Info, State) -&amp;gt;
    {noreply, State}.

terminate(_Reason, #state{socket=Socket}) -&amp;gt;
    gen_udp:close(Socket),
    ok.

code_change(_OldVsn, State, _Extra) -&amp;gt;
  {ok, State}.

% --- end ---
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;To demonstrate this, let's write the simplest possible event handler:
&lt;/p&gt;
&lt;pre&gt;-module(log).

-export([start/0]).
-export([info/1, warn/1, error/1]).

-behaviour(gen_event).
-export([init/1, handle_event/2, handle_call/2, handle_info/2, terminate/2, code_change/3]).

% --- api ---

start() -&amp;gt;
    switch:add_sup_handler(?MODULE, none).

info(Info) -&amp;gt;
    error_logger:info_report([{pid, self()} | Info]).

warn(Warn) -&amp;gt;
    error_logger:warning_report([{pid, self()} | Warn]).

error(Error) -&amp;gt;
    error_logger:error_report([{pid, self()} | Error]).

% --- gen_event callbacks ---

init(none) -&amp;gt;
    {ok, none}.

handle_event(Event, State) -&amp;gt;
    log:info([Event]),
    {ok, State}.

handle_call(_Request, State) -&amp;gt;
    {ok, ok, State}.

handle_info(_Info, State) -&amp;gt;
    {ok, State}.

terminate(_Reason, _State) -&amp;gt;
    ok.

code_change(_OldVsn, State, _Extra) -&amp;gt;
    {ok, State}.

% --- end ---
&lt;/pre&gt;
&lt;p&gt;Here we have some wrappers around the standard error logger and an event handler which (after masses of gen_event boilerplate) simply logs every event.
&lt;/p&gt;
&lt;p&gt;This is enough functionality now to start talking to a TeleHash node:
&lt;/p&gt;
&lt;pre&gt;1&amp;gt; c(util), c(telex), c(switch), c(log).
{ok,log}
2&amp;gt; switch:start_link().
{ok,&amp;lt;0.55.0&amp;gt;,&amp;lt;0.56.0&amp;gt;}
3&amp;gt; log:start().
ok
4&amp;gt; T = {struct, [{'+end', 'a9993e364706816aba3e25717850c26c9cd0d89d'}]}.
{struct,[{'+end',a9993e364706816aba3e25717850c26c9cd0d89d}]}
5&amp;gt; switch:send({address, &quot;127.0.0.1&quot;, 55555}, T).
ok
6&amp;gt;
=INFO REPORT==== 17-Mar-2011::12:21:13 ===
    pid: &amp;lt;0.55.0&amp;gt;
    {telex,{address,{127,0,0,1},55555},
           {struct,[{&amp;lt;&amp;lt;&quot;_ring&quot;&amp;gt;&amp;gt;,5932},
                    {&amp;lt;&amp;lt;&quot;.see&quot;&amp;gt;&amp;gt;,[]},
                    {&amp;lt;&amp;lt;&quot;_br&quot;&amp;gt;&amp;gt;,51},
                    {&amp;lt;&amp;lt;&quot;_to&quot;&amp;gt;&amp;gt;,&amp;lt;&amp;lt;&quot;127.0.0.1:42424&quot;&amp;gt;&amp;gt;}]}}

&lt;/pre&gt;
&lt;p&gt;Here we ask localhost:55555 for the nearest nodes it knows to the end 'a99...89d'. The reply is contained in the &lt;b&gt;.see&lt;/b&gt; field (which is empty because localhost:55555 hasn't seeded itself yet and so doesn't know any nodes at all).
&lt;/p&gt;
&lt;p&gt;The next post will deal with dialing, at which point we will have a working announcer.
&lt;/p&gt;</description>
	<pubDate>Thu, 17 Mar 2011 12:47:18 +0000</pubDate>
</item>
<item>
	<title>erlang.org RSS Feed: R14B02 released</title>
	<guid>http://www.erlang.org/news/13</guid>
	<link>http://www.erlang.org/news/13</link>
	<description>&lt;p&gt;&lt;p&gt;
	&lt;b&gt;Erlang/OTP R14B02&lt;/b&gt; has been released as planned on March 16:th 2011. It is the second R14 service release.&lt;/p&gt;
&lt;p&gt;
	See the release notes in the &lt;a href=&quot;http://www.erlang.org/download/otp_src_R14B02.readme&quot;&gt;readme file&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;
	Download the new release from the &lt;a href=&quot;http://www.erlang.org/download.html&quot;&gt;download page&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;
	&lt;strong&gt;Highlights:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
	&lt;li&gt;
		The &amp;quot;halfword&amp;quot; emulator is now official. A 64-bit emulator that uses less memory than the full 64-bit emulator.&lt;/li&gt;
	&lt;li&gt;
		EDoc handles Erlang specifications and types.&lt;/li&gt;
	&lt;li&gt;
		All test suites now run with CommonTest&lt;/li&gt;
&lt;/ul&gt;
&lt;/p&gt;</description>
	<pubDate>Thu, 17 Mar 2011 09:47:43 +0000</pubDate>
</item>
<item>
	<title>Scattered Thoughts: transactional mealy machines</title>
	<guid>http://scattered-thoughts.net/one/1300/292121/72985</guid>
	<link>http://scattered-thoughts.net/one/1300/292121/72985</link>
	<description>&lt;p&gt;This is a hugely overdue post about an interesting system I worked on almost a year ago whilst at &lt;a href=&quot;http://smarkets.com&quot;&gt;Smarkets&lt;/a&gt; and never got around to writing about. Unfortunately I don't have the code in front of me but the overall idea is simple enough to explain without examples.
&lt;/p&gt;
&lt;p&gt;Smarkets is a betting exchange (effectively a small stock exchange for buying and selling bets). The exchange system which handles all the money and manages the markets has quite stringent requirements. We want events to be serializable (because ordering is very important in a fast moving market), low latency and ideally distributed across more than one machine. However the exchange also has to handle a large number of bursty updates focused on a small number of records (popular markets, power users). I'm told that the early prototypes using postgres simply couldn't handle the high contention so a move to a more loosely coupled system was necessary.
&lt;/p&gt;
&lt;p&gt;The architecture in place when I arrived at Smarkets was based on &lt;a href=&quot;http://www.cidrdb.org/cidr2007/papers/cidr07p15.pdf&quot;&gt;this paper&lt;/a&gt; which I highly recommend reading. The main idea is that serializability across machines is difficult verging on impossible and that systems which try to paper over this (eg fully ACID distributed transactions) tend to be fragile at scale. The proposed solution is to identify specific sets of actions which must be serializable and handle each set with a single actor on a single machine. These actors then communicate with each other via asynchronous messages. In Smarkets' case the actors are individual markets, users, accounts and orders. These can be modeled nicely as &lt;a href=&quot;http://en.wikipedia.org/wiki/Mealy_machine&quot;&gt;mealy machines&lt;/a&gt; where the output value is a list of messages, hence the title.
&lt;/p&gt;
&lt;p&gt;This idea was very effective but the implementation at Smarkets was some of the scariest code in the repository (thanks mostly to being the oldest code). Each actor was implemented as a single erlang process which archived messages (using couchdb) after reading them. There was a lot of repetitive boilerplate code, it was hard to test (because the actors message each other directly) and worst of all there were ways to lose messages before they were archived (eg process inbox is lost if the process dies, messages between machines can be dropped silently).
&lt;/p&gt;
&lt;p&gt;I wrote a new system to handle the actor implementation whilst keeping the domain-specific logic of each actor mostly unchanged. Each actor is defined by a pair of callback functions (a behaviour, in erlang-speak). The &lt;b&gt;init&lt;/b&gt; function sets the initial state of the actor. The &lt;b&gt;transition&lt;/b&gt; function takes the current state and an incoming message and returns the new state and possibly some outgoing messages. Everything else is handled by a generic module which takes this behaviour and turns it into a running actor. Each actor consists of an inbox, outbox and a current state, all of which are persisted using mnesia. Each actor also has a unique id used for addressing messages. The transition process - pop a message off the inbox queue, run the transition function, store the new state, push outgoing messages to the outbox - is implemented as a single ACID transaction using mnesia. For actors on the same machine messages are moved directly from one actor’s outbox to another's inbox directly using mnesia transactions. For actors on different machines the outbox using erlang messages and sends repeatedly (with exponential backoff) until the receiver confirms receipt. The outbox attaches auto-incrementing message ids to each message which, together with the actor id of the sender, allows the receiver to ignore duplicate messages.
&lt;/p&gt;
&lt;p&gt;In this way the domain-specific logic is separated from message handling and storage. This led to much less repetition and a more maintainable system. It also made it easy to setup tests or replay past events without recreating the whole system. Last, but certainly not least, it can only lose messages if the database or disk fails and even then is easier to restore from backup than the previous system.
&lt;/p&gt;
&lt;p&gt;Note that this explanation is somewhat simplified. I have glossed over some fiddly implementation details like error handling (if an actor fails to handle a message the sender needs to be notified in many cases) and also left out extra features like subscribing to state changes (eg notify me when this order is filled). There is also a knack to designing actors which must cooperate without &lt;a href=&quot;http://en.wikipedia.org/wiki/Common_knowledge_(logic&quot;&gt;common knowledge&lt;/a&gt;). Hopefully the &lt;a href=&quot;https://smarkets.com/about/contact/&quot;&gt;Smarkets team&lt;/a&gt; will find some time to open-source the actual code.
&lt;/p&gt;</description>
	<pubDate>Wed, 16 Mar 2011 16:15:21 +0000</pubDate>
</item>
<item>
	<title>Erlang Inside: Interview with Tino Breddin, Embedded Erlang developer, on the social science of tech communities.</title>
	<guid>http://erlanginside.com/?p=261</guid>
	<link>http://erlanginside.com/interview-with-tino-breddin-ruby-vs-python-vs-erlang-communities-261</link>
	<description>Why are communities such as Erlang&amp;#8217;s and Ruby&amp;#8217;s so different? What makes them approchable or not? Is America the problem? Should we forcibly relocate all Ruby developers to Sweden? Tino talks about that question and others in this fascinating interview. Follow Tino on twitter at @tolbrino and follow me at @chaddepue. Get an alert when [...]</description>
	<pubDate>Wed, 16 Mar 2011 14:18:17 +0000</pubDate>
</item>
<item>
	<title>Programming in the 21st Century: Don't Distract New Programmers with OOP</title>
	<guid>http://prog21.dadgum.com/93.html</guid>
	<link>http://prog21.dadgum.com/93.html</link>
	<description>When I get asked &quot;What's a good first programming language to teach my [son / daughter / other-person-with-no-programming-experience]?&quot; my answer has been the same for the last 5+ years: Python.
&lt;br /&gt;&lt;br /&gt;That may be unexpected, coming from someone who often talks about non-mainstream languages, but I stand by it.
&lt;br /&gt;&lt;br /&gt;Python is good for a wide range of simple and interesting problems that would be too much effort in C. (Seriously, a basic &lt;a href=&quot;http://prog21.dadgum.com/29.html&quot;&gt;spellchecker&lt;/a&gt; can be implemented in a few lines of Python.) There are surprisingly few sticking points where the solution is easy to see, but there's a tricky mismatch between it and the core language features. Erlang has a couple of biggies. Try implementing any algorithm that's most naturally phrased in terms of in-place array updates, for example. In Python the sailing tends to be smooth. Arrays and dictionaries and sets cover a lot of ground.
&lt;br /&gt;&lt;br /&gt;There's one caveat to using Python as an introductory programming language: avoid the object-oriented features. You can't dodge them completely, as fundamental data types have useful methods associated with them, and that's okay. Just make use of what's already provided and resist talking about how to create classes, and especially avoid talking about any notions of object-oriented design where every little bit of data has to be wrapped up in a class.
&lt;br /&gt;&lt;br /&gt;The shift from procedural to OO brings with it a shift from thinking about problems and solutions to thinking about  &lt;i&gt;architecture&lt;/i&gt;. That's easy to see just by comparing a procedural Python program with an object-oriented one. The latter is almost always longer, full of extra interface and indentation and annotations. The temptation is to start moving trivial bits of code into classes and adding all these little methods and anticipating methods that aren't needed yet but might be someday.
&lt;br /&gt;&lt;br /&gt;When you're trying to help someone learn how to go from a problem statement to working code, the last thing you want is to get them sidetracked by faux-engineering busywork. Some people are going to run with those scraps of OO knowledge and build crazy class hierarchies and end up not as focused on on what they should be learning. Other people are going to lose interest because there's a layer of extra nonsense that makes programming even more cumbersome.
&lt;br /&gt;&lt;br /&gt;At some point, yes, you'll need to discuss how to create objects in Python, but resist for as long as you can.
&lt;br /&gt;&lt;br /&gt;(If you liked this, you might like &lt;a href=&quot;http://prog21.dadgum.com/53.html&quot;&gt;How I Learned to Stop Worrying and Love Erlang's Process Dictionary&lt;/a&gt;.)</description>
	<pubDate>Wed, 16 Mar 2011 06:00:00 +0000</pubDate>
</item>
<item>
	<title>Erlang Factory News: Only 5 Places left at the Erlang Factory SF Bay Area!</title>
	<guid>http://erlang-factory.com/news/rss/e11067ef6a7ce4536d1da29f9dfb8544</guid>
	<link></link>
	<description>&lt;p&gt;We only have 5 places left at the Erlang Factory SF Bay Area so if you haven't booked yet, don't miss out and &lt;a href=&quot;https://www.erlang-factory.com/conference/SFBay2011/register&quot;&gt;book now!&lt;/a&gt; The OTP Express course has now sold out but there are a few places left on the other courses at the &lt;a href=&quot;http://www.erlang-factory.com/conference/SFBay2011/university&quot;&gt;University&lt;/a&gt;.&lt;/p&gt;</description>
	<pubDate>Mon, 14 Mar 2011 16:01:17 +0000</pubDate>
</item>
<item>
	<title>Process-one Blogs: Sea Beyond 2011 Talk 4: Eric Cestari on XMPP over Websocket and GitLive web push</title>
	<guid>http://www.process-one.net/en/blogs/article/sea_beyond_2011_talk_4_eric_cestari_on_xmpp_over_websocket_and_gitlive_web_/</guid>
	<link>http://www.process-one.net/en/blogs/article/sea_beyond_2011_talk_4_eric_cestari_on_xmpp_over_websocket_and_gitlive_web_/</link>
	<description>&lt;p&gt;Eric Cestari, From ProcessOne, explains his work on Websocket integration in ejabberd and introduce &lt;a href=&quot;http://gitlive.com&quot;&gt;GitLive&lt;/a&gt; as a demonstration of our web push platform.&lt;/p&gt; &lt;p&gt;This is an interesting view of the present and future of XMPP in the browser. The talk also shows that you can already today build web push service that already takes advantage of websockets for web platforms that supports it.&lt;/p&gt;
&lt;p&gt;All details are in Eric Cestari's talk at Sea Beyond:&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;You can see the slides here:&lt;/p&gt;
&lt;p&gt;







&lt;/p&gt;
&lt;p&gt;Do not miss the &lt;a href=&quot;http://www.process-one.net/en/blogs/article/sea_beyond_2011_video_summary/&quot;&gt;event summary and the other videos from Sea Beyond event&lt;/a&gt;.&lt;/p&gt;</description>
	<pubDate>Mon, 14 Mar 2011 11:19:06 +0000</pubDate>
</item>
<item>
	<title>Programming in the 21st Century: Exploring Audio Files with Erlang</title>
	<guid>http://prog21.dadgum.com/92.html</guid>
	<link>http://prog21.dadgum.com/92.html</link>
	<description>It takes surprisingly little Erlang code to dig into the contents of an uncompressed audio file. And it turns out that three of the most common uncompressed audio file formats--WAV, AIFF, and Apple's CAF--all follow the same general structure. Once you understand the basics of one, it's easy to deal with the others. AIFF is the trickiest of the three, so that's the one I'll use as an example.
&lt;br /&gt;&lt;br /&gt;First, load the entire file into a binary:
&lt;pre&gt;load(Filename) -&amp;gt;
   {ok, B} = file:read_file(Filename),
   B.
&lt;/pre&gt;There's a small header: four characters spelling out &quot;FORM&quot;, a length which doesn't matter, then four more characters spelling out &quot;AIFF&quot;. The interesting part is the rest of the file, so let's just validate the header and put the rest of the file into a binary called B:
&lt;pre&gt;&amp;lt;&amp;lt;&quot;FORM&quot;, _:32, &quot;AIFF&quot;, B/binary&amp;gt;&amp;gt; = load(Filename).
&lt;/pre&gt;The &quot;rest of file&quot; binary is broken into chunks that follow a simple format: a four character chunk name, the length of the data in the chunk (which doesn't include the header), and then the data itself. Here's a little function that breaks a binary into a list of &lt;tt&gt;{Chunk_Name, Contents}&lt;/tt&gt; pairs:
&lt;pre&gt;chunkify(Binary) -&amp;gt; chunkify(Binary, []).
chunkify(&amp;lt;&amp;lt;N1,N2,N3,N4, Len:32,
   Data:Len/binary, Rest/binary&amp;gt;&amp;gt;, Chunks) -&amp;gt;
   Name = list_to_atom([N1,N2,N3,N4]),
   chunkify(adjust(Len, Rest), [{Name, Data}|Chunks]);
chunkify(&amp;lt;&amp;lt;&amp;gt;&amp;gt;, Chunks) -&amp;gt;
   Chunks.
&lt;/pre&gt;Ignore the &lt;tt&gt;adjust&lt;/tt&gt; function for now; I'll get back to that. 
&lt;br /&gt;&lt;br /&gt;Given the results of &lt;tt&gt;chunkify&lt;/tt&gt;, it's easy to find a specific chunk using &lt;tt&gt;lists:keyfind/3&lt;/tt&gt;. Really, though, other than to test the chunkification code, there's rarely a reason to iterate through all the chunks in a file. It's nicer to return a function that makes lookups easy. Replace the last line of &lt;tt&gt;chunkify&lt;/tt&gt; with this:
&lt;pre&gt;fun(Name) -&amp;gt; element(2, lists:keyfind(Name, 1, Chunks)) end.
&lt;/pre&gt;The key info about sample rates and number of channels and all that is in a chunk called &lt;tt&gt;COMM&lt;/tt&gt; and now we've got an easy way to get at and decode that chunk:
&lt;pre&gt;Chunks = chunkify(B).
&amp;lt;&amp;lt;Channels:16, Frames:32, Sample_Size:16, Rate:10/binary&amp;gt;&amp;gt; =
   Chunks('COMM').
&lt;/pre&gt;The sound samples themselves are in a chunk called &lt;tt&gt;SSND&lt;/tt&gt;. The first eight bytes of that chunk don't matter, so to decode that chunk it's just:
&lt;pre&gt;&amp;lt;&amp;lt;_:8/binary, Samples/binary&amp;gt;&amp;gt; = Chunks('SSND').
&lt;/pre&gt;Okay, now the few weird bits of the AIFF format. First, if the size of a chunk is odd, then there's one extra pad byte following it. That's what the &lt;tt&gt;adjust&lt;/tt&gt; function is for. It checks if a pad byte exists and removes it before decoding the rest of the binary. The second quirk is that the sample rate is encoded as a ten-byte extended floating point value, and most languages don't have support for them--including Erlang. There's an algorithm in the AIFF spec for encoding and decoding extended floats, and I translated it into Erlang.
&lt;br /&gt;&lt;br /&gt;Here's the complete code for the AIFF decoder:
&lt;pre&gt;load_aiff(Filename) -&amp;gt;
   &amp;lt;&amp;lt;&quot;FORM&quot;, _:32, &quot;AIFF&quot;, B/binary&amp;gt;&amp;gt; = load(Filename),
   Chunks = chunkify(B),
   &amp;lt;&amp;lt;Channels:16, Frames:32, Sample_Size:16, Rate:10/binary&amp;gt;&amp;gt; =
      Chunks('COMM'),
   &amp;lt;&amp;lt;_:8/binary, Samples/binary&amp;gt;&amp;gt; = Chunks('SSND'),
   {Channels, Frames, Sample_Size, extended_to_int(Rate), Samples}.

chunkify(Binary) -&amp;gt; chunkify(Binary, []).
chunkify(&amp;lt;&amp;lt;N1,N2,N3,N4, Length:32,
   Data:Length/binary, Rest/binary&amp;gt;&amp;gt;, Chunks) -&amp;gt;
   Name = list_to_atom([N1,N2,N3,N4]),
   chunkify(adjust(Length, Rest), [{Name, Data}|Chunks]);
chunkify(&amp;lt;&amp;lt;&amp;gt;&amp;gt;, Chunks) -&amp;gt;
   fun(Name) -&amp;gt; element(2, lists:keyfind(Name, 1, Chunks)) end.

adjust(Length, B) -&amp;gt;
   case Length band 1 of
      1 -&amp;gt; &amp;lt;&amp;lt;_:8, Rest/binary&amp;gt;&amp;gt; = B, Rest;
      _ -&amp;gt; B
   end.

extended_to_int(&amp;lt;&amp;lt;_, Exp, Mantissa:32, _:4/binary&amp;gt;&amp;gt;) -&amp;gt;
   extended_to_int(30 - Exp, Mantissa, 0).
extended_to_int(0, Mantissa, Last) -&amp;gt;
   Mantissa + (Last band 1);
extended_to_int(Exp, Mantissa, _Last) -&amp;gt;
   extended_to_int(Exp - 1, Mantissa bsr 1, Mantissa).

load(Filename) -&amp;gt;
   {ok, B} = file:read_file(Filename),
   B.
&lt;/pre&gt;WAV and CAF both follow the same general structure of a header followed by chunks. WAV uses little-endian values, while the other two are big-endian. CAF doesn't have chunk alignment requirements, so that removes the need for &lt;tt&gt;adjust&lt;/tt&gt;. And fortunately it's only AIFF that requires that ugly conversion from extended floating point in order to get the sample rate.</description>
	<pubDate>Sat, 12 Mar 2011 06:00:00 +0000</pubDate>
</item>
<item>
	<title>Process-one Blogs: Sea Beyond 2011 Talk 3: David Banes on Cleartext</title>
	<guid>http://www.process-one.net/en/blogs/article/sea_beyond_2011_talk_3_david_banes_on_cleartext/</guid>
	<link>http://www.process-one.net/en/blogs/article/sea_beyond_2011_talk_3_david_banes_on_cleartext/</link>
	<description>&lt;p&gt;David Banes introduces &lt;a href=&quot;http://www.cleartextsystems.com/&quot;&gt;Cleartext&lt;/a&gt; new opensource product: an Enterprise Messaging 2.0, based on ejabberd for the server part and developed in Adobe Air for the client part.&lt;/p&gt; &lt;p&gt;The platform features of course EIM, Group Chat &amp;amp; Microblogging, has security and compliance designed in, and notably has Integrated Content Filtering, Malware Protection, URL &amp;amp; Image Filtering and Archiving with eDiscovery.&lt;/p&gt;
&lt;p&gt;Enjoy David Bane's presentation:&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;You can see the slides here:&lt;/p&gt;
&lt;p&gt;







/p&amp;gt;&lt;/p&gt;
&lt;p&gt;Do not miss the &lt;a href=&quot;http://www.process-one.net/en/blogs/article/sea_beyond_2011_video_summary/&quot;&gt;event summary and the other videos from Sea Beyond event&lt;/a&gt;.&lt;/p&gt;</description>
	<pubDate>Thu, 10 Mar 2011 13:48:46 +0000</pubDate>
</item>
<item>
	<title>Programming in the 21st Century: Accidental Innovation, Part 3</title>
	<guid>http://prog21.dadgum.com/91.html</guid>
	<link>http://prog21.dadgum.com/91.html</link>
	<description>I didn't write the &lt;a href=&quot;http://prog21.dadgum.com/89.html&quot;&gt;previous&lt;/a&gt; &lt;a href=&quot;http://prog21.dadgum.com/90.html&quot;&gt;two&lt;/a&gt; installments so I could build up my ego. I wanted to give concrete examples of innovation and the circumstances surrounding it, to show that it's not magic or glamorous, to show that innovation is more than sitting down and saying &quot;Okay, time to innovate!&quot;
&lt;br /&gt;&lt;br /&gt;It's curious how often the &quot;innovative&quot; stamp is applied to things that don't fit any kind of reasonable definition of the word. How many times have you seen text like this on random company X's &quot;About&quot; page:
&lt;blockquote&gt;We develop innovative solutions which enable enterprise-class cloud computing infrastructure that...something something synergy...something something &quot;outside the box.&quot;
&lt;/blockquote&gt;I've seen that enough that I've formulated a simple rule: If you have to say that you're innovating, then you're not. Or in a less snarky way: Innovation in itself is an empty goal, so if you're using it in the mission statement for the work you're doing, then odds are the rest of the mission statement is equally vacant.
&lt;br /&gt;&lt;br /&gt;Really, the only way to innovate is to do so accidentally.
&lt;br /&gt;&lt;br /&gt;In both the examples I gave, I wasn't thinking about how to do things differently. I was thinking about how to solve a problem and only that problem. The results ended up being interesting because I didn't spend all my time fixated on what other people had done, to the point where that's all I could see. If I started designing a puzzle game in 2011, I'd know all about Tetris and all the knock-offs of Tetris and all the incremental changes and improvements that stemmed from Tetris. It would be difficult work within the restrictions of the label &quot;puzzle game&quot; and come up with something that transcends the boundaries of those restrictions.
&lt;br /&gt;&lt;br /&gt;Suppose it's the late 1990s, and your goal is to design a next generation graphical interface for desktop PCs--something better than Windows. Already you're sunk, because you're looking at Windows, you're thinking about Windows, and all of your decisions will be colored by a long exposure to Windows-like interfaces. There are icons, a desktop, resizable windows, some kind of task bar, etc. What you end up with will almost certainly not be completely identical to Windows, but it won't be innovative.
&lt;br /&gt;&lt;br /&gt;Now there are some interesting problems behind that vague goal of building a next generation GUI. The core question is how to let the user run multiple applications at the same time and switch between them. And that question has some interesting and wide-ranging answers. You can see the results of some of those lines of thinking in current systems, such as doing away with the &quot;app in a movable window&quot; idea and having each application take over the entire screen. Then the question becomes a different one: How to switch between multiple full-screen apps? This is all very different than starting with the desktop metaphor and trying to morph it into something innovative.
&lt;br /&gt;&lt;br /&gt;(If you liked this, you might like &lt;a href=&quot;http://prog21.dadgum.com/69.html&quot;&gt;How to Think Like a Pioneer&lt;/a&gt;.)</description>
	<pubDate>Wed, 09 Mar 2011 06:00:00 +0000</pubDate>
</item>
<item>
	<title>Process-one Blogs: Sea Beyond 2011 Talk 2: Christophe Romain, Karim Gemayel on Pubsub and distributed social networks</title>
	<guid>http://www.process-one.net/en/blogs/article/sea_beyond_2011_talk_2_christophe_romain_karim_gemayel_on_pubsub_and_distri/</guid>
	<link>http://www.process-one.net/en/blogs/article/sea_beyond_2011_talk_2_christophe_romain_karim_gemayel_on_pubsub_and_distri/</link>
	<description>&lt;p&gt;Christophe Romain and Karim Gemayel presents XMPP pubsub in the context of distributed social networking.&lt;/p&gt; &lt;p&gt;Christophe Romain and Karim Gemayel have been working on XMPP pubsub for several years now. They have build ejabberd implementation as well as custom improvements in various customers context, for features and scalability.&lt;/p&gt;
&lt;p&gt;In this presentation, they explains XMPP pubsub and how it can be used to build distributed social networks.&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;Do not miss the &lt;a href=&quot;http://www.process-one.net/en/blogs/article/sea_beyond_2011_video_summary/&quot;&gt;event summary and the other videos from Sea Beyond event&lt;/a&gt;.&lt;/p&gt;</description>
	<pubDate>Mon, 07 Mar 2011 14:24:55 +0000</pubDate>
</item>
<item>
	<title>Erlang, Testing and TDD: gianfrancoalongi</title>
	<guid>http://erlcode.wordpress.com/?p=685</guid>
	<link>http://erlcode.wordpress.com/2011/03/05/erlang-tdd-hand-on-project-workernet-part-8/</link>
	<description>&lt;p&gt;Last story to play  on the workernet job layer,&lt;/p&gt;
&lt;p&gt;“&lt;em&gt;I want to be able to delete a job once it’s done, through any node&lt;/em&gt;.”&lt;/p&gt;
&lt;p&gt;To design this &amp;#8211; the test is written first (as usual)&lt;/p&gt;
&lt;p&gt;&lt;!--- Snippet --&gt;&lt;/p&gt;
&lt;pre&gt;&lt;span&gt;done_job_deleted&lt;/span&gt;() -&amp;gt;
    &lt;span&gt;wn_resource_layer&lt;/span&gt;:&lt;span&gt;register&lt;/span&gt;(#&lt;span&gt;wn_resource&lt;/span&gt;{&lt;span&gt;name&lt;/span&gt; = &lt;span&gt;&quot;Laptop&quot;&lt;/span&gt;,
                                     &lt;span&gt;type&lt;/span&gt; = [{&lt;span&gt;perl&lt;/span&gt;,1}],
                                     &lt;span&gt;resides&lt;/span&gt; = &lt;span&gt;node&lt;/span&gt;()
                                    }),
    &lt;span&gt;Path&lt;/span&gt; = &lt;span&gt;create_file_at&lt;/span&gt;(?&lt;span&gt;NODE_ROOT&lt;/span&gt;),
    &lt;span&gt;File1&lt;/span&gt; = #&lt;span&gt;wn_file&lt;/span&gt;{&lt;span&gt;id&lt;/span&gt; = &lt;span&gt;&quot;File1&quot;&lt;/span&gt;,&lt;span&gt;file&lt;/span&gt; = &lt;span&gt;Path&lt;/span&gt;,&lt;span&gt;resides&lt;/span&gt; = &lt;span&gt;node&lt;/span&gt;()},
    &lt;span&gt;Job1&lt;/span&gt; = #&lt;span&gt;wn_job&lt;/span&gt;{&lt;span&gt;id&lt;/span&gt; = &lt;span&gt;&quot;JobId&quot;&lt;/span&gt;,
                   &lt;span&gt;files&lt;/span&gt; = [&lt;span&gt;File1&lt;/span&gt;],
                   &lt;span&gt;resources&lt;/span&gt; = [&lt;span&gt;perl&lt;/span&gt;],
                   &lt;span&gt;commands&lt;/span&gt; = [&lt;span&gt;&quot;perl -e 'print(\&quot;HelloWorld\n\&quot;)'&quot;&lt;/span&gt;],
                   &lt;span&gt;timeout&lt;/span&gt; = 1000
                  },
    &lt;span&gt;ok&lt;/span&gt; = &lt;span&gt;wn_job_layer&lt;/span&gt;:&lt;span&gt;register&lt;/span&gt;(&lt;span&gt;Job1&lt;/span&gt;),
    &lt;span&gt;ok&lt;/span&gt; = &lt;span&gt;wn_job_layer&lt;/span&gt;:&lt;span&gt;stream&lt;/span&gt;(&lt;span&gt;user&lt;/span&gt;,&lt;span&gt;&quot;JobId&quot;&lt;/span&gt;),
    &lt;span&gt;timer&lt;/span&gt;:&lt;span&gt;sleep&lt;/span&gt;(500),
    &lt;span&gt;ok&lt;/span&gt; = &lt;span&gt;wn_job_layer&lt;/span&gt;:&lt;span&gt;delete&lt;/span&gt;(&lt;span&gt;&quot;JobId&quot;&lt;/span&gt;),
    ?&lt;span&gt;assertMatch&lt;/span&gt;([#&lt;span&gt;wn_file&lt;/span&gt;{&lt;span&gt;id&lt;/span&gt;=&lt;span&gt;&quot;File1&quot;&lt;/span&gt;}],&lt;span&gt;wn_file_layer&lt;/span&gt;:&lt;span&gt;list_files&lt;/span&gt;()),
    ?&lt;span&gt;assertEqual&lt;/span&gt;([],&lt;span&gt;wn_job_layer&lt;/span&gt;:&lt;span&gt;list_all_jobs&lt;/span&gt;()),
    &lt;span&gt;ok&lt;/span&gt;.&lt;/pre&gt;
&lt;p&gt;&lt;!-- End Of Snippet --&gt;&lt;/p&gt;
&lt;p&gt;Es usual, this will not even compile first as stuff is missing and dialyzer should get a headache about it. Now, everything is there except the &lt;em&gt;wn_job_layer:delete/1&lt;/em&gt; function.&lt;/p&gt;
&lt;p&gt;Therefore, the first implementation goes into &lt;em&gt;wn_job_layer.erl&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;!--- Snippet --&gt;&lt;/p&gt;
&lt;pre&gt;&lt;span&gt;-spec&lt;/span&gt;(&lt;span&gt;delete&lt;/span&gt;(&lt;span&gt;string&lt;/span&gt;()) -&amp;gt; &lt;span&gt;ok&lt;/span&gt; | {&lt;span&gt;error&lt;/span&gt;,&lt;span&gt;term&lt;/span&gt;()}).
&lt;span&gt;delete&lt;/span&gt;(&lt;span&gt;Id&lt;/span&gt;) -&amp;gt;
    &lt;span&gt;gen_server&lt;/span&gt;:&lt;span&gt;call&lt;/span&gt;(?&lt;span&gt;MODULE&lt;/span&gt;,{&lt;span&gt;delete&lt;/span&gt;,&lt;span&gt;Id&lt;/span&gt;}).&lt;/pre&gt;
&lt;p&gt;8!--- End Of Snippet --&gt;&lt;/p&gt;
&lt;p&gt;next the internal handle_call/3 clause for handling the delete request&lt;/p&gt;
&lt;p&gt;&lt;!--- Snippet --&gt;&lt;/p&gt;
&lt;pre&gt;&lt;span&gt;handle_call&lt;/span&gt;({&lt;span&gt;delete&lt;/span&gt;,&lt;span&gt;Id&lt;/span&gt;},&lt;span&gt;_&lt;/span&gt;,&lt;span&gt;State&lt;/span&gt;) -&amp;gt;
    &lt;span&gt;Result&lt;/span&gt; = &lt;span&gt;try_delete&lt;/span&gt;(&lt;span&gt;Id&lt;/span&gt;,&lt;span&gt;State&lt;/span&gt;),
    {&lt;span&gt;reply&lt;/span&gt;,&lt;span&gt;Result&lt;/span&gt;,&lt;span&gt;State&lt;/span&gt;};&lt;/pre&gt;
&lt;p&gt;&lt;!--- End Of Snippet --&gt;&lt;/p&gt;
&lt;p&gt;this raises the need to implement the internal try_delete/2 function&lt;/p&gt;
&lt;p&gt;&lt;!--- Snippet --&gt;&lt;/p&gt;
&lt;pre&gt;&lt;span&gt;try_delete&lt;/span&gt;(&lt;span&gt;Id&lt;/span&gt;,&lt;span&gt;State&lt;/span&gt;) -&amp;gt;
    &lt;span&gt;case&lt;/span&gt; &lt;span&gt;ets&lt;/span&gt;:&lt;span&gt;lookup&lt;/span&gt;(&lt;span&gt;State&lt;/span&gt;#&lt;span&gt;state&lt;/span&gt;.&lt;span&gt;jobs&lt;/span&gt;,&lt;span&gt;Id&lt;/span&gt;) &lt;span&gt;of&lt;/span&gt;
        [{&lt;span&gt;Id&lt;/span&gt;,&lt;span&gt;JobKeeperPid&lt;/span&gt;,&lt;span&gt;_&lt;/span&gt;}] -&amp;gt;
            &lt;span&gt;case&lt;/span&gt; &lt;span&gt;wn_job_keeper&lt;/span&gt;:&lt;span&gt;get_stored_result&lt;/span&gt;(&lt;span&gt;JobKeeperPid&lt;/span&gt;) &lt;span&gt;of&lt;/span&gt;
                {&lt;span&gt;ok&lt;/span&gt;,&lt;span&gt;Result&lt;/span&gt;} -&amp;gt;
                    &lt;span&gt;ok&lt;/span&gt; = &lt;span&gt;wn_job_keeper&lt;/span&gt;:&lt;span&gt;delete&lt;/span&gt;(&lt;span&gt;JobKeeperPid&lt;/span&gt;),
                    &lt;span&gt;ets&lt;/span&gt;:&lt;span&gt;delete&lt;/span&gt;(&lt;span&gt;State&lt;/span&gt;#&lt;span&gt;state&lt;/span&gt;.&lt;span&gt;jobs&lt;/span&gt;,&lt;span&gt;Id&lt;/span&gt;),
                    &lt;span&gt;wn_file_layer&lt;/span&gt;:&lt;span&gt;delete_file&lt;/span&gt;(&lt;span&gt;node&lt;/span&gt;(),&lt;span&gt;Result&lt;/span&gt;);
                &lt;span&gt;X&lt;/span&gt; -&amp;gt; &lt;span&gt;X&lt;/span&gt;
            &lt;span&gt;end&lt;/span&gt;;
        [] -&amp;gt;
            {&lt;span&gt;error&lt;/span&gt;,&lt;span&gt;no_such_job&lt;/span&gt;}
    &lt;span&gt;end&lt;/span&gt;.&lt;/pre&gt;
&lt;p&gt;&lt;!--- End Of Snippet --&gt;&lt;/p&gt;
&lt;p&gt;That was all the code needed inside &lt;em&gt; wn_job_layer.erl &lt;/em&gt;.  However, the last internal function brought up the need for a new function; &lt;em&gt;wn_job_keeper:delete/1&lt;/em&gt; so the function is to be implemented next!&lt;/p&gt;
&lt;p&gt;In &lt;em&gt; wn_job_keeper.erl&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;!--- Snippet --&gt;&lt;/p&gt;
&lt;pre&gt;&lt;span&gt;-spec&lt;/span&gt;(&lt;span&gt;delete&lt;/span&gt;(&lt;span&gt;pid&lt;/span&gt;()) -&amp;gt; &lt;span&gt;ok&lt;/span&gt; | {&lt;span&gt;error&lt;/span&gt;,&lt;span&gt;term&lt;/span&gt;()}).
&lt;span&gt;delete&lt;/span&gt;(&lt;span&gt;Pid&lt;/span&gt;) -&amp;gt;
    &lt;span&gt;gen_fsm&lt;/span&gt;:&lt;span&gt;sync_send_all_state_event&lt;/span&gt;(&lt;span&gt;Pid&lt;/span&gt;,&lt;span&gt;delete&lt;/span&gt;).&lt;/pre&gt;
&lt;p&gt;&lt;!--- End Of Snippet --&gt;&lt;/p&gt;
&lt;p&gt;&lt;!--- Snippet --&gt;&lt;/p&gt;
&lt;pre&gt;&lt;span&gt;try_delete&lt;/span&gt;(&lt;span&gt;Id&lt;/span&gt;,&lt;span&gt;State&lt;/span&gt;) -&amp;gt;
    &lt;span&gt;case&lt;/span&gt; &lt;span&gt;ets&lt;/span&gt;:&lt;span&gt;lookup&lt;/span&gt;(&lt;span&gt;State&lt;/span&gt;#&lt;span&gt;state&lt;/span&gt;.&lt;span&gt;jobs&lt;/span&gt;,&lt;span&gt;Id&lt;/span&gt;) &lt;span&gt;of&lt;/span&gt;
        [{&lt;span&gt;Id&lt;/span&gt;,&lt;span&gt;JobKeeperPid&lt;/span&gt;,&lt;span&gt;_&lt;/span&gt;}] -&amp;gt;
            &lt;span&gt;case&lt;/span&gt; &lt;span&gt;wn_job_keeper&lt;/span&gt;:&lt;span&gt;get_stored_result&lt;/span&gt;(&lt;span&gt;JobKeeperPid&lt;/span&gt;) &lt;span&gt;of&lt;/span&gt;
                {&lt;span&gt;ok&lt;/span&gt;,&lt;span&gt;Result&lt;/span&gt;} -&amp;gt;
                    &lt;span&gt;ok&lt;/span&gt; = &lt;span&gt;wn_job_keeper&lt;/span&gt;:&lt;span&gt;delete&lt;/span&gt;(&lt;span&gt;JobKeeperPid&lt;/span&gt;),
                    &lt;span&gt;ets&lt;/span&gt;:&lt;span&gt;delete&lt;/span&gt;(&lt;span&gt;State&lt;/span&gt;#&lt;span&gt;state&lt;/span&gt;.&lt;span&gt;jobs&lt;/span&gt;,&lt;span&gt;Id&lt;/span&gt;),
                    &lt;span&gt;wn_file_layer&lt;/span&gt;:&lt;span&gt;delete_file&lt;/span&gt;(&lt;span&gt;node&lt;/span&gt;(),&lt;span&gt;Result&lt;/span&gt;);
                &lt;span&gt;X&lt;/span&gt; -&amp;gt; &lt;span&gt;X&lt;/span&gt;
            &lt;span&gt;end&lt;/span&gt;;
        [] -&amp;gt;
            {&lt;span&gt;error&lt;/span&gt;,&lt;span&gt;no_such_job&lt;/span&gt;}
    &lt;span&gt;end&lt;/span&gt;.&lt;/pre&gt;
&lt;p&gt;&lt;!--- End Of Snippet --&gt;&lt;/p&gt;
&lt;p&gt;With a handle_call clause inside it for the delete request&lt;/p&gt;
&lt;p&gt;&lt;!--- Snippet --&gt;&lt;/p&gt;
&lt;pre&gt;&lt;span&gt;handle_sync_event&lt;/span&gt;(&lt;span&gt;delete&lt;/span&gt;,&lt;span&gt;_&lt;/span&gt;,&lt;span&gt;done&lt;/span&gt;,&lt;span&gt;State&lt;/span&gt;) -&amp;gt;
    {&lt;span&gt;stop&lt;/span&gt;,&lt;span&gt;normal&lt;/span&gt;,&lt;span&gt;ok&lt;/span&gt;,&lt;span&gt;State&lt;/span&gt;};
&lt;span&gt;handle_sync_event&lt;/span&gt;(&lt;span&gt;delete&lt;/span&gt;,&lt;span&gt;_&lt;/span&gt;,&lt;span&gt;X&lt;/span&gt;,&lt;span&gt;State&lt;/span&gt;) -&amp;gt;
    {&lt;span&gt;reply&lt;/span&gt;,{&lt;span&gt;error&lt;/span&gt;,&lt;span&gt;not_done&lt;/span&gt;},&lt;span&gt;X&lt;/span&gt;,&lt;span&gt;State&lt;/span&gt;};&lt;/pre&gt;
&lt;p&gt;&lt;!--- End Of Snippet --&gt;&lt;/p&gt;
&lt;p&gt;That would be all! Since so much had already been written in prior tests, this one turned out to be quite a breeze. Of course this passes both compilation, dialyzation and testing. The judge &amp;#8216;make full&amp;#8217; turns it&amp;#8217;s mighty eye on this&lt;/p&gt;
&lt;p&gt;&lt;!--- Snippet --&gt;&lt;/p&gt;
&lt;pre&gt;&lt;span&gt;zen&lt;/span&gt;:&lt;span&gt;worker_net&lt;/span&gt;-0.1 &lt;span&gt;zenon&lt;/span&gt;&lt;span&gt;$ &lt;/span&gt;&lt;span&gt;make&lt;/span&gt; &lt;span&gt;full&lt;/span&gt;
&lt;span&gt;erlc&lt;/span&gt; -&lt;span&gt;pa&lt;/span&gt; . -&lt;span&gt;o&lt;/span&gt; &lt;span&gt;ebin&lt;/span&gt;/  &lt;span&gt;src&lt;/span&gt;/*.&lt;span&gt;erl&lt;/span&gt; &lt;span&gt;test&lt;/span&gt;/*.&lt;span&gt;erl&lt;/span&gt;
&lt;span&gt;erl&lt;/span&gt; -&lt;span&gt;pa&lt;/span&gt; &lt;span&gt;ebin&lt;/span&gt;/ -&lt;span&gt;eval&lt;/span&gt; 'eunit:&lt;span&gt;test&lt;/span&gt;(&lt;span&gt;wn_resource_layer&lt;/span&gt;,[&lt;span&gt;verbose&lt;/span&gt;]), &lt;span&gt;init&lt;/span&gt;:&lt;span&gt;stop&lt;/span&gt;().'
&lt;span&gt;Erlang&lt;/span&gt; &lt;span&gt;R14B&lt;/span&gt; (&lt;span&gt;erts&lt;/span&gt;-5.8.1) [&lt;span&gt;source&lt;/span&gt;] [&lt;span&gt;smp&lt;/span&gt;:4:4] [&lt;span&gt;rq&lt;/span&gt;:4] [&lt;span&gt;async&lt;/span&gt;-&lt;span&gt;threads&lt;/span&gt;:0] [&lt;span&gt;hipe&lt;/span&gt;] [&lt;span&gt;kernel&lt;/span&gt;-&lt;span&gt;poll&lt;/span&gt;:&lt;span&gt;false&lt;/span&gt;]

&lt;span&gt;Eshell&lt;/span&gt; &lt;span&gt;V5&lt;/span&gt;.8.1  (&lt;span&gt;abort&lt;/span&gt; &lt;span&gt;with&lt;/span&gt; ^&lt;span&gt;G&lt;/span&gt;)
1&amp;gt; ======================== &lt;span&gt;EUnit&lt;/span&gt; ========================
&lt;span&gt;module&lt;/span&gt; &lt;span&gt;'wn_resource_layer'&lt;/span&gt;
  &lt;span&gt;module&lt;/span&gt; &lt;span&gt;'wn_resource_layer_tests'&lt;/span&gt;
    &lt;span&gt;wn_resource_layer_tests&lt;/span&gt;: &lt;span&gt;local_resource_test_&lt;/span&gt; (&lt;span&gt;Can&lt;/span&gt; &lt;span&gt;register&lt;/span&gt; &lt;span&gt;resources&lt;/span&gt; &lt;span&gt;locally&lt;/span&gt;)...[0.001 &lt;span&gt;s&lt;/span&gt;] &lt;span&gt;ok&lt;/span&gt;
    &lt;span&gt;wn_resource_layer_tests&lt;/span&gt;: &lt;span&gt;register_distributed&lt;/span&gt; (&lt;span&gt;Can&lt;/span&gt; &lt;span&gt;Register&lt;/span&gt; &lt;span&gt;Distributed&lt;/span&gt;)...[0.006 &lt;span&gt;s&lt;/span&gt;] &lt;span&gt;ok&lt;/span&gt;
    &lt;span&gt;wn_resource_layer_tests&lt;/span&gt;: &lt;span&gt;register_restart_register&lt;/span&gt; (&lt;span&gt;Can&lt;/span&gt; &lt;span&gt;Register&lt;/span&gt;, &lt;span&gt;Restart&lt;/span&gt; &lt;span&gt;and&lt;/span&gt; &lt;span&gt;Register&lt;/span&gt;)...[0.015 &lt;span&gt;s&lt;/span&gt;] &lt;span&gt;ok&lt;/span&gt;
    &lt;span&gt;wn_resource_layer_tests&lt;/span&gt;: &lt;span&gt;register_deregister&lt;/span&gt; (&lt;span&gt;Can&lt;/span&gt; &lt;span&gt;Register&lt;/span&gt;, &lt;span&gt;Deregister&lt;/span&gt; &lt;span&gt;and&lt;/span&gt; &lt;span&gt;Register&lt;/span&gt;)...[0.013 &lt;span&gt;s&lt;/span&gt;] &lt;span&gt;ok&lt;/span&gt;
    [&lt;span&gt;done&lt;/span&gt; &lt;span&gt;in&lt;/span&gt; 6.324 &lt;span&gt;s&lt;/span&gt;]
  [&lt;span&gt;done&lt;/span&gt; &lt;span&gt;in&lt;/span&gt; 6.325 &lt;span&gt;s&lt;/span&gt;]
=======================================================
  &lt;span&gt;All&lt;/span&gt; 4 &lt;span&gt;tests&lt;/span&gt; &lt;span&gt;passed&lt;/span&gt;.
&lt;span&gt;erl&lt;/span&gt; -&lt;span&gt;pa&lt;/span&gt; &lt;span&gt;ebin&lt;/span&gt;/ -&lt;span&gt;eval&lt;/span&gt; 'eunit:&lt;span&gt;test&lt;/span&gt;(&lt;span&gt;wn_file_layer&lt;/span&gt;,[&lt;span&gt;verbose&lt;/span&gt;]), &lt;span&gt;init&lt;/span&gt;:&lt;span&gt;stop&lt;/span&gt;().'
&lt;span&gt;Erlang&lt;/span&gt; &lt;span&gt;R14B&lt;/span&gt; (&lt;span&gt;erts&lt;/span&gt;-5.8.1) [&lt;span&gt;source&lt;/span&gt;] [&lt;span&gt;smp&lt;/span&gt;:4:4] [&lt;span&gt;rq&lt;/span&gt;:4] [&lt;span&gt;async&lt;/span&gt;-&lt;span&gt;threads&lt;/span&gt;:0] [&lt;span&gt;hipe&lt;/span&gt;] [&lt;span&gt;kernel&lt;/span&gt;-&lt;span&gt;poll&lt;/span&gt;:&lt;span&gt;false&lt;/span&gt;]

&lt;span&gt;Eshell&lt;/span&gt; &lt;span&gt;V5&lt;/span&gt;.8.1  (&lt;span&gt;abort&lt;/span&gt; &lt;span&gt;with&lt;/span&gt; ^&lt;span&gt;G&lt;/span&gt;)
1&amp;gt; ======================== &lt;span&gt;EUnit&lt;/span&gt; ========================
&lt;span&gt;module&lt;/span&gt; &lt;span&gt;'wn_file_layer'&lt;/span&gt;
  &lt;span&gt;module&lt;/span&gt; &lt;span&gt;'wn_file_layer_tests'&lt;/span&gt;
    &lt;span&gt;wn_file_layer_tests&lt;/span&gt;: &lt;span&gt;file_layer_local_test_&lt;/span&gt; (&lt;span&gt;Can&lt;/span&gt; &lt;span&gt;store&lt;/span&gt; &lt;span&gt;file&lt;/span&gt; &lt;span&gt;locally&lt;/span&gt;)...[0.326 &lt;span&gt;s&lt;/span&gt;] &lt;span&gt;ok&lt;/span&gt;
    &lt;span&gt;wn_file_layer_tests&lt;/span&gt;: &lt;span&gt;file_layer_local_test_&lt;/span&gt; (&lt;span&gt;Can&lt;/span&gt; &lt;span&gt;retrieve&lt;/span&gt; &lt;span&gt;files&lt;/span&gt; &lt;span&gt;locally&lt;/span&gt;)...[0.003 &lt;span&gt;s&lt;/span&gt;] &lt;span&gt;ok&lt;/span&gt;
    &lt;span&gt;wn_file_layer_tests&lt;/span&gt;: &lt;span&gt;file_layer_local_test_&lt;/span&gt; (&lt;span&gt;Can&lt;/span&gt; &lt;span&gt;delete&lt;/span&gt; &lt;span&gt;files&lt;/span&gt; &lt;span&gt;locally&lt;/span&gt;)...[0.002 &lt;span&gt;s&lt;/span&gt;] &lt;span&gt;ok&lt;/span&gt;
    &lt;span&gt;wn_file_layer_tests&lt;/span&gt;: &lt;span&gt;can_store_distributed&lt;/span&gt; (&lt;span&gt;Can&lt;/span&gt; &lt;span&gt;store&lt;/span&gt; &lt;span&gt;file&lt;/span&gt; &lt;span&gt;distributed&lt;/span&gt;)...[0.024 &lt;span&gt;s&lt;/span&gt;] &lt;span&gt;ok&lt;/span&gt;
    &lt;span&gt;wn_file_layer_tests&lt;/span&gt;: &lt;span&gt;can_retrieve_distributed&lt;/span&gt; (&lt;span&gt;Can&lt;/span&gt; &lt;span&gt;retrieve&lt;/span&gt; &lt;span&gt;file&lt;/span&gt; &lt;span&gt;distributed&lt;/span&gt;)...[0.018 &lt;span&gt;s&lt;/span&gt;] &lt;span&gt;ok&lt;/span&gt;
    &lt;span&gt;wn_file_layer_tests&lt;/span&gt;: &lt;span&gt;can_delete_distributed&lt;/span&gt; (&lt;span&gt;Can&lt;/span&gt; &lt;span&gt;delete&lt;/span&gt; &lt;span&gt;file&lt;/span&gt; &lt;span&gt;distributed&lt;/span&gt;)...[0.020 &lt;span&gt;s&lt;/span&gt;] &lt;span&gt;ok&lt;/span&gt;
    &lt;span&gt;wn_file_layer_tests&lt;/span&gt;: &lt;span&gt;must_retain&lt;/span&gt; (&lt;span&gt;Must&lt;/span&gt; &lt;span&gt;retain&lt;/span&gt; &lt;span&gt;information&lt;/span&gt; &lt;span&gt;between&lt;/span&gt; &lt;span&gt;node&lt;/span&gt; &lt;span&gt;kill&lt;/span&gt; &lt;span&gt;and&lt;/span&gt; &lt;span&gt;node&lt;/span&gt; &lt;span&gt;restart&lt;/span&gt;)...[0.391 &lt;span&gt;s&lt;/span&gt;] &lt;span&gt;ok&lt;/span&gt;
    [&lt;span&gt;done&lt;/span&gt; &lt;span&gt;in&lt;/span&gt; 2.348 &lt;span&gt;s&lt;/span&gt;]
  [&lt;span&gt;done&lt;/span&gt; &lt;span&gt;in&lt;/span&gt; 2.348 &lt;span&gt;s&lt;/span&gt;]
=======================================================
  &lt;span&gt;All&lt;/span&gt; 7 &lt;span&gt;tests&lt;/span&gt; &lt;span&gt;passed&lt;/span&gt;.
&lt;span&gt;erl&lt;/span&gt; -&lt;span&gt;pa&lt;/span&gt; &lt;span&gt;ebin&lt;/span&gt;/ -&lt;span&gt;eval&lt;/span&gt; 'eunit:&lt;span&gt;test&lt;/span&gt;(&lt;span&gt;wn_job_layer&lt;/span&gt;,[&lt;span&gt;verbose&lt;/span&gt;]), &lt;span&gt;init&lt;/span&gt;:&lt;span&gt;stop&lt;/span&gt;().'
&lt;span&gt;Erlang&lt;/span&gt; &lt;span&gt;R14B&lt;/span&gt; (&lt;span&gt;erts&lt;/span&gt;-5.8.1) [&lt;span&gt;source&lt;/span&gt;] [&lt;span&gt;smp&lt;/span&gt;:4:4] [&lt;span&gt;rq&lt;/span&gt;:4] [&lt;span&gt;async&lt;/span&gt;-&lt;span&gt;threads&lt;/span&gt;:0] [&lt;span&gt;hipe&lt;/span&gt;] [&lt;span&gt;kernel&lt;/span&gt;-&lt;span&gt;poll&lt;/span&gt;:&lt;span&gt;false&lt;/span&gt;]

&lt;span&gt;Eshell&lt;/span&gt; &lt;span&gt;V5&lt;/span&gt;.8.1  (&lt;span&gt;abort&lt;/span&gt; &lt;span&gt;with&lt;/span&gt; ^&lt;span&gt;G&lt;/span&gt;)
1&amp;gt; ======================== &lt;span&gt;EUnit&lt;/span&gt; ========================
&lt;span&gt;module&lt;/span&gt; &lt;span&gt;'wn_job_layer'&lt;/span&gt;
  &lt;span&gt;module&lt;/span&gt; &lt;span&gt;'wn_job_layer_tests'&lt;/span&gt;
    &lt;span&gt;wn_job_layer_tests&lt;/span&gt;: &lt;span&gt;local_test_&lt;/span&gt; (&lt;span&gt;Can&lt;/span&gt; &lt;span&gt;register&lt;/span&gt; &lt;span&gt;locally&lt;/span&gt;)...[0.005 &lt;span&gt;s&lt;/span&gt;] &lt;span&gt;ok&lt;/span&gt;
    &lt;span&gt;wn_job_layer_tests&lt;/span&gt;: &lt;span&gt;local_test_&lt;/span&gt; (&lt;span&gt;Executed&lt;/span&gt; &lt;span&gt;locally&lt;/span&gt;)...{1299,338969,204767} : &lt;span&gt;file_fetching_done&lt;/span&gt;
{1299,338969,204771} : &lt;span&gt;executing_commands&lt;/span&gt;
{1299,338969,218209} : {&lt;span&gt;executing&lt;/span&gt;,&lt;span&gt;&quot;more EUnitFile&quot;&lt;/span&gt;}
{1299,338969,225651} : &lt;span&gt;&quot;1,2,3&quot;&lt;/span&gt;
{1299,338969,226358} : &lt;span&gt;no_more_commands&lt;/span&gt;
{1299,338969,226362} : &lt;span&gt;building_result_tgz&lt;/span&gt;
{1299,338969,226462} : &lt;span&gt;done&lt;/span&gt;
[1.018 &lt;span&gt;s&lt;/span&gt;] &lt;span&gt;ok&lt;/span&gt;
    &lt;span&gt;wn_job_layer_tests&lt;/span&gt;: &lt;span&gt;local_test_&lt;/span&gt; (&lt;span&gt;Executed&lt;/span&gt; &lt;span&gt;queue&lt;/span&gt;)...{1299,338970,229138} : &lt;span&gt;file_fetching_done&lt;/span&gt;
{1299,338970,229141} : &lt;span&gt;executing_commands&lt;/span&gt;
{1299,338970,229539} : {&lt;span&gt;executing&lt;/span&gt;,&lt;span&gt;&quot;file EunitFile&quot;&lt;/span&gt;}
{1299,338970,239052} : &lt;span&gt;&quot;EunitFile: ASCII text&quot;&lt;/span&gt;
{1299,338970,239301} : &lt;span&gt;no_more_commands&lt;/span&gt;
{1299,338970,239304} : &lt;span&gt;building_result_tgz&lt;/span&gt;
{1299,338970,239476} : &lt;span&gt;done&lt;/span&gt;
{1299,338970,243238} : &lt;span&gt;file_fetching_done&lt;/span&gt;
{1299,338970,243250} : &lt;span&gt;executing_commands&lt;/span&gt;
{1299,338970,243360} : {&lt;span&gt;executing&lt;/span&gt;,&lt;span&gt;&quot;cat EUnitFile&quot;&lt;/span&gt;}
{1299,338970,250730} : &lt;span&gt;&quot;1,2,3&quot;&lt;/span&gt;
{1299,338970,250782} : &lt;span&gt;no_more_commands&lt;/span&gt;
{1299,338970,250785} : &lt;span&gt;building_result_tgz&lt;/span&gt;
{1299,338970,250856} : &lt;span&gt;done&lt;/span&gt;
[1.105 &lt;span&gt;s&lt;/span&gt;] &lt;span&gt;ok&lt;/span&gt;
    &lt;span&gt;wn_job_layer_tests&lt;/span&gt;: &lt;span&gt;local_test_&lt;/span&gt; (&lt;span&gt;Queueus&lt;/span&gt; &lt;span&gt;on&lt;/span&gt; &lt;span&gt;resource&lt;/span&gt; &lt;span&gt;type&lt;/span&gt; &lt;span&gt;amount&lt;/span&gt;)...[0.001 &lt;span&gt;s&lt;/span&gt;] &lt;span&gt;ok&lt;/span&gt;
    &lt;span&gt;wn_job_layer_tests&lt;/span&gt;: &lt;span&gt;local_test_&lt;/span&gt; (&lt;span&gt;Canceled&lt;/span&gt; &lt;span&gt;in&lt;/span&gt; &lt;span&gt;queue&lt;/span&gt;)...[0.001 &lt;span&gt;s&lt;/span&gt;] &lt;span&gt;ok&lt;/span&gt;
    &lt;span&gt;wn_job_layer_tests&lt;/span&gt;: &lt;span&gt;local_test_&lt;/span&gt; (&lt;span&gt;Done&lt;/span&gt; &lt;span&gt;Job&lt;/span&gt; &lt;span&gt;Stored&lt;/span&gt; &lt;span&gt;in&lt;/span&gt; &lt;span&gt;file&lt;/span&gt; &lt;span&gt;layer&lt;/span&gt;)...[0.606 &lt;span&gt;s&lt;/span&gt;] &lt;span&gt;ok&lt;/span&gt;
    &lt;span&gt;wn_job_layer_tests&lt;/span&gt;: &lt;span&gt;local_test_&lt;/span&gt; (&lt;span&gt;Done&lt;/span&gt; &lt;span&gt;Job&lt;/span&gt; &lt;span&gt;canceled&lt;/span&gt;)...{1299,338971,966369} : &lt;span&gt;file_fetching_done&lt;/span&gt;
{1299,338971,966377} : &lt;span&gt;executing_commands&lt;/span&gt;
{1299,338971,966651} : {&lt;span&gt;executing&lt;/span&gt;,&lt;span&gt;&quot;perl -e 'print(\&quot;HelloWorld\n\&quot;)'&quot;&lt;/span&gt;}
{1299,338972,3168} : &lt;span&gt;&quot;HelloWorld&quot;&lt;/span&gt;
{1299,338972,3405} : &lt;span&gt;no_more_commands&lt;/span&gt;
{1299,338972,3408} : &lt;span&gt;building_result_tgz&lt;/span&gt;
{1299,338972,3508} : &lt;span&gt;done&lt;/span&gt;
[0.504 &lt;span&gt;s&lt;/span&gt;] &lt;span&gt;ok&lt;/span&gt;
    [&lt;span&gt;done&lt;/span&gt; &lt;span&gt;in&lt;/span&gt; 3.305 &lt;span&gt;s&lt;/span&gt;]
  [&lt;span&gt;done&lt;/span&gt; &lt;span&gt;in&lt;/span&gt; 3.305 &lt;span&gt;s&lt;/span&gt;]
=======================================================
  &lt;span&gt;All&lt;/span&gt; 7 &lt;span&gt;tests&lt;/span&gt; &lt;span&gt;passed&lt;/span&gt;.
&lt;span&gt;dialyzer&lt;/span&gt; &lt;span&gt;src&lt;/span&gt;/*.&lt;span&gt;erl&lt;/span&gt; &lt;span&gt;test&lt;/span&gt;/*.&lt;span&gt;erl&lt;/span&gt;
  &lt;span&gt;Checking&lt;/span&gt; &lt;span&gt;whether&lt;/span&gt; &lt;span&gt;the&lt;/span&gt; &lt;span&gt;PLT&lt;/span&gt; /&lt;span&gt;Users&lt;/span&gt;/&lt;span&gt;zenon&lt;/span&gt;/.&lt;span&gt;dialyzer_plt&lt;/span&gt; &lt;span&gt;is&lt;/span&gt; &lt;span&gt;up&lt;/span&gt;-&lt;span&gt;to&lt;/span&gt;-&lt;span&gt;date&lt;/span&gt;... &lt;span&gt;yes&lt;/span&gt;
  &lt;span&gt;Proceeding&lt;/span&gt; &lt;span&gt;with&lt;/span&gt; &lt;span&gt;analysis&lt;/span&gt;...
&lt;span&gt;Unknown&lt;/span&gt; &lt;span&gt;functions&lt;/span&gt;:
  &lt;span&gt;eunit&lt;/span&gt;:&lt;span&gt;test/1&lt;/span&gt;
 &lt;span&gt;done&lt;/span&gt; &lt;span&gt;in&lt;/span&gt; 0m6.92s
&lt;span&gt;done&lt;/span&gt; (&lt;span&gt;passed&lt;/span&gt; &lt;span&gt;successfully&lt;/span&gt;)
&lt;span&gt;zen&lt;/span&gt;:&lt;span&gt;worker_net&lt;/span&gt;-0.1 &lt;span&gt;zenon&lt;/span&gt;&lt;span&gt;$ &lt;/span&gt;&lt;/pre&gt;
&lt;p&gt;&lt;!--- End Of Snippet --&gt;&lt;/p&gt;
&lt;h3&gt;The end&lt;/h3&gt;
&lt;h3&gt;This concludes the TDD hands on project for the WorkerNet &amp;#8211; the source can now be found through my github repository for this.&lt;/h3&gt;
&lt;p&gt;git://github.com/Gianfrancoalongi/WorkerNet.git&lt;/p&gt;
&lt;br /&gt;  &lt;a rel=&quot;nofollow&quot; href=&quot;http://feeds.wordpress.com/1.0/gocomments/erlcode.wordpress.com/685/&quot;&gt;&lt;img alt=&quot;&quot; border=&quot;0&quot; src=&quot;http://feeds.wordpress.com/1.0/comments/erlcode.wordpress.com/685/&quot; /&gt;&lt;/a&gt; &lt;a rel=&quot;nofollow&quot; href=&quot;http://feeds.wordpress.com/1.0/godelicious/erlcode.wordpress.com/685/&quot;&gt;&lt;img alt=&quot;&quot; border=&quot;0&quot; src=&quot;http://feeds.wordpress.com/1.0/delicious/erlcode.wordpress.com/685/&quot; /&gt;&lt;/a&gt; &lt;a rel=&quot;nofollow&quot; href=&quot;http://feeds.wordpress.com/1.0/gofacebook/erlcode.wordpress.com/685/&quot;&gt;&lt;img alt=&quot;&quot; border=&quot;0&quot; src=&quot;http://feeds.wordpress.com/1.0/facebook/erlcode.wordpress.com/685/&quot; /&gt;&lt;/a&gt; &lt;a rel=&quot;nofollow&quot; href=&quot;http://feeds.wordpress.com/1.0/gotwitter/erlcode.wordpress.com/685/&quot;&gt;&lt;img alt=&quot;&quot; border=&quot;0&quot; src=&quot;http://feeds.wordpress.com/1.0/twitter/erlcode.wordpress.com/685/&quot; /&gt;&lt;/a&gt; &lt;a rel=&quot;nofollow&quot; href=&quot;http://feeds.wordpress.com/1.0/gostumble/erlcode.wordpress.com/685/&quot;&gt;&lt;img alt=&quot;&quot; border=&quot;0&quot; src=&quot;http://feeds.wordpress.com/1.0/stumble/erlcode.wordpress.com/685/&quot; /&gt;&lt;/a&gt; &lt;a rel=&quot;nofollow&quot; href=&quot;http://feeds.wordpress.com/1.0/godigg/erlcode.wordpress.com/685/&quot;&gt;&lt;img alt=&quot;&quot; border=&quot;0&quot; src=&quot;http://feeds.wordpress.com/1.0/digg/erlcode.wordpress.com/685/&quot; /&gt;&lt;/a&gt; &lt;a rel=&quot;nofollow&quot; href=&quot;http://feeds.wordpress.com/1.0/goreddit/erlcode.wordpress.com/685/&quot;&gt;&lt;img alt=&quot;&quot; border=&quot;0&quot; src=&quot;http://feeds.wordpress.com/1.0/reddit/erlcode.wordpress.com/685/&quot; /&gt;&lt;/a&gt; &lt;img alt=&quot;&quot; border=&quot;0&quot; src=&quot;http://stats.wordpress.com/b.gif?host=erlcode.wordpress.com&amp;blog=15510807&amp;post=685&amp;subd=erlcode&amp;ref=&amp;feed=1&quot; width=&quot;1&quot; height=&quot;1&quot; /&gt;</description>
	<pubDate>Sat, 05 Mar 2011 15:46:09 +0000</pubDate>
</item>
<item>
	<title>RedHotErlang: Using SimpleBridge</title>
	<guid>http://www.redhoterlang.com/web/b4e6c0afca712a3b4f15869018000b2e</guid>
	<link>http://www.redhoterlang.com/web/plink?id=b4e6c0afca712a3b4f15869018000b2e</link>
	<description>I like [SimpleBridge][1]; written by [Rusty][2] and part of [Nitrogen][3]. 
I often use it as a separate component, i.e without using Nitrogen. 
SimpleBridge provides an abstraction above the underlying 
HTTP engine. Apart from being a nice programmi...</description>
	<pubDate>Sat, 05 Mar 2011 08:38:23 +0000</pubDate>
</item>
<item>
	<title>Erlang Inside: Interview with Kostis Sagonas – On Erlang tools, type systems, and how HiPE compares to JIT</title>
	<guid>http://erlanginside.com/?p=244</guid>
	<link>http://erlanginside.com/interview-with-kostis-sagonas-leader-of-the-hipe-team-and-erlang-tool-developer-244</link>
	<description>Chad DePue long form interview with Kostis Sagonas, leader of the HiPE and Dialyzer teams and a speaker at the upcoming Erlang Factory SF Bay.</description>
	<pubDate>Fri, 04 Mar 2011 15:22:45 +0000</pubDate>
</item>

</channel>
</rss>

