<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" media="screen" href="/~d/styles/rss2full.xsl"?><?xml-stylesheet type="text/css" media="screen" href="http://feeds.feedburner.com/~d/styles/itemcontent.css"?><rss xmlns:atom="http://www.w3.org/2005/Atom" xmlns:posterous="http://posterous.com/help/rss/1.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:media="http://search.yahoo.com/mrss/" version="2.0">
  <channel>
    <title>rfw.posterous.com</title>
    <link>http://rfw.posterous.com</link>
    <description>publishing without a plan</description>
    <generator>posterous.com</generator>
    <link xmlns="http://www.w3.org/2005/Atom" href="http://posterous.com/api/sup_update#3bb880092" type="application/json" rel="http://api.friendfeed.com/2008/03#sup" />
    
    
    <atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="self" type="application/rss+xml" href="http://feeds.feedburner.com/rfwpost" /><feedburner:info xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0" uri="rfwpost" /><atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="hub" href="http://pubsubhubbub.appspot.com/" /><atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="hub" href="http://posterous.superfeedr.com/" /><item>
      <pubDate>Thu, 11 Nov 2010 02:09:00 -0800</pubDate>
      <title>Stop procrastination: Introducing the noprocrast gem</title>
      <link>http://rfw.posterous.com/33144299</link>
      <guid>http://rfw.posterous.com/33144299</guid>
      <description>
        <![CDATA[<p>
	<p>For the last few weeks, I've been coding our live broadcasting platform <a href="http://mixlr.com">Mixlr</a> into shape, ready for its big launch next month. But with the amount of distractions available on the internet, it's easy get pulled away. One minute I'm coding, the next I'm reading an article on Hacker News. I don't even know it's happened until it's too late: muscle memory takes over. Somehow, I subconsciously hit CMD-TAB, CMD-L, down, return and there's an interesting article that simply must be read.</p>
<p>It's a killer.</p>
<p>So yesterday I made a solution. Sometimes I edit my /etc/hosts file to make news.ycombinator.com, Twitter, Facebook and so on resolve to 127.0.0.1, and therefore be unreachable. But it's a hassle to manually edit the file again and again.</p>
<p>Enter the <a href="http://github.com/rfwatson/noprocrast">noprocrast</a> gem. Noprocrast simply automates adding and removing addictive websites from /etc/hosts. To install it, simply:</p>
<p><div class="data type-text">
    
      <table class="lines" cellspacing="0" cellpadding="0">
        <tr>
          <td>
            <pre class="line_numbers"><span rel="#L1" id="L1">1</span>
</pre>
          </td>
          <td width="100%">
            
              
                <div class="highlight"><pre /><div class="line" id="LC1">gem install noprocrast</div></pre></div>
              
            
          </td>
        </tr>
      </table>
    
  </div></p>
<p>Check whether noprocrast is doing anything:</p>
<p><div class="data type-text">
    
      <table class="lines" cellspacing="0" cellpadding="0">
        <tr>
          <td>
            <pre class="line_numbers"><span rel="#L1" id="L1">1</span>
</pre>
          </td>
          <td width="100%">
            
              
                <div class="highlight"><pre /><div class="line" id="LC1">noprocrast status</div></pre></div>
              
            
          </td>
        </tr>
      </table>
    
  </div></p>
<p>Edit the list of hosts which noprocrast will block:</p>
<p><div class="data type-text">
    
      <table class="lines" cellspacing="0" cellpadding="0">
        <tr>
          <td>
            <pre class="line_numbers"><span rel="#L1" id="L1">1</span>
</pre>
          </td>
          <td width="100%">
            
              
                <div class="highlight"><pre /><div class="line" id="LC1">noprocrast edit</div></pre></div>
              
            
          </td>
        </tr>
      </table>
    
  </div></p>
<p>Activate noprocrast (start blocking sites):</p>
<p><div class="data type-tcsh">
    
      <table class="lines" cellspacing="0" cellpadding="0">
        <tr>
          <td>
            <pre class="line_numbers"><span rel="#L1" id="L1">1</span>
</pre>
          </td>
          <td width="100%">
            
              
                <div class="highlight"><pre /><div class="line" id="LC1">noprocrast on     <span class="c"># you may need to use sudo</span></div></pre></div>
              
            
          </td>
        </tr>
      </table>
    
  </div></p>
<p>De-activate noprocrast (stop blocking sites):</p>
<p><div class="data type-tcsh">
    
      <table class="lines" cellspacing="0" cellpadding="0">
        <tr>
          <td>
            <pre class="line_numbers"><span rel="#L1" id="L1">1</span>
</pre>
          </td>
          <td width="100%">
            
              
                <div class="highlight"><pre /><div class="line" id="LC1">noprocrast off     <span class="c"># you may need to use sudo</span></div></pre></div>
              
            
          </td>
        </tr>
      </table>
    
  </div></p>
<p>Blocked hosts are stored in a file called .noprocrast in your home directory. Just in case, noprocrast makes a backup of your hosts file, to /etc/.hosts.noprocastbackup, the first time it runs.</p>
<p>The gem has been built and tested on Mac OSX, but should run fine on Linux. Finally, if you want to improve or expand the code, clone it <a href="http://github.com/rfwatson/noprocrast">at Github</a>.</p>
<p>Happy efficient coding.</p>
<p>&nbsp;</p>
<p><strong>Follow <a href="http://twitter.com/rfwatson">@rfwatson on Twitter</a></strong></p>
<p><strong><a href="http://mixlr.com">Mixlr</a></strong>: high-quality audio broadcasting platform launches 2nd December.</p>
	
</p>

<p><a href="http://rfw.posterous.com/33144299">Permalink</a> 

	| <a href="http://rfw.posterous.com/33144299#comment">Leave a comment&nbsp;&nbsp;&raquo;</a>

</p>]]>
      </description>
      <posterous:author>
        <posterous:userImage>http://posterous.com/images/profile/missing-user-75.png</posterous:userImage>
        <posterous:profileUrl>http://posterous.com/users/3sIZjvZRR8A1</posterous:profileUrl>
        <posterous:firstName>Rob</posterous:firstName>
        <posterous:lastName>W</posterous:lastName>
        <posterous:nickName>robw</posterous:nickName>
        <posterous:displayName>Rob W</posterous:displayName>
      </posterous:author>
    </item>
    <item>
      <pubDate>Fri, 11 Jun 2010 04:01:00 -0700</pubDate>
      <title>Mixlr on Hacker News</title>
      <link>http://rfw.posterous.com/mixlr-on-hacker-news</link>
      <guid>http://rfw.posterous.com/mixlr-on-hacker-news</guid>
      <description>
        <![CDATA[<p>
	<p>Today, I'm inviting readers of <a href="http://news.ycombinator.com" target="_blank">Hacker News</a> to sign up for Mixlr. </p>
<p />
<div>If you'd prefer to jump right in and start using Mixlr straight away, <a href="http://mixlr.com/signup" target="_blank"><strong>sign up here</strong></a> using the invitation code <strong>HN</strong>. (In case there are no invitations left, then email me: rob &lt;at&gt; <a href="http://mixlr.com" target="_blank">mixlr.com</a>. I will send you one out.)</div>
<p />
<div>If you've got a few minutes to spare then read on. The rest of this post summarises a little background about Mixlr and the service I am looking to provide.</div>
<p />
<div><span style="font-size: large;">What is Mixlr?</span></div>
<p />
<div>Mixlr is a web-application which aims to simplify sharing <strong>live</strong> music. If you are a DJ or part of a live act, you may have already found that it can be surprisingly hard to broadcast yourself live on the web. Essentially, there are two options. Firstly, set up your own streaming server and share the URL with your listeners. This route will give the broadcaster total control over what is streamed, but is technically difficult to do. Sharing an iTunes URL isn't ideal either (most users will find it easier to visit a web page). And let's be honest: most DJs and musicians are far too busy practising, performing and shmoozing to be a Unix sysadmin wizard on the side. </div>
<p />
<div>Secondly, you could attempt to use one of the existing services which have attempted to commoditise the process of internet broadcasting. Too many of these services, however, apply the advertising model heavily to their business, meaning that the user has their listening experience compromised by audio adverts, popups or irritating Flash ads. This is simply not acceptable for many performers, or listeners -- especially now that we have moved from the MySpace era where such behaviour was <em>de rigueur</em>, into the conspicuously ad-free epoch of <a href="http://www.soundcloud.com" target="_blank">SoundCloud</a>. </div>
<p />
<div>Mixlr wants to solve this problem for musicians and DJs. Mixlr offers:</div>
<div>
<ul>
<li>A simple way for musicians to share live performances on the web. Simple means that a musician with the humblest amount of computer proficiency should be able to use it with ease.</li>
<li>A business model focussed on modestly charging the content creator for distribution, and not the consumer for listening to the content. </li>
<li>A user interface which does not distract the user, or devalue content, with advertising.</li>
</ul>
<div><span style="font-size: large;">So.. what does it let me do?</span></div>
</div>
<div>
<ul>
<li>Broadcast live audio<strong>.</strong> <a href="http://mixlr.com/broadcast" target="_blank">Download our simple Mac OSX application</a>. Select an audio input and hit "Share". You'll be streaming live on the internet instantly -- with no adverts, interruptions or messy configuration to have to battle with. (Sorry, Windows and Linux users: we definitely have you in mind, but we want to get our Mac application perfect before we release for other platforms. Follow us <a href="http://twitter.com/mixlr" target="_blank">on Twitter</a> for updates).</li>
<li>Publish live sets to the web<strong>.</strong> When you've finished playing live, it's just one click to save the recording of your performance on the web -- no extra uploading required. Anybody can listen back to it immediately. Use SoundCloud? No problem: it's just takes a click to export your recording there too.</li>
<li>Even if you're not running OSX, you can still upload your pre-recorded recordings easily, and host them on Mixlr.</li>
<li>Get feedback from your listeners<strong>.</strong> Mixlr's experimental real-time comments (kind of like Campfire: instant messages which are saved forever and timed against a broadcast) help to recreate the natural feedback cycle between audience and performer which occurs during a live gig.</li>
</ul>
<div>There is also tight integration with Twitter and Facebook to help make it even easier to share live music with your friends and followers.</div>
</div>
<p />
<div>Anyway, that's probably enough information to get you started. I'd love to get feedback so please leave it either below, or back on the Hacker News thread.</div>
<p />
<div>
<strong>Sign up for Mixlr here: <a href="http://mixlr.com/signup" target="_blank">http://mixlr.com/signup</a> </strong>(Sign up code: HN)</div>
	
</p>

<p><a href="http://rfw.posterous.com/mixlr-on-hacker-news">Permalink</a> 

	| <a href="http://rfw.posterous.com/mixlr-on-hacker-news#comment">Leave a comment&nbsp;&nbsp;&raquo;</a>

</p>]]>
      </description>
      <posterous:author>
        <posterous:userImage>http://posterous.com/images/profile/missing-user-75.png</posterous:userImage>
        <posterous:profileUrl>http://posterous.com/users/3sIZjvZRR8A1</posterous:profileUrl>
        <posterous:firstName>Rob</posterous:firstName>
        <posterous:lastName>W</posterous:lastName>
        <posterous:nickName>robw</posterous:nickName>
        <posterous:displayName>Rob W</posterous:displayName>
      </posterous:author>
    </item>
    <item>
      <pubDate>Wed, 09 Jun 2010 13:38:00 -0700</pubDate>
      <title>How much is too minimal</title>
      <link>http://rfw.posterous.com/how-to-measure-exactly-how-minimal</link>
      <guid>http://rfw.posterous.com/how-to-measure-exactly-how-minimal</guid>
      <description>
        <![CDATA[<p>
	<p>I'm a big fan of the concept of <a href="http://en.wikipedia.org/wiki/Minimum_viable_product">Minimum Viable Product</a>. Take an idea, think it through and identify the most fundamental characteristics which can be implemented to get real people using your product. Then, get feedback and move forward iteratively. Keeping the MVP in mind keeps you close to what your real users want to do, and helps to minimize time spent developing features which just aren't used. It's the formula I've followed whilst developing my live music sharing platform <a href="http://mixlr.com">Mixlr</a>, and for the most part it's worked great. </p>
<div> </div>
<div>Cutting back on unnecessary features, however, does not mean letting your standards slip on those which are left. MVP does not mean implementing the bare minimum with which people could use your application; rather, the minimum which people can use your application <em>successfully</em>, and leave with a great feeling. A successful MVP then should consist of only the most fundamental features, implemented exquisitely.</div>
<div> </div>
<div>Over the last few weeks I have been working, slowly readying <a href="http://mixlr.com">Mixlr</a> for a wider release. Whilst the functionality has been slowly slotting into place, I've had a nagging feeling that something was missing. I couldn't place it for ages, until I started playing around with the signup procedure.</div>
<div> </div>
<div>How many times have you seen a signup page which looks something like this?</div>
<div> </div>
<div><div class='p_embed p_image_embed'>
<img alt="Screen_shot_2010-06-09_at_20" height="577" src="http://posterous.com/getfile/files.posterous.com/rfw/D5bWXEDQxuXVlTHlT7InrPC5c7oo2g38Jgzd3fk0HTjIiNyldgoMLagODKfK/Screen_shot_2010-06-09_at_20.1.png" width="396" />
</div>
</div>
<div> </div>
<div>Straight out of the box material. Ok, it's simple and people know what to do with it: but for most webapps, the experience of signing up will become the lasting first impression for the majority of its users. And the signup procedure of websites like Facebook and Twitter have raised the bar in terms of the level of attention to detail users expect when signing up for a service online. Actually, this form is total crap: it doesn't offer anything to the user, is totally unsuccessful at distinguishing the application from any other, and has no place <strong>even in an Minimum Viable Product</strong>.</div>
<div> </div>
<div>So, I started to have a play around with Mixlr. Taking a small dose inspiration from Twitter, I came up with a new landing page:</div>
<div> </div>
<div><div class='p_embed p_image_embed'>
<a href="http://posterous.com/getfile/files.posterous.com/rfw/ZzajIFpxSfhL6k9fjHWFLlpOn8Pt1VEShGpMVo1fpoomqt5ypLInXZnnvo0L/0Screen_shot_2010-06-09_at_20.2.png"><img alt="0screen_shot_2010-06-09_at_20" height="460" src="http://posterous.com/getfile/files.posterous.com/rfw/jvuwRGrzg5WitvfIKWnReeOhNMCltn49yUtVE23mrL2ZwJRbllcpqDfcRVsW/0Screen_shot_2010-06-09_at_20.2.png.scaled.500.jpg" width="500" /></a>
</div>
</div>
<div> </div>
<div>Ok, so this form is serving exactly the same purpose, but to me at least this looks a million miles away from the original. It just feels more "official" and engaging. The presentation has moved from the amateur to be infinitely more professional. Without changing any actual practical functionality whatsoever. <strong>Definitely</strong> a big improvement.</div>
<div> </div>
<div>Next, I added two further steps to the signup process:</div>
<p />
<p> </p>
<div>
<p><div class='p_embed p_image_embed'>
<a href="http://posterous.com/getfile/files.posterous.com/rfw/tfFIGx8R8Zlp82FfiowfiNXTcfmvrUP9TC4L4w754DXtgXNp3VxkdbTbz843/Screen_shot_2010-06-09_at_20.2.png"><img alt="Screen_shot_2010-06-09_at_20" height="319" src="http://posterous.com/getfile/files.posterous.com/rfw/vGiPgzNEmGXeYjyJIvACzcoasPU3sygkVAzJoFAXVzQyRunEWuGew0tPzP7u/Screen_shot_2010-06-09_at_20.2.png.scaled.500.jpg" width="500" /></a>
</div>
</p>
</div>
<div>
<p><div class='p_embed p_image_embed'>
<a href="http://posterous.com/getfile/files.posterous.com/rfw/XZz48Grf5vinhqFeJBZZvOYVZKmTY9ncnKLZhrSwQN5yRvQ2tWWz7wqUtJKs/Screen_shot_2010-06-09_at_20.3.png"><img alt="Screen_shot_2010-06-09_at_20" height="441" src="http://posterous.com/getfile/files.posterous.com/rfw/Caly6ZHmyisDBHy98TAujWVj92frUDyuG9OCZq212AUcZb1YE4545BPvsKOh/Screen_shot_2010-06-09_at_20.3.png.scaled.500.jpg" width="500" /></a>
</div>
</p>
<p> </p>
</div>
<div> </div>
<div>These pages serve at least three purposes. Users are more likely to add more of their personal details, which of course potentially opens up a multitude of benefits to everybody concerned. Secondly, the user is held by the hand and gently led into the heart of the application, instead of just being dumped on a home page and left to fend for themselves. To me this user is far more likely to become engaged and involved in the application. And finally, once again, the whole procedure again comes across like something professional. It's a different world than the single original page, and only took a couple of days to implement, but could easily be mistaken for not belonging in an MVP.</div>
<div> </div>
<div>At this point, the signup is complete. All that's left is for the user to confirm their email address by clicking the standard link. The first page I display is thus:</div>
<div> </div>
<div><div class='p_embed p_image_embed'>
<a href="http://posterous.com/getfile/files.posterous.com/rfw/3Z1rpsR68qCVD2F1XZ4bL3Sg4bumQ1CM4sZNpWSr2RZkc0wJblZ4Y73MKurN/0Screen_shot_2010-06-09_at_20.3.png"><img alt="0screen_shot_2010-06-09_at_20" height="436" src="http://posterous.com/getfile/files.posterous.com/rfw/8F86zFwsUBY2njga3s6gGSiXvoL7opHlEkZA2zUw12O3BtSOVN607jWXIJ2Q/0Screen_shot_2010-06-09_at_20.3.png.scaled.500.jpg" width="500" /></a>
</div>
</div>
<div> </div>
<div>At first I was rendering all of the text in the normal dark grey, but it just felt far too easy to miss the main point -- <em>check your email!</em> -- which in any case has to be done before most of the other stuff works. Nevertheless, I wanted to keep the user's attention, and make it clear that I'm not letting go of their hand quite yet. As soon as the user opens their email and clicks the link, bingo:</div>
<div> </div>
<div><div class='p_embed p_image_embed'>
<a href="http://posterous.com/getfile/files.posterous.com/rfw/lQV7R188M7DsL9fRNp6rNEWQ3JBuxK7mNDQDWJihP6DKJQSEWlUJbPFNEnZP/Screen_shot_2010-06-09_at_20.4.png"><img alt="Screen_shot_2010-06-09_at_20" height="396" src="http://posterous.com/getfile/files.posterous.com/rfw/0EaZxSgijoE9VaudB0wVtffasPjMxWLlSpxog42Q3xfGaEhRzXxW3AA1zEwd/Screen_shot_2010-06-09_at_20.4.png.scaled.500.jpg" width="500" /></a>
</div>
</div>
<div> </div>
<div>The final piece of attention-to-detail lay in the content. Mixlr's sweetest feature is currently only available for Mac OSX. So splashing that as point number one seemed a bit unfair for Windows and Linux users. Employing a little bit of user-agent detection, however, means that a slightly different message can welcome users on non-Mac platforms:</div>
<div> </div>
<div><div class='p_embed p_image_embed'>
<a href="http://posterous.com/getfile/files.posterous.com/rfw/OEnVlZhO0QL4o3hsbPAlzk1HPdfIFuzmRgKli5Hmq1z5MvDG0kKfayhUqEHb/Screen_shot_2010-06-09_at_20.5.png"><img alt="Screen_shot_2010-06-09_at_20" height="255" src="http://posterous.com/getfile/files.posterous.com/rfw/OnpSaBiVipoPIq6LOllBPIl0ShIKzThpXzVB3qxVI2Tbqh3NaqBrFuEU5icz/Screen_shot_2010-06-09_at_20.5.png.scaled.500.jpg" width="500" /></a>
</div>
</div>
<div> </div>
<div><span style="font-size: large;">Conclusion</span></div>
<div> </div>
<div>What does all of this tell me? That while the concept of an MVP is deeply valuable, it's easy to say "no" too often, and forget that the features you <em>do</em> have should be implemented with all the care and perfection that is possible.</div>
<div> </div>
<div>Don't make your MVP too minimal. </div>
<div> </div>
<div> </div>
<div>If you want you can <a href="http://mixlr.com"><strong>sign up for a Mixlr invite here</strong></a>. You can also <a href="http://twitter.com/rfwatson">follow @rfwatson on Twitter</a>.</div>
<div> </div>
<div> </div>
<div> </div>
	
</p>

<p><a href="http://rfw.posterous.com/how-to-measure-exactly-how-minimal">Permalink</a> 

	| <a href="http://rfw.posterous.com/how-to-measure-exactly-how-minimal#comment">Leave a comment&nbsp;&nbsp;&raquo;</a>

</p>]]>
      </description>
      <posterous:author>
        <posterous:userImage>http://posterous.com/images/profile/missing-user-75.png</posterous:userImage>
        <posterous:profileUrl>http://posterous.com/users/3sIZjvZRR8A1</posterous:profileUrl>
        <posterous:firstName>Rob</posterous:firstName>
        <posterous:lastName>W</posterous:lastName>
        <posterous:nickName>robw</posterous:nickName>
        <posterous:displayName>Rob W</posterous:displayName>
      </posterous:author>
      <media:content type="image/png" height="580" width="658" url="http://getfile9.posterous.com/getfile/files.posterous.com/rfw/XZz48Grf5vinhqFeJBZZvOYVZKmTY9ncnKLZhrSwQN5yRvQ2tWWz7wqUtJKs/Screen_shot_2010-06-09_at_20.3.png">
        <media:thumbnail height="441" width="500" url="http://getfile0.posterous.com/getfile/files.posterous.com/rfw/Caly6ZHmyisDBHy98TAujWVj92frUDyuG9OCZq212AUcZb1YE4545BPvsKOh/Screen_shot_2010-06-09_at_20.3.png.scaled.500.jpg" />
      </media:content>
      <media:content type="image/png" height="577" width="396" url="http://getfile4.posterous.com/getfile/files.posterous.com/rfw/D5bWXEDQxuXVlTHlT7InrPC5c7oo2g38Jgzd3fk0HTjIiNyldgoMLagODKfK/Screen_shot_2010-06-09_at_20.1.png">
        <media:thumbnail height="577" width="396" url="http://getfile4.posterous.com/getfile/files.posterous.com/rfw/D5bWXEDQxuXVlTHlT7InrPC5c7oo2g38Jgzd3fk0HTjIiNyldgoMLagODKfK/Screen_shot_2010-06-09_at_20.1.png" />
      </media:content>
      <media:content type="image/png" height="345" width="677" url="http://getfile8.posterous.com/getfile/files.posterous.com/rfw/OEnVlZhO0QL4o3hsbPAlzk1HPdfIFuzmRgKli5Hmq1z5MvDG0kKfayhUqEHb/Screen_shot_2010-06-09_at_20.5.png">
        <media:thumbnail height="255" width="500" url="http://getfile9.posterous.com/getfile/files.posterous.com/rfw/OnpSaBiVipoPIq6LOllBPIl0ShIKzThpXzVB3qxVI2Tbqh3NaqBrFuEU5icz/Screen_shot_2010-06-09_at_20.5.png.scaled.500.jpg" />
      </media:content>
      <media:content type="image/png" height="568" width="651" url="http://getfile4.posterous.com/getfile/files.posterous.com/rfw/3Z1rpsR68qCVD2F1XZ4bL3Sg4bumQ1CM4sZNpWSr2RZkc0wJblZ4Y73MKurN/0Screen_shot_2010-06-09_at_20.3.png">
        <media:thumbnail height="436" width="500" url="http://getfile5.posterous.com/getfile/files.posterous.com/rfw/8F86zFwsUBY2njga3s6gGSiXvoL7opHlEkZA2zUw12O3BtSOVN607jWXIJ2Q/0Screen_shot_2010-06-09_at_20.3.png.scaled.500.jpg" />
      </media:content>
      <media:content type="image/png" height="418" width="655" url="http://getfile9.posterous.com/getfile/files.posterous.com/rfw/tfFIGx8R8Zlp82FfiowfiNXTcfmvrUP9TC4L4w754DXtgXNp3VxkdbTbz843/Screen_shot_2010-06-09_at_20.2.png">
        <media:thumbnail height="319" width="500" url="http://getfile0.posterous.com/getfile/files.posterous.com/rfw/vGiPgzNEmGXeYjyJIvACzcoasPU3sygkVAzJoFAXVzQyRunEWuGew0tPzP7u/Screen_shot_2010-06-09_at_20.2.png.scaled.500.jpg" />
      </media:content>
      <media:content type="image/png" height="519" width="655" url="http://getfile4.posterous.com/getfile/files.posterous.com/rfw/lQV7R188M7DsL9fRNp6rNEWQ3JBuxK7mNDQDWJihP6DKJQSEWlUJbPFNEnZP/Screen_shot_2010-06-09_at_20.4.png">
        <media:thumbnail height="396" width="500" url="http://getfile5.posterous.com/getfile/files.posterous.com/rfw/0EaZxSgijoE9VaudB0wVtffasPjMxWLlSpxog42Q3xfGaEhRzXxW3AA1zEwd/Screen_shot_2010-06-09_at_20.4.png.scaled.500.jpg" />
      </media:content>
      <media:content type="image/png" height="602" width="654" url="http://getfile9.posterous.com/getfile/files.posterous.com/rfw/ZzajIFpxSfhL6k9fjHWFLlpOn8Pt1VEShGpMVo1fpoomqt5ypLInXZnnvo0L/0Screen_shot_2010-06-09_at_20.2.png">
        <media:thumbnail height="460" width="500" url="http://getfile0.posterous.com/getfile/files.posterous.com/rfw/jvuwRGrzg5WitvfIKWnReeOhNMCltn49yUtVE23mrL2ZwJRbllcpqDfcRVsW/0Screen_shot_2010-06-09_at_20.2.png.scaled.500.jpg" />
      </media:content>
    </item>
    <item>
      <pubDate>Thu, 15 Apr 2010 07:27:56 -0700</pubDate>
      <title>Redis-powered newsfeed (addendum)</title>
      <link>http://rfw.posterous.com/redis-powered-newsfeed-addendum</link>
      <guid>http://rfw.posterous.com/redis-powered-newsfeed-addendum</guid>
      <description>
        <![CDATA[<p>
	Some interesting commentary on my Redis newsfeed <a href="http://rfw.posterous.com/a-redis-powered-newsfeed-implementation">post</a> from yesterday:<p /><div><a href="http://nosql.mypopescu.com/post/522925057/redis-powered-facebook-like-newsfeeds">http://nosql.mypopescu.com/post/522925057/redis-powered-facebook-like-newsfeeds</a></div>
	
</p>

<p><a href="http://rfw.posterous.com/redis-powered-newsfeed-addendum">Permalink</a> 

	| <a href="http://rfw.posterous.com/redis-powered-newsfeed-addendum#comment">Leave a comment&nbsp;&nbsp;&raquo;</a>

</p>]]>
      </description>
      <posterous:author>
        <posterous:userImage>http://posterous.com/images/profile/missing-user-75.png</posterous:userImage>
        <posterous:profileUrl>http://posterous.com/users/3sIZjvZRR8A1</posterous:profileUrl>
        <posterous:firstName>Rob</posterous:firstName>
        <posterous:lastName>W</posterous:lastName>
        <posterous:nickName>robw</posterous:nickName>
        <posterous:displayName>Rob W</posterous:displayName>
      </posterous:author>
    </item>
    <item>
      <pubDate>Wed, 14 Apr 2010 10:51:00 -0700</pubDate>
      <title>A Redis-powered newsfeed implementation</title>
      <link>http://rfw.posterous.com/a-redis-powered-newsfeed-implementation</link>
      <guid>http://rfw.posterous.com/a-redis-powered-newsfeed-implementation</guid>
      <description>
        <![CDATA[<p>
	<p>Most people are now familiar with the concept the newsfeed. Recently, I decided to implement newsfeed functionality on <a href="http://mixlr.com" target="_blank">Mixlr</a>, my embryonic mixset distribution service. This post shows how I have used Redis to create a fast and efficient newsfeed implementation. </p>
<div><div class='p_embed p_image_embed'>
<a href="http://posterous.com/getfile/files.posterous.com/rfw/7RrZO3YsoWpIttZnbehiMczZI7HhyqtrbqcX7S40TyzSWY2TZ6fsT6VKaLyG/shot2.png"><img alt="Shot2" height="66" src="http://posterous.com/getfile/files.posterous.com/rfw/W38T2XuIxz9TUOpcYaoDhPqjUetYT9fJ083547cBuVb6thHIjpxLaW2QEMsx/shot2.png.scaled.500.jpg" width="500" /></a>
</div>
</div>
<div>
<span style="font-size: large;">What exactly should a newsfeed do?</span>
<div>
<div>
<br />First of all, let's define what to expect from a newsfeed implementation.</div>
</div>
<div> </div>
<div>
<strong>It should log every action, of every user.</strong> On Mixlr, users can currently upload, rate and share mixsets, and follow other users. The newsfeed should store a single stream of all of these actions. We shall call this stream the <em>master newsfeed</em>: every action carried out by every user<em>, </em>anywhere on the site.</div>
<div> </div>
<div><strong>Allow actions to be grouped by perspective. <span style="font-weight: normal;">In real-life use, certain subsets of the master newsfeed will be more useful than the entire stream. For example, all of the actions carried out by Fred, which will be displayed on Fred's profile page. Or all of the actions carried out by users who Kevin follows, which will be displayed on Kevin's home page. Or even all of the actions performed by Fred, Kevin and all other users on a particular mixset. It should be possible to efficiently retrieve newsfeeds from all of these perspectives.</span></strong></div>
<p />
<div>
<strong>Generate arbitrary data formats.</strong> It should be possible to generate different formats of data from any given newsfeed perspective. For example, I might want to make the feed of Fred's recent actions available as XHTML in a mobile client or Facebook application, HTML in a normal web page, or JSON as part of an API response. This quickly rules out caching pre-rendered HTML as an implementation.</div>
<p />
<div>
<strong><span style="font-weight: normal;"><strong>Be fast. <em>Really</em> fast.</strong> Mixlr is built for speed. This means that whatever implementation is chosen, there must be essentially no increase in the time any individual page takes to load. This should hold regardless of the perspective being retrieved, the data format being generated, or the amount of news "items" being requested.<br /> </span></strong>
<div> </div>
<div>A naive newsfeed implementation might try to join whatever database tables it needs to retrieve the newsfeed data, every time the page is requested. The problem is that in a typical database schema, such a request will require a large number of tables to be joined. As the number of users on a website grows, the amount of data being examined increases at a rate far enough exceeding O(n) that performance falls away quickly. In short, if you want your web application to scale, then this is not a good way to implement newsfeeds. </div>
<div> </div>
</div>
<div>There are a number of strategies which help to ease this problem (all of which are outside the scope of this post). I have approached the problem a different way: remove SQL from the equation entirely.</div>
<div> </div>
<div><span style="font-size: large;">Enter Redis.</span></div>
<p />
<div>
<a href="http://code.google.com/p/redis/">Redis</a> is a key-value storage system which is typically configured to be semi-persistent. In Redis' case, this means that data is held in memory and saved at regular intervals to disk. This is perfect for implementing a newsfeed, because in the worst case scenario -- the server crashes without warning -- all that happens is that the last few minutes of newsfeed entries are lost. For Mixlr, and many other websites, this is an acceptable risk. The chances are, no user will even notice. And when everything does work as it should, all newsfeed data will be held in memory and retrieving it is really, really fast.</div>
<div> </div>
<div>My implementation makes use of all of Redis' basic datatypes. A typical newsitem, representing a single user action, looks something like this:</div>
<div> </div>
<div><div class="data type-ruby">
    
      <table class="lines" cellspacing="0" cellpadding="0">
        <tr>
          <td>
            <pre class="line_numbers"><span rel="#L1" id="L1">1</span>
<span rel="#L2" id="L2">2</span>
<span rel="#L3" id="L3">3</span>
<span rel="#L4" id="L4">4</span>
<span rel="#L5" id="L5">5</span>
<span rel="#L6" id="L6">6</span>
<span rel="#L7" id="L7">7</span>
<span rel="#L8" id="L8">8</span>
<span rel="#L9" id="L9">9</span>
</pre>
          </td>
          <td width="100%">
            
              
                <div class="highlight"><pre /><div class="line" id="LC1"><span class="p">{</span> </div><div class="line" id="LC2">&nbsp;&nbsp;<span class="ss">:type</span> <span class="o">=&gt;</span> <span class="ss">:rate</span><span class="p">,</span> </div><div class="line" id="LC3">&nbsp;&nbsp;<span class="ss">:user_id</span> <span class="o">=&gt;</span> <span class="mi">1</span><span class="p">,</span> </div><div class="line" id="LC4">&nbsp;&nbsp;<span class="ss">:target_class_name</span> <span class="o">=&gt;</span> <span class="s1">&#39;Mixset&#39;</span><span class="p">,</span> </div><div class="line" id="LC5">&nbsp;&nbsp;<span class="ss">:target_id</span> <span class="o">=&gt;</span> <span class="mi">100</span><span class="p">,</span> </div><div class="line" id="LC6">&nbsp;&nbsp;<span class="ss">:timestamp</span> <span class="o">=&gt;</span> <span class="mi">1271259457</span><span class="p">,</span> </div><div class="line" id="LC7">&nbsp;&nbsp;<span class="ss">:content</span> <span class="o">=&gt;</span> <span class="o">[</span><span class="s2">&quot;Rob rated mixset &#39;%s&#39; 10/10&quot;</span><span class="p">,</span> <span class="s2">&quot;Some great mixset&quot;</span><span class="p">,</span> <span class="s2">&quot;http://mixlr.com/somebody/some-great-mixset&quot;</span><span class="o">]</span></div><div class="line" id="LC8"><span class="p">}</span> </div><div class="line" id="LC9"><br /></div></pre></div>
              
            
          </td>
        </tr>
      </table>
    
  </div></div>
<div> </div>
<div>First I create an MD5 hash of this newsitem, minus the timestamp. If for any reason the application attempts to add a duplicate newsitem later on, the MD5 for this item will be identical to the first, and can be easily detected and discarded. Using the MD5 as a key, I store the newsitem in Redis as a hash datatype (a JSON-encoded string could also suffice).</div>
<div><div class='p_embed p_image_embed'>
<a href="http://posterous.com/getfile/files.posterous.com/rfw/2l3lF8twetTeoY0CV46wHJrg5xcx1XqU66wttZvjOTizdeUqKEEYfHVY4tkH/redis1.png"><img alt="Redis1" height="60" src="http://posterous.com/getfile/files.posterous.com/rfw/2C6JaB3pCwi9h113IZYLlXpWXaoM0HvjLBCCgHct3PwujMpQZCjoWHCrINN6/redis1.png.scaled.500.jpg" width="500" /></a>
</div>
</div>
<div>I can now retrieve the original data from memory at any time by using the MD5 as a key. (I set this key to expire in 7 days, which means Redis will clear it from memory automatically. Who's interested in old news, anyway?)</div>
<div> </div>
<div>Now it is time to represent the different newsfeed "perspectives" I want to retrieve. Each perspective relates to a particular view of the newsfeed. These perspectives are simply implemented using ordered Redis lists, one for each perspective. Each list is full of MD5 keys, each one pointing to a newsitem value. </div>
<div> </div>
<div>In Mixlr, I maintain the following perspectives:</div>
<div>
<ul>
<li>Master news feed (every action carried out site-wide)</li>
<li>User news feed (every action carried out by individual users)</li>
<li>Instance news feed (every action carried out <em>to</em> an object)</li>
<li>Friend news feed (every action carried out by a user's friends/followed users)</li>
</ul>
<div>So in the above case, I insert the MD5 key into four Redis lists:</div>
<div>
<ul>
<li>the master news feed </li>
<li>User #1 (the user doing the rating)</li>
<li>Mixset #100 (the mixset being rated)</li>
<li>The friend feed of every user who follows User #1</li>
</ul>
<div>Retrieving any newsfeed is then a simple matter of querying for the first N entries in the relevant list, retrieving the corresponding JSON-encoded hashes from Redis and rendering some appropriate HTML or other output. </div>
<div> </div>
<div><span style="font-size: large;">The trade-offs</span></div>
</div>
</div>
<div> </div>
<div>In any real-life architecture, there are trade-offs made between speed, storage type and memory usage. In this case, memory and disk space is being compromised for the sake of speed of access. Retrieving any type of newsfeed is virtually free, which means that page loading speed will not be affected. Writes are still very fast, as there is no disk or SQL access involved. Although memory usage is relatively high, the minimal amount of data which is stored, and the use of MD5 keys to avoid unneccesary data replication, help to keep it within reason. Additionally, appropriate use of Redis' automatic key expiry settings, and a regular cronjob pruning of old lists, will help even further.</div>
<div> </div>
<div><span style="font-size: large;">Conclusion</span></div>
<p />
<div>Using Redis has allowed me to implement an elegant and efficient newsfeed functionality in a way that would have been difficult or impossible until recently. I can be confident that every newsfeed I retrieve will have no impact on performance. If you need to implement similar functionality then I recommend you consider an implementation using Redis.</div>
<div> </div>
<div>If you liked this post, then <a href="http://twitter.com/rfwatson"><strong>follow me on Twitter</strong></a>. You can also sign up as a Mixlr beta-tester <a href="http://mixlr.com">here</a>.</div>
<div> </div>
<div>Further reading: <a href="http://rfw.posterous.com/how-nodejs-saved-my-web-application">How I implemented Mixlr's backend using NodeJS</a>.</div>
<div> </div>
</div>
	
</p>

<p><a href="http://rfw.posterous.com/a-redis-powered-newsfeed-implementation">Permalink</a> 

	| <a href="http://rfw.posterous.com/a-redis-powered-newsfeed-implementation#comment">Leave a comment&nbsp;&nbsp;&raquo;</a>

</p>]]>
      </description>
      <posterous:author>
        <posterous:userImage>http://posterous.com/images/profile/missing-user-75.png</posterous:userImage>
        <posterous:profileUrl>http://posterous.com/users/3sIZjvZRR8A1</posterous:profileUrl>
        <posterous:firstName>Rob</posterous:firstName>
        <posterous:lastName>W</posterous:lastName>
        <posterous:nickName>robw</posterous:nickName>
        <posterous:displayName>Rob W</posterous:displayName>
      </posterous:author>
      <media:content type="image/png" height="101" width="769" url="http://getfile8.posterous.com/getfile/files.posterous.com/rfw/7RrZO3YsoWpIttZnbehiMczZI7HhyqtrbqcX7S40TyzSWY2TZ6fsT6VKaLyG/shot2.png">
        <media:thumbnail height="66" width="500" url="http://getfile9.posterous.com/getfile/files.posterous.com/rfw/W38T2XuIxz9TUOpcYaoDhPqjUetYT9fJ083547cBuVb6thHIjpxLaW2QEMsx/shot2.png.scaled.500.jpg" />
      </media:content>
      <media:content type="image/png" height="83" width="697" url="http://getfile6.posterous.com/getfile/files.posterous.com/rfw/2l3lF8twetTeoY0CV46wHJrg5xcx1XqU66wttZvjOTizdeUqKEEYfHVY4tkH/redis1.png">
        <media:thumbnail height="60" width="500" url="http://getfile7.posterous.com/getfile/files.posterous.com/rfw/2C6JaB3pCwi9h113IZYLlXpWXaoM0HvjLBCCgHct3PwujMpQZCjoWHCrINN6/redis1.png.scaled.500.jpg" />
      </media:content>
    </item>
    <item>
      <pubDate>Mon, 15 Mar 2010 14:03:00 -0700</pubDate>
      <title>Monit 101 (AKA system monitoring, and why you should care)</title>
      <link>http://rfw.posterous.com/monit-101-an-developers-guide-to-system-monit</link>
      <guid>http://rfw.posterous.com/monit-101-an-developers-guide-to-system-monit</guid>
      <description>
        <![CDATA[<p>
	<p>This article is a primer on using <a href="http://mmonit.com/monit/" target="_blank">monit</a> to monitor unix processes. Specifically it shows how monit can be used to improve the stability and availability of a Ruby on Rails web application. All of the techniques discussed would be equally applicable to monitoring almost any other type of Unix process.</p>
<p />
<div><span style="font-size: large;">Why could monit be useful for me?</span></div>
<p />
<div>You run a web application on Slicehost, Linode or a similar VPS. Perhaps you run your own dedicated server. You want to ensure that your application is available at all times. This means that both front and backend servers must be running constantly, and support processes (such as memcached, Sphinx, mysql, etc) must always be available.&nbsp;</div>
<p />
<div>These processes are system-critical. If any of them fail, your web application will partly or wholly stop functioning. In an e-commerce site that may mean significant loss of sales. In any website it's most definitely bad news. Monit can help to avoid it happening, and this article shows how.</div>
<p />
<div>The following is the desired outcome: In the event that any of the above processes crash or otherwise stop functioning, they should be automatically restarted by monit. In the worst case scenario, where monit can't restart one or more automatically, an email should be sent warning that manual intervention is required.</div>
<p />
<div>Furthermore, if for some reason the server is rebooted without your knowledge, monit should automatically re-launch each of the components required for full functionality. So if Slicehost ever reboots your slice in the middle of the night, it won't be 8 hours before anybody can use your website again.</div>
<p />
<div><span style="font-size: large;">Why not God?</span></div>
<p />
<div><a href="http://god.rubyforge.org/" target="_blank">God</a>&nbsp;is an open-source system-monitoring tool written in Ruby.&nbsp;</div>
<p />
<div>I have never used God, but I am familiar with the nature of the Ruby programming language. Ruby is an expressive and powerful language which is perfectly suited to the task of building certain classes of system utilities and web applications. It is also an integral component of the web application <a href="http://rfw.posterous.com/how-nodejs-saved-my-web-application" target="_blank">I am currently developing</a>&nbsp;.</div>
<p />
<div>Ruby does have certain drawbacks however. Specifically, the standard (MRI) installation tends to create memory leaks and bloat, especially in long-running Ruby processes. Of course this includes such important processes as web servers -- and even system monitoring tools themselves. This includes God.&nbsp;<a href="http://blog.bradgessler.com/use-monit-with-rails-not-god" target="_blank">Here</a> is an angry, but interesting, write-up of the problem which contains more than a grain of truth.</div>
<p />
<div>My recent experience suggests that deploying using the free&nbsp;<a href="http://www.rubyenterpriseedition.com/" target="_blank">Ruby Enterprise Edition</a>, significantly reduces the problem of memory leaks in long-running processes. However, apart from being the topic for a different blog post, this does not alter my conclusion that given a feasible non-Ruby alternative, avoiding the use of Ruby in a system-monitoring tool is a definite advantage.</div>
<p />
<div>This is precisely the train of thought which led me to evaluate monit.</div>
<p />
<div><span style="font-size: large;">What is monit?</span></div>
<p />
<div>Monit is an open-source utility for managing and monitoring processes, files, directories and more. It is written in C, and so neatly sidesteps the problem of Ruby's stability. Monit is capable of a lot more than I will cover here. You should note that I used the software for the first time only a few hours ago. While this means I am far from an expert, it also illustrates the simplicity of getting started.</div>
<p />
<div>I have sometimes heard about the supposed complexity of Monit configuration files. This put me off trying the software in the past. It is a myth. Monit configuration files are simple, and easy to create. There is nothing to fear.</div>
<p />
<div><span style="font-size: large;">How do I get started?</span></div>
<p />
<div>Download, compile and install monit. You may wish to carry this out on your server.</div>
<p />
<div>
<div><div class="data type-shell">
    
      <table class="lines" cellspacing="0" cellpadding="0">
        <tr>
          <td>
            <pre class="line_numbers"><span rel="#L1" id="L1">1</span>
<span rel="#L2" id="L2">2</span>
<span rel="#L3" id="L3">3</span>
<span rel="#L4" id="L4">4</span>
<span rel="#L5" id="L5">5</span>
<span rel="#L6" id="L6">6</span>
</pre>
          </td>
          <td width="100%">
            
              
                <div class="highlight"><pre /><div class="line" id="LC1">wget http://mmonit.com/monit/dist/monit-5.1.1.tar.gz</div><div class="line" id="LC2">tar xvfz monit-*</div><div class="line" id="LC3"><span class="nb">cd </span>monit-*</div><div class="line" id="LC4">./configure</div><div class="line" id="LC5">make</div><div class="line" id="LC6">make install</div></pre></div>
              
            
          </td>
        </tr>
      </table>
    
  </div></div>
</div>
<p />
<div>Here, I will illustrate how to monitor a full Ruby on Rails web application, including the frontend server (nginx), backend server (unicorn), Redis, a Node.js component, and a Resque message queue worker.</div>
<p />
<div>Create a monit configuration file in a suitable location. Inside it, add something like this:</div>
<p />
<div><div class="data type-shell">
    
      <table class="lines" cellspacing="0" cellpadding="0">
        <tr>
          <td>
            <pre class="line_numbers"><span rel="#L1" id="L1">1</span>
<span rel="#L2" id="L2">2</span>
<span rel="#L3" id="L3">3</span>
<span rel="#L4" id="L4">4</span>
<span rel="#L5" id="L5">5</span>
<span rel="#L6" id="L6">6</span>
<span rel="#L7" id="L7">7</span>
<span rel="#L8" id="L8">8</span>
<span rel="#L9" id="L9">9</span>
</pre>
          </td>
          <td width="100%">
            
              
                <div class="highlight"><pre /><div class="line" id="LC1"><span class="nb">set </span>daemon 30                     <span class="c"># with start delay 30</span></div><div class="line" id="LC2"><span class="nb">set </span>mailserver localhost          <span class="c"># sudo apt-get install postfix</span></div><div class="line" id="LC3"><span class="nb">set </span>mail-format <span class="o">{</span> from: monit@mydomain.com <span class="o">}</span></div><div class="line" id="LC4"><span class="nb">set </span>alert my@email.com</div><div class="line" id="LC5"><span class="nb">set </span>logfile /var/logs/monit.log</div><div class="line" id="LC6">&nbsp;</div><div class="line" id="LC7"><span class="nb">set </span>httpd port 6789 and           <span class="c"># web interface:</span></div><div class="line" id="LC8">&nbsp;&nbsp;&nbsp;&nbsp;allow admin:myspace1          <span class="c"># use HTTP basic auth </span></div><div class="line" id="LC9"><br /></div></pre></div>
              
            
          </td>
        </tr>
      </table>
    
  </div></div>
<p />
<div>The first line tells monit to check all processes every 30 seconds. The second line tells it to use the local mail server (I used the one-line postfix installation illustrated which worked perfectly).&nbsp;</div>
<p />
<div>Line four sets the location of the log file (tail it). Finally, lines 7-8 set up a web interface which allow you to remotely monitor and restart your server processes, <em>without</em>&nbsp;requiring an SSH connection:</div>
<p />
<div><div class='p_embed p_image_embed'>
<a href="http://posterous.com/getfile/files.posterous.com/rfw/e9tmxTc1Prj4wBTtJ06B0OH2wZOL1QPa99aLzQP7yEYFFWce01Hby3Rpk5Yk/Screen_shot_2010-03-15_at_14.4.png.scaled.1000.jpg"><img alt="Screen_shot_2010-03-15_at_14" height="254" src="http://posterous.com/getfile/files.posterous.com/rfw/hO6uYp1a8qqPeGTDaVpvhEsUDBDBFoiZvGIdTDHN7UUIEHizlDumSv3l8qBq/Screen_shot_2010-03-15_at_14.4.png.scaled.500.jpg" width="500" /></a>
</div>
</div>
<div>Useful, huh?</div>
<p />
<div>Important note: The following is not a one-size fits-all recipe for every web server. Copy-and-pasting without examining and adapting for your own circumstances, is unlikely to work well, if at all. The examples may, however, be useful as pointers. I adapted them from <a href="http://www.igvita.com/2006/11/07/monit-makes-mongrel-play-nice/">this</a> old, but useful article.</div>
<p />
<div><span style="font-size: large;">Nginx</span></div>
<p />
<div>First of all let's monitor our front-end web server. In this case it's Nginx, which is usually reasonably stable, but it could just as well be Apache which can be slightly less so. Add the following to your monit.conf:</div>
<p />
<div><div class="data type-shell">
    
      <table class="lines" cellspacing="0" cellpadding="0">
        <tr>
          <td>
            <pre class="line_numbers"><span rel="#L1" id="L1">1</span>
<span rel="#L2" id="L2">2</span>
<span rel="#L3" id="L3">3</span>
<span rel="#L4" id="L4">4</span>
<span rel="#L5" id="L5">5</span>
<span rel="#L6" id="L6">6</span>
<span rel="#L7" id="L7">7</span>
<span rel="#L8" id="L8">8</span>
<span rel="#L9" id="L9">9</span>
<span rel="#L10" id="L10">10</span>
<span rel="#L11" id="L11">11</span>
<span rel="#L12" id="L12">12</span>
<span rel="#L13" id="L13">13</span>
<span rel="#L14" id="L14">14</span>
<span rel="#L15" id="L15">15</span>
<span rel="#L16" id="L16">16</span>
</pre>
          </td>
          <td width="100%">
            
              
                <div class="highlight"><pre /><div class="line" id="LC1"><span class="c">#### nginx ####</span></div><div class="line" id="LC2">check process nginx with pidfile /tmp/nginx.pid</div><div class="line" id="LC3">&nbsp;&nbsp;&nbsp;&nbsp;start <span class="nv">program</span> <span class="o">=</span> <span class="s2">&quot;/etc/init.d/nginx start&quot;</span></div><div class="line" id="LC4">&nbsp;&nbsp;&nbsp;&nbsp;stop <span class="nv">program</span> <span class="o">=</span> <span class="s2">&quot;/etc/init.d/nginx stop&quot;</span></div><div class="line" id="LC5">&nbsp;</div><div class="line" id="LC6">&nbsp;&nbsp;&nbsp;&nbsp;<span class="k">if </span>totalmem is greater than 100 Mb <span class="k">for </span>5 cycles <span class="k">then </span>alert          </div><div class="line" id="LC7">&nbsp;&nbsp;&nbsp;&nbsp;<span class="k">if </span>cpu is greater than 50% <span class="k">for </span>2 cycles <span class="k">then </span>alert                  </div><div class="line" id="LC8">&nbsp;&nbsp;&nbsp;&nbsp;<span class="k">if </span>cpu is greater than 80% <span class="k">for </span>3 cycles <span class="k">then </span>restart                </div><div class="line" id="LC9">&nbsp;&nbsp;&nbsp;&nbsp;<span class="k">if </span>loadavg<span class="o">(</span>5min<span class="o">)</span> greater than 10 <span class="k">for </span>8 cycles <span class="k">then </span>restart         </div><div class="line" id="LC10">&nbsp;&nbsp;&nbsp;&nbsp;<span class="k">if </span>3 restarts within 20 cycles <span class="k">then </span>timeout                        </div><div class="line" id="LC11">&nbsp;</div><div class="line" id="LC12">&nbsp;&nbsp;&nbsp;&nbsp;<span class="k">if </span>failed port 80 protocol http and request <span class="s1">&#39;/ping.txt&#39;</span>                  <span class="c"># check for response</span></div><div class="line" id="LC13">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;with timeout 20 seconds</div><div class="line" id="LC14">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="k">then </span>restart</div><div class="line" id="LC15">&nbsp;&nbsp;&nbsp;&nbsp;group nginx</div><div class="line" id="LC16"><br /></div></pre></div>
              
            
          </td>
        </tr>
      </table>
    
  </div></div>
<p />
<div>You will need to gsub the location of your nginx pidfile, and the commands to start and stop nginx on your server. As well as checking the process is running, I have monit attempt to download an empty text file <span style="font-family: courier new, monospace;">ping.txt</span> in the root html directory. If this file is unavailable then nginx will be instantly restarted.</div>
<p />
<div><span style="font-size: large;">Unicorn</span></div>
<p />
<div>Unicorn is a <a href="http://tomayko.com/writings/unicorn-is-unix">pre-forking unix http server</a> which can be easily configured to serve Ruby on Rails applications. The following configuration could be readily adapted to monitor a cluster of Mongrel or thin servers. Here is the monit.conf snippet:</div>
<p />
<div><div class="data type-shell">
    
      <table class="lines" cellspacing="0" cellpadding="0">
        <tr>
          <td>
            <pre class="line_numbers"><span rel="#L1" id="L1">1</span>
<span rel="#L2" id="L2">2</span>
<span rel="#L3" id="L3">3</span>
<span rel="#L4" id="L4">4</span>
<span rel="#L5" id="L5">5</span>
<span rel="#L6" id="L6">6</span>
<span rel="#L7" id="L7">7</span>
<span rel="#L8" id="L8">8</span>
<span rel="#L9" id="L9">9</span>
<span rel="#L10" id="L10">10</span>
<span rel="#L11" id="L11">11</span>
<span rel="#L12" id="L12">12</span>
<span rel="#L13" id="L13">13</span>
<span rel="#L14" id="L14">14</span>
<span rel="#L15" id="L15">15</span>
</pre>
          </td>
          <td width="100%">
            
              
                <div class="highlight"><pre /><div class="line" id="LC1"><span class="c">##### unicorn #####</span></div><div class="line" id="LC2">check process unicorn_rails with pidfile /var/tmp/unicorn.pid</div><div class="line" id="LC3">&nbsp;&nbsp;&nbsp;&nbsp;start <span class="nv">program</span> <span class="o">=</span> <span class="s2">&quot;/usr/local/bin/unicorn_rails -D -E production -c /etc/unicorn.rb&quot;</span> as uid deploy and gid deploy</div><div class="line" id="LC4">&nbsp;&nbsp;&nbsp;&nbsp;stop <span class="nv">program</span>  <span class="o">=</span> <span class="s2">&quot;/usr/bin/killall -9 unicorn_rails&quot;</span> as uid deploy and gid deploy</div><div class="line" id="LC5">&nbsp;</div><div class="line" id="LC6">&nbsp;&nbsp;&nbsp;&nbsp;<span class="k">if </span>totalmem is greater than 300 Mb <span class="k">for </span>5 cycles <span class="k">then </span>alert          </div><div class="line" id="LC7">&nbsp;&nbsp;&nbsp;&nbsp;<span class="k">if </span>cpu is greater than 50% <span class="k">for </span>2 cycles <span class="k">then </span>alert                  </div><div class="line" id="LC8">&nbsp;&nbsp;&nbsp;&nbsp;<span class="k">if </span>cpu is greater than 80% <span class="k">for </span>3 cycles <span class="k">then </span>restart                </div><div class="line" id="LC9">&nbsp;&nbsp;&nbsp;&nbsp;<span class="k">if </span>loadavg<span class="o">(</span>5min<span class="o">)</span> greater than 10 <span class="k">for </span>8 cycles <span class="k">then </span>restart        </div><div class="line" id="LC10">&nbsp;&nbsp;&nbsp;&nbsp;<span class="k">if </span>5 restarts within 20 cycles <span class="k">then </span>timeout                       </div><div class="line" id="LC11">&nbsp;</div><div class="line" id="LC12">&nbsp;&nbsp;&nbsp;&nbsp;<span class="k">if </span>failed port 7070 protocol http and request /ping_rails                   </div><div class="line" id="LC13">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;with timeout 20 seconds</div><div class="line" id="LC14">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="k">then </span>restart</div><div class="line" id="LC15">&nbsp;&nbsp;&nbsp;&nbsp;group unicorn</div></pre></div>
              
            
          </td>
        </tr>
      </table>
    
  </div></div>
<p />
<div>Nginx needs to be run as root, but I don't want to run my Rails apps with superuser priveleges. The <span style="font-family: courier new, monospace;">as uid deploy and gid deploy</span><span style="font-family: arial, helvetica, sans-serif;">&nbsp;commands ensure that they are launched as user:group deploy:deploy.</span></div>
<p />
<div><span style="font-family: arial, helvetica, sans-serif;">I have Unicorn configured to serve through a unix socket, but it also listens on port 7070. That means that hitting <a href="http://mydomain.com:7070">mydomain.com:7070</a> bypasses Nginx, and serves the Rails app directly from Unicorn. This is useful for testing: I configured a simple <a href="http://weblog.rubyonrails.org/2008/12/17/introducing-rails-metal">metal</a>&nbsp;action to serve an empty 200 response to the route </span><span style="font-family: courier new, monospace;">/ping_rails</span><span style="font-family: arial, helvetica, sans-serif;">. If this action fails for any reason, Unicorn will be restarted immediately, and I will be emailed.</span></div>
<p />
<div><span style="font-family: arial, helvetica, sans-serif;"><span style="font-size: large;">Redis</span></span></div>
<p />
<div><span style="font-family: arial, helvetica, sans-serif;">Redis is a persistent key-value store which while very stable is also integral to the functionality of my application. The setup to monitor Redis is simpler:</span></div>
<p />
<div><span style="font-family: arial, helvetica, sans-serif;"><div class="data type-text">
    
      <table class="lines" cellspacing="0" cellpadding="0">
        <tr>
          <td>
            <pre class="line_numbers"><span rel="#L1" id="L1">1</span>
<span rel="#L2" id="L2">2</span>
<span rel="#L3" id="L3">3</span>
<span rel="#L4" id="L4">4</span>
<span rel="#L5" id="L5">5</span>
</pre>
          </td>
          <td width="100%">
            
              
                <div class="highlight"><pre /><div class="line" id="LC1">check process redis-server with pidfile /var/tmp/redis.pid</div><div class="line" id="LC2">&nbsp;&nbsp;&nbsp;&nbsp;start program = &quot;/home/deploy/src/redis-1.2.0/redis-server /home/deploy/redis.conf&quot;</div><div class="line" id="LC3">&nbsp;&nbsp;&nbsp;&nbsp;stop program = &quot;/usr/bin/killall -9 redis-server&quot;</div><div class="line" id="LC4">&nbsp;&nbsp;&nbsp;&nbsp;group redis</div><div class="line" id="LC5"><br /></div></pre></div>
              
            
          </td>
        </tr>
      </table>
    
  </div></span></div>
<p />
<div><span style="font-family: arial, helvetica, sans-serif;"><span style="font-size: large;">Node.js</span></span></div>
<p />
<div><span style="font-family: arial, helvetica, sans-serif;">Node.js handles uploading and processing uploaded files, an integral part of my web application. This was the trickiest configuration to get working. I found the easiest way to get a pidfile was to write it from within the node.js script itself:</span></div>
<p />
<div><span style="font-family: arial, helvetica, sans-serif;"><div class="data type-javascript">
    
      <table class="lines" cellspacing="0" cellpadding="0">
        <tr>
          <td>
            <pre class="line_numbers"><span rel="#L1" id="L1">1</span>
<span rel="#L2" id="L2">2</span>
<span rel="#L3" id="L3">3</span>
<span rel="#L4" id="L4">4</span>
</pre>
          </td>
          <td width="100%">
            
              
                <div class="highlight"><pre /><div class="line" id="LC1"><span class="c1">// At the start of my node.js script:</span></div><div class="line" id="LC2"><span class="kd">var</span> <span class="nx">pidfile</span> <span class="o">=</span> <span class="nx">fs</span><span class="p">.</span><span class="nx">openSync</span><span class="p">(</span><span class="s2">&quot;/var/tmp/node.pid&quot;</span><span class="p">,</span> <span class="s2">&quot;w&quot;</span><span class="p">);</span></div><div class="line" id="LC3"><span class="nx">fs</span><span class="p">.</span><span class="nx">writeSync</span><span class="p">(</span><span class="nx">pidfile</span><span class="p">,</span> <span class="nx">process</span><span class="p">.</span><span class="nx">pid</span> <span class="o">+</span> <span class="s2">&quot;&quot;</span><span class="p">);</span></div><div class="line" id="LC4"><span class="nx">fs</span><span class="p">.</span><span class="nx">closeSync</span><span class="p">(</span><span class="nx">pidfile</span><span class="p">);</span></div></pre></div>
              
            
          </td>
        </tr>
      </table>
    
  </div></span></div>
<p />
<div><span style="font-family: arial, helvetica, sans-serif;">Then add the following to your monit.conf:</span></div>
<p />
<div><span style="font-family: arial, helvetica, sans-serif;"><div class="data type-shell">
    
      <table class="lines" cellspacing="0" cellpadding="0">
        <tr>
          <td>
            <pre class="line_numbers"><span rel="#L1" id="L1">1</span>
<span rel="#L2" id="L2">2</span>
<span rel="#L3" id="L3">3</span>
<span rel="#L4" id="L4">4</span>
<span rel="#L5" id="L5">5</span>
<span rel="#L6" id="L6">6</span>
<span rel="#L7" id="L7">7</span>
<span rel="#L8" id="L8">8</span>
<span rel="#L9" id="L9">9</span>
<span rel="#L10" id="L10">10</span>
<span rel="#L11" id="L11">11</span>
<span rel="#L12" id="L12">12</span>
<span rel="#L13" id="L13">13</span>
<span rel="#L14" id="L14">14</span>
<span rel="#L15" id="L15">15</span>
</pre>
          </td>
          <td width="100%">
            
              
                <div class="highlight"><pre /><div class="line" id="LC1">check process node with pidfile /var/tmp/node.pid</div><div class="line" id="LC2">&nbsp;&nbsp;&nbsp;&nbsp;start <span class="nv">program</span> <span class="o">=</span> <span class="s2">&quot;/bin/sh /home/deploy/start-node&quot;</span></div><div class="line" id="LC3">&nbsp;&nbsp;&nbsp;&nbsp;stop <span class="nv">program</span> <span class="o">=</span> <span class="s2">&quot;/usr/bin/killall -9 node&quot;</span></div><div class="line" id="LC4">&nbsp;</div><div class="line" id="LC5">&nbsp;&nbsp;&nbsp;&nbsp;<span class="k">if </span>totalmem is greater than 200 Mb <span class="k">for </span>5 cycles <span class="k">then </span>alert          </div><div class="line" id="LC6">&nbsp;&nbsp;&nbsp;&nbsp;<span class="k">if </span>cpu is greater than 50% <span class="k">for </span>2 cycles <span class="k">then </span>alert                   </div><div class="line" id="LC7">&nbsp;&nbsp;&nbsp;&nbsp;<span class="k">if </span>cpu is greater than 80% <span class="k">for </span>3 cycles <span class="k">then </span>restart                </div><div class="line" id="LC8">&nbsp;&nbsp;&nbsp;&nbsp;<span class="k">if </span>loadavg<span class="o">(</span>5min<span class="o">)</span> greater than 10 <span class="k">for </span>8 cycles <span class="k">then </span>restart         </div><div class="line" id="LC9">&nbsp;&nbsp;&nbsp;&nbsp;<span class="k">if </span>3 restarts within 30 cycles <span class="k">then </span>timeout                        </div><div class="line" id="LC10">&nbsp;</div><div class="line" id="LC11">&nbsp;&nbsp;&nbsp;&nbsp;<span class="k">if </span>failed port 81 protocol http                   </div><div class="line" id="LC12">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;with timeout 20 seconds</div><div class="line" id="LC13">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="k">then </span>restart</div><div class="line" id="LC14">&nbsp;&nbsp;&nbsp;&nbsp;group node</div><div class="line" id="LC15"><br /></div></pre></div>
              
            
          </td>
        </tr>
      </table>
    
  </div></span></div>
<p />
<div><span style="font-family: arial, helvetica, sans-serif;">I was unable to get monit to launch node directly, but wrapping it in a simple bash script which set the current working directly correctly worked fine.</span></div>
<p />
<div><span style="font-family: arial, helvetica, sans-serif;"><span style="font-size: large;">Resque</span></span></div>
<p />
<div><span style="font-family: arial, helvetica, sans-serif;">Resque is a Rails background queue worker which uses Redis to run Rails actions in the background, detached from the HTTP request cycle. It is important to the functioning of my website (for example, it is responsible for sending all outbound emails). Again, setting up monit was a little tricky. Resque is launched via a rake task, but I needed to somehow write a pidfile for monit to use. When I wrapped it in a simple Ruby script which wrote a pidfile and launched the rake task, I found that rake was launched in a different process to the Ruby script, meaning that monit couldn't see that Resque had launched successfully, and kept needlessly trying to restart it.</span></div>
<p />
<div><span style="font-family: arial, helvetica, sans-serif;">This seemed to be solved by using the backtick form of shelling out a command from a Ruby script. Here is my start-resque script:</span></div>
<p />
<div><span style="font-family: arial, helvetica, sans-serif;"><div class="data type-ruby">
    
      <table class="lines" cellspacing="0" cellpadding="0">
        <tr>
          <td>
            <pre class="line_numbers"><span rel="#L1" id="L1">1</span>
<span rel="#L2" id="L2">2</span>
<span rel="#L3" id="L3">3</span>
<span rel="#L4" id="L4">4</span>
<span rel="#L5" id="L5">5</span>
<span rel="#L6" id="L6">6</span>
<span rel="#L7" id="L7">7</span>
</pre>
          </td>
          <td width="100%">
            
              
                <div class="highlight"><pre /><div class="line" id="LC1"><span class="c1">#!/usr/bin/env ruby</span></div><div class="line" id="LC2"><br /></div><div class="line" id="LC3"><span class="no">File</span><span class="o">.</span><span class="n">open</span><span class="p">(</span><span class="s2">&quot;/var/tmp/resque.pid&quot;</span><span class="p">,</span> <span class="s2">&quot;w&quot;</span><span class="p">)</span> <span class="k">do</span> <span class="o">|</span><span class="n">f</span><span class="o">|</span></div><div class="line" id="LC4">&nbsp;&nbsp;<span class="n">f</span> <span class="o">&lt;&lt;</span> <span class="no">Process</span><span class="o">.</span><span class="n">pid</span><span class="o">.</span><span class="n">to_s</span></div><div class="line" id="LC5"><span class="k">end</span></div><div class="line" id="LC6"><span class="sb">`cd /home/deploy/public_html/myapp/current &amp;&amp; RAILS_ENV=production VERBOSE=1 QUEUE=* rake environment resque:work`</span></div><div class="line" id="LC7"><br /></div></pre></div>
              
            
          </td>
        </tr>
      </table>
    
  </div></span></div>
<p />
<div><span style="font-family: arial, helvetica, sans-serif;">And then the monit.conf snippet:</span></div>
<p />
<div><span style="font-family: arial, helvetica, sans-serif;"><div class="data type-shell">
    
      <table class="lines" cellspacing="0" cellpadding="0">
        <tr>
          <td>
            <pre class="line_numbers"><span rel="#L1" id="L1">1</span>
<span rel="#L2" id="L2">2</span>
<span rel="#L3" id="L3">3</span>
<span rel="#L4" id="L4">4</span>
<span rel="#L5" id="L5">5</span>
</pre>
          </td>
          <td width="100%">
            
              
                <div class="highlight"><pre /><div class="line" id="LC1">check process resque with pidfile /var/tmp/resque.pid</div><div class="line" id="LC2">&nbsp;&nbsp;&nbsp;&nbsp;start <span class="nv">program</span> <span class="o">=</span> <span class="s2">&quot;/home/deploy/myapp/start-resque&quot;</span> as uid deploy and gid deploy</div><div class="line" id="LC3">&nbsp;&nbsp;&nbsp;&nbsp;stop <span class="nv">program</span> <span class="o">=</span> <span class="s2">&quot;/bin/kill -9 `cat /var/tmp/resque.pid`&quot;</span></div><div class="line" id="LC4">&nbsp;&nbsp;&nbsp;&nbsp;group redis</div><div class="line" id="LC5"><br /></div></pre></div>
              
            
          </td>
        </tr>
      </table>
    
  </div></span></div>
<p />
<div><span style="font-family: arial, helvetica, sans-serif;"><span style="font-size: large;">Ready? Go</span></span></div>
<p />
<div><span style="font-family: arial, helvetica, sans-serif;">You can launch monit at any time with &nbsp;</span><span style="font-family: courier new, monospace;">monit -c /your/monit.conf</span><span style="font-family: arial, helvetica, sans-serif;">. Tail the log that you set up in the first step and try killing nginx. If monit is working, you will watch it automatically restart. Don't forget to check your inbox, and check out the web interface on <a href="http://yourdomain.com:6789">yourdomain.com:6789</a>.</span></div>
<p />
<div><span style="font-family: arial, helvetica, sans-serif;"><span style="font-size: large;">Launching monit on start-up</span></span></div>
<p />
<div><span style="font-family: arial, helvetica, sans-serif;">An integral part of this strategy is for monit to start automatically if my server is rebooted. Once it starts, it will automatically nudge all of the above processes into action and effectively boot your entire application.</span></div>
<p />
<div><span style="font-family: arial, helvetica, sans-serif;">To achieve this I added the following line to </span><span style="font-family: courier new, monospace;">/etc/crontab</span><span style="font-family: arial, helvetica, sans-serif;">:</span></div>
<p />
<div><span style="font-family: arial, helvetica, sans-serif;"><div class="data type-shell">
    
      <table class="lines" cellspacing="0" cellpadding="0">
        <tr>
          <td>
            <pre class="line_numbers"><span rel="#L1" id="L1">1</span>
</pre>
          </td>
          <td width="100%">
            
              
                <div class="highlight"><pre /><div class="line" id="LC1">@reboot root /usr/local/bin/monit -c /path/to/my/monit.conf</div></pre></div>
              
            
          </td>
        </tr>
      </table>
    
  </div></span></div>
<p />
<div><span style="font-family: arial, helvetica, sans-serif;"><span style="font-size: large;">Conclusions</span></span></div>
<p />
<div><span style="font-family: arial, helvetica, sans-serif;">I have not yet deployed monit into a production environment but I am confident of its prospects.&nbsp;</span></div>
<p />
<div><span style="font-family: arial, helvetica, sans-serif;">Contrary to my previous misconception, monit is easy to install and configure. And being written in C it stands a good chance of standing up to the strain of a heavyweight server environment.&nbsp;</span></div>
<p />
<div><span style="font-family: arial, helvetica, sans-serif;">Running a monitoring tool such as this is essential for any kind of mission-critical web application, and I am only surprised that I have not delved into monit until now. If you need to ensure the availability of any kind of unix system process, then I recommend you consider using monit.</span></div>
<p />
<p />
<div><span style="font-family: arial, helvetica, sans-serif;">Follow <strong><a href="http://twitter.com/rfwatson">@rfwatson on Twitter</a></strong></span></div>
	
</p>

<p><a href="http://rfw.posterous.com/monit-101-an-developers-guide-to-system-monit">Permalink</a> 

	| <a href="http://rfw.posterous.com/monit-101-an-developers-guide-to-system-monit#comment">Leave a comment&nbsp;&nbsp;&raquo;</a>

</p>]]>
      </description>
      <posterous:author>
        <posterous:userImage>http://posterous.com/images/profile/missing-user-75.png</posterous:userImage>
        <posterous:profileUrl>http://posterous.com/users/3sIZjvZRR8A1</posterous:profileUrl>
        <posterous:firstName>Rob</posterous:firstName>
        <posterous:lastName>W</posterous:lastName>
        <posterous:nickName>robw</posterous:nickName>
        <posterous:displayName>Rob W</posterous:displayName>
      </posterous:author>
      <media:content type="image/png" height="551" width="1083" url="http://getfile5.posterous.com/getfile/files.posterous.com/rfw/WlTyLerT4FO0p51X4Rf4t94982WWIL8egruxujh2Y358b2wjt1UiZmlqFWll/Screen_shot_2010-03-15_at_14.4.png">
        <media:thumbnail height="254" width="500" url="http://getfile6.posterous.com/getfile/files.posterous.com/rfw/hO6uYp1a8qqPeGTDaVpvhEsUDBDBFoiZvGIdTDHN7UUIEHizlDumSv3l8qBq/Screen_shot_2010-03-15_at_14.4.png.scaled.500.jpg" />
      </media:content>
    </item>
    <item>
      <pubDate>Sun, 28 Feb 2010 12:39:00 -0800</pubDate>
      <title>How NodeJS saved my web application</title>
      <link>http://rfw.posterous.com/how-nodejs-saved-my-web-application</link>
      <guid>http://rfw.posterous.com/how-nodejs-saved-my-web-application</guid>
      <description>
        <![CDATA[<p>
	<p>Recently I've been working on a new web application. One of the features allows users to upload audio files to the application. This is the story about how tackling an unforeseen bug led to a massive improvement in the application architecture, and an appreciation of a whole new approach to building web-apps.</p>
<p>First of all, let's get clear what's going on in this particular feature. The flow looks something like this:</p>
<div><div class='p_embed p_image_embed'>
<a href="http://posterous.com/getfile/files.posterous.com/rfw/aUKSgMsA6aUpTh1jMgaIqAjK6zCEcC5ilqabJ72v6BoVgkm1Us3yAjebL1CR/zap1_2.png"><img alt="Zap1_2" height="143" src="http://posterous.com/getfile/files.posterous.com/rfw/Dd0Dhh8IucDr4AT7ElN0OuHdRM7dH0QIXsBJzxXHH4YZjTd3LSYLpJcWtcYs/zap1_2.png.scaled.500.jpg" width="500" /></a>
</div>
<br />
<div>It's pretty straightforward: the user selects a file, and while they're filling in some additional details and watching a progress meter, the file is being uploaded, processed on the application server and then saved to Amazon S3. Once all the boxes are ticked, everybody can move on.</div>
<p />
<div>Now a little bit of background. I've got previous in building websites similar to this one. Last year I launched&nbsp;<a href="http://www.donkdj.com">DonkDJ.com</a>, which revolved around almost the same principle. The user chooses a file to the DonkDJ app servers: then, while they watched a progress meter and (occasionally) clicked on some ads, the song was uploaded, <a href="http://developer.echonest.com/">rhythmically analyzed</a>, mutilated usually beyond recognition and returned to the user.</div>
<p />
<div>At the time, I used what was probably the latest web technologies to achieve this. I used Starling as a message queue, and a batch of Workling worker processes which handled the processing jobs as they appeared on the queue. Basically, the server-side flow went something like this (I've missed out the UI part this time):</div>
<div><div class='p_embed p_image_embed'>
<a href="http://posterous.com/getfile/files.posterous.com/rfw/3hrk6ZOJT8j6uMzPKpof64SUFsMf10tuMn7BWMXwuOHUnSKMi9CnunlIqrnI/zap2_2.png"><img alt="Zap2_2" height="107" src="http://posterous.com/getfile/files.posterous.com/rfw/X6m4BBYEXrvQIARbJbSuFokh2ySss8hHIAQd1ErFm8XdrD93wtr5ken68uY9/zap2_2.png.scaled.500.jpg" width="500" /></a>
</div>
</div>
<div>Now there's nothing fundamentally wrong with this approach, and it certainly worked. DonkDJ turned out to be a <a href="http://www.nme.com/blog/index.php?blog=15&amp;p=6059&amp;more=1&amp;c=1">viral media smash hit</a>, and within 24 hours of launch it was processing up to 5,000 uploads per day, without ever really falling over. Not bad for a 1Gb Slicehost server.&nbsp;</div>
<p />
<div>There are, however, three big disadvantages to this setup:</div>
<div>
<ul>
<li>It should be clear from the diagram above that architecture is serial. First of all, the complete file has to be uploaded to the server. Then (assuming a worker process is available immediately) the file is processed. Then after it's processed, it's transferred to S3. Finally, the UI can be informed that we're ready to move on. This isn't necessarily the most efficient way to work. What if some of the processing tasks could take place in parallel? That could mean a much shorter wait, a better user experience and quite possibly a resulting higher conversion or repeat-visit rate.</li>
<li>The communication between different threads (one handling the UI, the other processing the file) was complicated. This wasn't helped by the fact that while making a request from the UI to the server was just a simple AJAX request, it was difficult-to-impossible to send a message in the other direction, from the server thread to the UI. This meant that constant polling, and a lot of SQL attribute-juggling, was necessary to make the different sides communicate properly. Far from elegant.</li>
<li>The design of Workling meant that the entire Rails environment had to be loaded into memory each time the application needed a new worker. Busy periods required upwards of 8 worker processes to keep up, plus 2-4 extra Rails processes to serve the website itself. Running 10-12 Rails processes on a 1Gb server is no laughing matter, especially when most of them are also processing large binary files.</li>
</ul>
<div>Now web technology changes a lot in a year, but when I started work on my new project (which requires, as you'll recall, a broadly similar architecture), I was naturally inclined to go with the experience I already had. After all, it had scaled successfully to a relatively high load, and I can see nothing in principle which would stop it scaling further. Just create some EC2 or Slicehost instances with more worker processes and employ nginx or haproxy as a load balancer. And so off I went, building my application, and having quite a lot of fun.</div>
<p />
<div>The problem surfaced deep into the development process. As you can see in the first diagram, this application (unlike DonkDJ) asks the user to fill in an additional details form while he or she are waiting for their upload to be processed.</div>
<p />
<div>Paperclip (the Rails plugin I used in both applications to handle uploaded files) prides itself on <a href="http://github.com/thoughtbot/paperclip">handling file attachments as normal ActiveRecord attributes</a>. In other words, you can do something very roughly like this:</div>
<p />
<div><div class="data type-ruby">
    
      <table class="lines" cellspacing="0" cellpadding="0">
        <tr>
          <td>
            <pre class="line_numbers"><span rel="#L1" id="L1">1</span>
<span rel="#L2" id="L2">2</span>
<span rel="#L3" id="L3">3</span>
<span rel="#L4" id="L4">4</span>
</pre>
          </td>
          <td width="100%">
            
              
                <div class="highlight"><pre /><div class="line" id="LC1"><span class="vi">@song</span> <span class="o">=</span> <span class="no">Song</span><span class="o">.</span><span class="n">new</span></div><div class="line" id="LC2"><span class="n">file</span> <span class="o">=</span> <span class="no">File</span><span class="o">.</span><span class="n">open</span><span class="p">(</span><span class="s2">&quot;/some/song.mp3&quot;</span><span class="p">)</span></div><div class="line" id="LC3"><span class="vi">@song</span><span class="o">.</span><span class="n">attachment</span> <span class="o">=</span> <span class="n">file</span></div><div class="line" id="LC4"><span class="vi">@song</span><span class="o">.</span><span class="n">save!</span></div></pre></div>
              
            
          </td>
        </tr>
      </table>
    
  </div></div>
<p />
<div>This means that the attachment is processed and saved, all during the ActiveRecord callback cycle. Because Rails opens an SQL transaction every time an instance is saved, this means that I was faced with a flow that looked something like this:</div>
<div><div class='p_embed p_image_embed'>
<a href="http://posterous.com/getfile/files.posterous.com/rfw/m0FnQjvZ1UozvKgVsAKKi1TeSltU8f5DT9fpVeX9lEask4iSxHpSsHBWi57a/zap3.png"><img alt="Zap3" height="129" src="http://posterous.com/getfile/files.posterous.com/rfw/PW2BgaEfiWs8tJEfJkjLtHgSSbqoa4y3i2ljKu0kcU7Chhqe173c0mNTRPAZ/zap3.png.scaled.500.jpg" width="500" /></a>
</div>
</div>
<div>In other words, when the user tries to submit their "additional details" form, then unless file processing is complete then MySQL is unable to save their information -- the database row is locked! Worse, there's no easily catchable exception: MySQL just hangs for 30 seconds and then times out. All this time, the user has a spinny AJAX wheel to stare at. This was a big, big problem.</div>
<p />
<div>So I weighed up my options. As I saw it, they were:</div>
<div>
<ul>
<li>Use an MyISAM table instead of InnoDB. MyISAM tables don't support transactions, and so Rails (presumably) wouldn't lock the row and the problem would be solved. Not really ideal though: I didn't have any need for super-blazing SQL speed, and I didn't feel inclined to sacrifice the benefits of InnoDB for the sake of a Rails plugin. <strong>Update: </strong>See janl's comment below.</li>
<li>Hack Rails so that it didn't open a transaction on this particular save. Really, really far from being either elegant or ideal.</li>
<li>Alter the UI so that the user wasn't able to save their "additional details" form until their upload had been completed. The easiest option but again, it didn't seem right to sacrifice so much flexibility because of the possibly <span style="text-decoration: line-through;">bad</span>&nbsp;unsuitable design of a Rails plugin.</li>
<li>Create a proxy class that was instantiated in place of ActiveRecord::Base if the database was locked. Upon saving, the data would be saved not to the database, but to Redis, which of course is not affected by SQL locks. Upon being processed, the worker process would fetch the additional information from Redis and save it to the database. Yes, I really did implement this, but please: <strong>nobody should have to go to this much trouble to implement concurrency in a web application.</strong> </li>
</ul>
<div>At this point I remembered about <a href="http://www.nodejs.org">nodejs</a>. I wasn't totally sure what it was about, but I needed to solve this concurrency problem, and I was looking for a good solution to implement an upload progress bar (hopefully) without using Flash. Why not take a look, I thought.&nbsp;</div>
<p />
<div>It turns out that nodejs is phenomenally useful. It is, in short, a lightweight implementation of server-side Javascript which is perfectly suited to handling file uploads and background processing. Others have done <a href="http://simonwillison.net/2009/Nov/23/node/">fine jobs</a> of describing its potential, but I will simply emphasise that its architecture is designed to be event-driven, rather than threaded. Just like programming javascript in the browser, that means that it is perfectly suited to designing a system which reacts to events as they happen, instead of waiting in a queue to fetch a job.</div>
<p />
<div>Over this weekend, I've totally re-written the backend of my project using nodejs. This is the new flow:</div>
<div><div class='p_embed p_image_embed'>
<a href="http://posterous.com/getfile/files.posterous.com/rfw/FHWtSHThn14DQMwYtexoKvcMsuVB3T38metchTN49BHcws72ec3IQNdNpmPr/zap4.png"><img alt="Zap4" height="121" src="http://posterous.com/getfile/files.posterous.com/rfw/QV7RLXhjPjUBi4OX4DV17AfRlHOmZ4Afr2aCGxIHRRxqG1l1gLKsoumiWVrZ/zap4.png.scaled.500.jpg" width="500" /></a>
</div>
</div>
<div>Here are the advantages:</div>
<div>
<div>
<div>
<ul>
<li>Whereas in DonkDJ I was using a Flash plugin to handle the file upload, using NodeJS means that I can get my hands on the data as it arrives, chunk-by-chunk. This means that simply by piping the data I receive into the file-system and back, I can process and transcode the audio as it arrives on the server. This means much quicker processing times and happier users.</li>
<li>The only reason I can't also simultaneously upload to S3 is that Amazon requires an accurate "Content-Length" header before it starts uploading. Because transcoding changes the size of the file, I have no way of knowing this value until the processing stage is complete. However: what if for some reason the file doesn't need processing? Perhaps it's already compressed enough, or is too small to worry about bandwidth. In this case, I could use the "Content-Length" header of the original request to upload to S3 while simultaneously uploading to the server. (Alternatively, send a message back to the browser and ask it to upload to S3 directly using a Flash object, therefore saving the bandwidth cost, and time, of sending the file twice.)</li>
<li>NodeJS is handsomely lightweight, handles lots of simultaneous jobs well, and doesn't load the Rails environment at all. Instead, I use Redis to communicate between Rails and the backend processes. This means that I will typically only need a couple of instances of Rails loaded, which is enough to serve a quite busy website if there's no background processing involved.</li>
<li>I can remove Paperclip, Resque and all other background processing tasks from my Rails&nbsp;application entirely. Rails now handles serving dynamic web pages and nothing else. This is clearly the way it ought to be.</li>
</ul>
<div>On top of the advantages of NodeJS, it is also perfectly suited to using <a href="http://rfw.posterous.com/musictweets-a-rediswebsocket-powered-experime">Websockets</a>, which I've employed to allow bi-directional communication between the worker processes and the browser. Because both server and client are using event-driven Javascript, this makes it wonderfully easy to implement features like upload progress bars and dynamic status messages, which in DonkDJ involved messy hacks, Rails controllers, Flash movies and who knows what else to achieve. (Check out <a href="http://github.com/gimite/web-socket-js">web-socket-js</a> for seamless crap-browser compatibility when using websockets).</div>
<p />
<div>So, in conclusion. There is no doubt that some of the advantages of NodeJS could have been achieved in DonkDJ with a better-designed architecture, but it would have been very painful to achieve. If you're looking to build a web application which handles any kind of file-uploading, you should look more into it. It's awesome.</div>
<p />
<p />
<div>Follow<strong> <a href="http://twitter.com/rfwatson">rfwatson on Twitter</a>.</strong></div>
</div>
</div>
</div>
</div>
</div>
</div>
	
</p>

<p><a href="http://rfw.posterous.com/how-nodejs-saved-my-web-application">Permalink</a> 

	| <a href="http://rfw.posterous.com/how-nodejs-saved-my-web-application#comment">Leave a comment&nbsp;&nbsp;&raquo;</a>

</p>]]>
      </description>
      <posterous:author>
        <posterous:userImage>http://posterous.com/images/profile/missing-user-75.png</posterous:userImage>
        <posterous:profileUrl>http://posterous.com/users/3sIZjvZRR8A1</posterous:profileUrl>
        <posterous:firstName>Rob</posterous:firstName>
        <posterous:lastName>W</posterous:lastName>
        <posterous:nickName>robw</posterous:nickName>
        <posterous:displayName>Rob W</posterous:displayName>
      </posterous:author>
      <media:content type="image/png" height="170" width="700" url="http://getfile3.posterous.com/getfile/files.posterous.com/rfw/FHWtSHThn14DQMwYtexoKvcMsuVB3T38metchTN49BHcws72ec3IQNdNpmPr/zap4.png">
        <media:thumbnail height="121" width="500" url="http://getfile4.posterous.com/getfile/files.posterous.com/rfw/QV7RLXhjPjUBi4OX4DV17AfRlHOmZ4Afr2aCGxIHRRxqG1l1gLKsoumiWVrZ/zap4.png.scaled.500.jpg" />
      </media:content>
      <media:content type="image/png" height="150" width="700" url="http://getfile8.posterous.com/getfile/files.posterous.com/rfw/3hrk6ZOJT8j6uMzPKpof64SUFsMf10tuMn7BWMXwuOHUnSKMi9CnunlIqrnI/zap2_2.png">
        <media:thumbnail height="107" width="500" url="http://getfile9.posterous.com/getfile/files.posterous.com/rfw/X6m4BBYEXrvQIARbJbSuFokh2ySss8hHIAQd1ErFm8XdrD93wtr5ken68uY9/zap2_2.png.scaled.500.jpg" />
      </media:content>
      <media:content type="image/png" height="200" width="700" url="http://getfile3.posterous.com/getfile/files.posterous.com/rfw/aUKSgMsA6aUpTh1jMgaIqAjK6zCEcC5ilqabJ72v6BoVgkm1Us3yAjebL1CR/zap1_2.png">
        <media:thumbnail height="143" width="500" url="http://getfile4.posterous.com/getfile/files.posterous.com/rfw/Dd0Dhh8IucDr4AT7ElN0OuHdRM7dH0QIXsBJzxXHH4YZjTd3LSYLpJcWtcYs/zap1_2.png.scaled.500.jpg" />
      </media:content>
      <media:content type="image/png" height="180" width="700" url="http://getfile8.posterous.com/getfile/files.posterous.com/rfw/m0FnQjvZ1UozvKgVsAKKi1TeSltU8f5DT9fpVeX9lEask4iSxHpSsHBWi57a/zap3.png">
        <media:thumbnail height="129" width="500" url="http://getfile9.posterous.com/getfile/files.posterous.com/rfw/PW2BgaEfiWs8tJEfJkjLtHgSSbqoa4y3i2ljKu0kcU7Chhqe173c0mNTRPAZ/zap3.png.scaled.500.jpg" />
      </media:content>
    </item>
    <item>
      <pubDate>Wed, 10 Feb 2010 13:42:00 -0800</pubDate>
      <title>The simplicity shift: surviving complex code</title>
      <link>http://rfw.posterous.com/the-challenge-of-coding-simply</link>
      <guid>http://rfw.posterous.com/the-challenge-of-coding-simply</guid>
      <description>
        <![CDATA[<p>
	<p>Today, I struggled for an hour to complete a difficult coding task. <p />The task -- to coerce an existing system to handle certain special cases differently -- was challenging for several reasons. <p />Firstly, the existing system was already rather complex. I wasn't overly familiar with the code, and so I had little intuitive feel for what was happening on the fringes of my attention. It was as if I was walking in an unfamiliar neighbourhood. Every avenue of code had to be navigated individually, and I could rarely afford my mind to wander from the road and consider the bigger picture.<p /> <em>(When you work with code you haven't written, it takes a lot more energy to navigate. You constantly have to look where you're going, which interrupts thinking about other matters, like how to fix or extend what you're looking at.)<br /> </em><br />Secondly, the system had been designed, built and deployed without consideration for this newly-required behaviour. In fact, by chance it had been implemented in a fashion which was actively <em>opposed</em> to its integration! Ideally, a large-scale refactoring was called for, but commercial pressure dictated that this was not the time to rip everything apart and start afresh.<br /> <em><br />(Although a total re-write would have taken longer, and carried a higher risk of introducing new problems, ironically it may well have taken less effort to achieve. It is easier for every developer to write new code from scratch, than it is to make a subtle and effective change to a wall of unfamiliar code.)<p /> </em>Eventually, as I grappled with the problem, I began to understand the best way to solve it. I was pleased because it felt like an acceptable solution. Not by any means a perfect one, but in the circumstances good. (A workaround, which is more than a mere hack.)<p /> I tested it, and it worked -- almost. All the acceptance tests passed, except one.<p />This edge case (of an edge case) seemed to completely break my solution. I sat for another twenty minutes, my mind still swimming with detail and control-flows, becoming frustrated that my solution didn't actually seem to work at all. When I finally saw the solution however -- unlike the original problem -- it was just a simple, one-line fix. <p /> Finally, everything worked.<p />Sometimes, dealing with complexity is unavoidable. After thinking in complex terms, making the mental shift back to thinking simply, doesn't happen instantly. It takes a bit of time, and then it happens of its own accord. It doesn't like to be forced.<p />Next time I will try to recognize when I need to make the shift, and make a cup of tea while I wait.<p /> </p>
	
</p>

<p><a href="http://rfw.posterous.com/the-challenge-of-coding-simply">Permalink</a> 

	| <a href="http://rfw.posterous.com/the-challenge-of-coding-simply#comment">Leave a comment&nbsp;&nbsp;&raquo;</a>

</p>]]>
      </description>
      <posterous:author>
        <posterous:userImage>http://posterous.com/images/profile/missing-user-75.png</posterous:userImage>
        <posterous:profileUrl>http://posterous.com/users/3sIZjvZRR8A1</posterous:profileUrl>
        <posterous:firstName>Rob</posterous:firstName>
        <posterous:lastName>W</posterous:lastName>
        <posterous:nickName>robw</posterous:nickName>
        <posterous:displayName>Rob W</posterous:displayName>
      </posterous:author>
    </item>
    <item>
      <pubDate>Fri, 15 Jan 2010 07:32:00 -0800</pubDate>
      <title>Why I don't call myself a hacker</title>
      <link>http://rfw.posterous.com/why-im-no-hacker</link>
      <guid>http://rfw.posterous.com/why-im-no-hacker</guid>
      <description>
        <![CDATA[<p>
	<p>Why do I never call myself a hacker? It's a matter of communication.</p>
<p>Language usage differs through time and place, but commonly I hear the word <em>hack</em> used in three ways.</p>
<p><strong>hacker</strong> <span style="font-size: x-small;"><em style="font-family: times new roman,serif;">n</em></span><br /> Refers to an individual who is skilled at avoiding security systems, especially computer systems. The usage I hear most commonly. The connotations are invariably negative, ranging from <a href="http://www.nytimes.com/2000/06/19/business/after-attack-by-hackers-aol-tightens-data-access.html" target="_blank">dishonest</a> to <a href="http://www.latimes.com/business/la-fi-google-china15-2010jan15,0,7251525.story" target="_blank">dangerous</a> to plain <a href="http://www.theregister.co.uk/2010/01/15/mckinnon_comment/" target="_blank">misguided</a>. Commonly used in the Western media as well as widely in public use.</p>
<p><strong>hacker</strong> <span style="font-size: x-small;"><em style="font-family: times new roman,serif;">n</em></span><br />Less widely used. Also refers to an individual who is highly skilled, typically in computer programming or similar, but this time the connotations are positive. Draws on historical associations with activities such as <a href="http://en.wikipedia.org/wiki/Phreaking" target="_blank">phreaking</a>, academic culture and more recently open source software.</p>
<p><strong>hack</strong> <em style="font-family: times new roman,serif;">v/n</em><br />Typically used to describe a quick fix to a problem, especially in programming, engineering and other fields. Although the connotations can be positive, I more often hear it being used -- and more importantly, understood -- negatively (<em>a dirty hack</em>, <em>hacked something together</em>, etc.).<p /> Language usage is always difficult to pin down, but I estimate that negativity is either implied or inferred, at least 80% of the times that the word <em>hacker</em> is used.</p>
<div>Language is a tool. It's a primary method for communicating with others around us, which is one of the most important things we have to master in order to succeed in this world. <br /></div>
<p>If I started a business, I would never choose to name it with a word that is commonly used or understood to have a negative connotation. It would be commercial suicide before I'd even started. I extend the same logic to how I describe myself. I just want to make it as easy as possible  to be taken seriously by those around me, whether it's my aunt, my boss or a client of my company.</p>
<div>I do appreciate that hacking as a glorious, colourful history which I have been roundly inspired by. And I appreciate that there are many talented and accomplished engineers, designers and entrepreneurs who refer to themselves as hackers, and deserve nothing but admiration.<p />But if anybody asks, I'm an engineer.<br /></div>
	
</p>

<p><a href="http://rfw.posterous.com/why-im-no-hacker">Permalink</a> 

	| <a href="http://rfw.posterous.com/why-im-no-hacker#comment">Leave a comment&nbsp;&nbsp;&raquo;</a>

</p>]]>
      </description>
      <posterous:author>
        <posterous:userImage>http://posterous.com/images/profile/missing-user-75.png</posterous:userImage>
        <posterous:profileUrl>http://posterous.com/users/3sIZjvZRR8A1</posterous:profileUrl>
        <posterous:firstName>Rob</posterous:firstName>
        <posterous:lastName>W</posterous:lastName>
        <posterous:nickName>robw</posterous:nickName>
        <posterous:displayName>Rob W</posterous:displayName>
      </posterous:author>
    </item>
    <item>
      <pubDate>Fri, 08 Jan 2010 06:39:00 -0800</pubDate>
      <title>Things I learnt by not writing tests</title>
      <link>http://rfw.posterous.com/things-i-learnt-by-not-writing-tests</link>
      <guid>http://rfw.posterous.com/things-i-learnt-by-not-writing-tests</guid>
      <description>
        <![CDATA[<p>
	<p>I like <a href="http://en.wikipedia.org/wiki/Test-driven_development">test-driven development</a>.<p />I like it because writing tests makes me stop and think before I start to code. I save large swathes of time by being able to recognise exactly what an implementation will actually achieve from the top down, before spending time and effort developing it in full from the bottom up. <p />I like it when, after making an innocuous change to some line of code, a failing test alerts me to a bug that would otherwise have taken me days or weeks to even know about, let alone fix. This happens at least several times per week, if I'm working full-time on a TDD project, which adds up to a lot of bugs avoided, which means much better software.<p />When I started writing test-first, I noticed the difference in the code and projects I produced immediately, and I've been TDD on my own projects ever since. It works, and I love doing it.<p />For the past few months I've been working on a large-scale Rails project which for a number of reasons, doesn't lend itself as easily to all-out test-driven development. So recently, I've been writing lots of Ruby code without tests, and that's made me better understand two, seemingly separate, things about TDD.<p /> Firstly, writing test-first permanently changed the way I write code. Even without a looming test failure, the methods I write still expect to have to confront one. They still tend to be short, descriptive and as self-contained as I can make them, and as a result easier to understand and change. This is despite not necessarily writing any tests at all, and presumably just because I'm in the habit.<p /> Secondly, I've realised that when I don't write tests I code too fast. TDD acts as a constraint on myself, something which stops me from running ahead of myself and forces me to stick around a particular problem area for long enough to understand it better, and often learn something that otherwise I'd have missed.</p>
<p>I don't remember ever reading a blog claiming that would be an advantage of TDD before I started. It's just a random side-effect, and this time  unexpected. I didn't even notice it until the tests were taken away. Now I understand myself a little better, I can at least try to compensate, slow down and achieve the same result without the tests.</p>
<p>Both of these  tell me that shaking things up, doing something a different way for a while and even just for the sake of it, is never a bad idea. You never know what you're going to take away from it.</p>
	
</p>

<p><a href="http://rfw.posterous.com/things-i-learnt-by-not-writing-tests">Permalink</a> 

	| <a href="http://rfw.posterous.com/things-i-learnt-by-not-writing-tests#comment">Leave a comment&nbsp;&nbsp;&raquo;</a>

</p>]]>
      </description>
      <posterous:author>
        <posterous:userImage>http://posterous.com/images/profile/missing-user-75.png</posterous:userImage>
        <posterous:profileUrl>http://posterous.com/users/3sIZjvZRR8A1</posterous:profileUrl>
        <posterous:firstName>Rob</posterous:firstName>
        <posterous:lastName>W</posterous:lastName>
        <posterous:nickName>robw</posterous:nickName>
        <posterous:displayName>Rob W</posterous:displayName>
      </posterous:author>
    </item>
    <item>
      <pubDate>Wed, 06 Jan 2010 09:19:00 -0800</pubDate>
      <title>The perception of programming</title>
      <link>http://rfw.posterous.com/the-perception-of-programming</link>
      <guid>http://rfw.posterous.com/the-perception-of-programming</guid>
      <description>
        <![CDATA[<p>
	<p>When I am making music, one of the most challenging aspects is the mixdown: listening intently to a looping segment of music for hours at a time, trying to make tiny changes to the volume or frequency of individual sounds, but never letting go of the bigger picture.</p>
<div class="gmail_quote">At the start of a session, it's pretty easy. When something sounds good, I know it straight away, and progress quickly.<p />After a few hours, it's a bit different. I've heard the sounds so many times that my ears -- and mind -- become completely accustomed to them. At this point, what sounds good is <em>whatever sounds good in comparison to what I was listening to five minutes previously</em>. Without even realising it, I've lost the bigger picture completely.<p /> I only ever know this has happened later, when I come back to what I thought was a good mix but find only  a horrible, muddy mess. It's really, really difficult to spot when it's actually happening.<p /> It's much the same with programming. I have to constantly challenge my own perception of the code that's in front of me. Two examples:<br /> 
<ul>
<li>When I work on a piece of existing code where a certain pattern has been used several times, and need to add a similar piece of functionality. At first glance, it seems an elegant solution to re-use the existing pattern. Actually, that's just a trick of the perception. When are three turds better than two?&nbsp; Why not just refactor the whole class and stop allowing my perception to deceive me? </li>
<li>When I work on a piece of code for a long time, it can become deceptively familiar. Deceptive because I begin to think it's better than it is, just because I know what's going on. It doesn't follow that familar code is always bad code, but the longer I look at it, the less I can tell.</li>
</ul>
I usually only spot these mistakes with hindsight too. At the time, it just feels right. That's perception again.<p />Learning to recognise and make adjustments for perception is a challenge for <em>every</em> creative person.<br /></div>
	
</p>

<p><a href="http://rfw.posterous.com/the-perception-of-programming">Permalink</a> 

	| <a href="http://rfw.posterous.com/the-perception-of-programming#comment">Leave a comment&nbsp;&nbsp;&raquo;</a>

</p>]]>
      </description>
      <posterous:author>
        <posterous:userImage>http://posterous.com/images/profile/missing-user-75.png</posterous:userImage>
        <posterous:profileUrl>http://posterous.com/users/3sIZjvZRR8A1</posterous:profileUrl>
        <posterous:firstName>Rob</posterous:firstName>
        <posterous:lastName>W</posterous:lastName>
        <posterous:nickName>robw</posterous:nickName>
        <posterous:displayName>Rob W</posterous:displayName>
      </posterous:author>
    </item>
    <item>
      <pubDate>Sat, 26 Dec 2009 09:26:00 -0800</pubDate>
      <title>MusicTweets - a Redis/WebSocket powered experiment for Google Chrome</title>
      <link>http://rfw.posterous.com/musictweets-a-rediswebsocket-powered-experime</link>
      <guid>http://rfw.posterous.com/musictweets-a-rediswebsocket-powered-experime</guid>
      <description>
        <![CDATA[<p>
	<p>I was at a loose end last night, and this was the result. It's a simple web-application which makes use of a number of technologies which I've been wanting to try out for a while: <a href="http://code.google.com/p/redis/">Redis</a>, <a href="http://dev.w3.org/html5/websockets/">WebSockets</a>, Twitter's <a href="http://apiwiki.twitter.com/Streaming-API-Documentation">Streaming API</a> and Ruby's <a href="http://rubyeventmachine.com/">EventMachine</a> gem.<p /> If you're running a build of <a href="http://www.google.co.uk/chrome">Chrome</a> which supports WebSockets, then <span style="text-decoration: line-through;">you can see it in action now at <a href="http://icanhaz.com/musictweets">http://icanhaz.com/musictweets</a>.</span> (the OSX release of Chrome definitely supports WebSockets, but the current Windows release seems not to yet, at least as far as I could tell).<p /> <strong>UPDATE</strong>: The demo is no longer online, but this screencast will give you an idea. MusicTweets simply streams music-related tweets live into your browser, categorizing them into colour-coded genres as it goes.<p /> 
<object data="http://www.screentoaster.com/swf/STPlayer.swf" type="application/x-shockwave-flash" height="344" width="425">
<param name="movie" value="http://www.screentoaster.com/swf/STPlayer.swf" />
<param name="allowFullScreen" value="true" />
<param name="allowScriptAccess" value="always" />
<param name="flashvars" value="video=stU0tSRkNIR19eSFlZWlJdUVVd" />
</object>
</p>
<p>The application is fairly simple. It receives a stream of JSON-encoded tweets which are immediately inserted into Redis, an in-memory key/value database, in the form of an ordered list. This task is handled by a separate, dedicated process, which means that any later processing, parsing and on-disk storage of tweets is entirely decoupled from the HTTP cycle. It can therefore be expected that a very high volume of tweets could be processed.<p /> Now we have some tweets waiting to be displayed, things start to get interesting. Although a number of solutions would have been possible, a traditional implementation of this application would be likely involve using regular client-side AJAX requests to poll the server and request a list of new tweets to display, if any were available. This approach has always seemed something of a messy hack, however. If no new data was available, then the server and possibly the database may be deluged with polling requests which are of no use. Hardly efficient. The advent of HTML5's WebSockets, so far available only in Google Chrome, means a different approach is possible.<p /> MusicTweets uses the EventMachine gem to allow direct, 2-way browser-to-server communication, while bypassing the normal web framework controller and routing elements. Each EM connection which is created polls Redis every second for any new, undisplayed tweets. It is important to note that this polling happens on the <em>server</em>-side, <em>not</em> the client-side as in the AJAX model. When new tweets are available, the data is prepared and then some more JSON pushed on to the browser, where a JavaScript callback which displays the data is automatically invoked.<p /> From a coding point of view, the elegance and simplicity of this model is clear. Any web developer will tell you that no more polling every five seconds is a very welcome prospect. Although I will not be covering them in detail in this post, there are a <a href="http://www.kaazing.org/confluence/display/KAAZING/What+is+an+HTML+5+WebSocket">number of other functional advantages</a> of WebSockets compared to older solutions to this problem, including the use of just a single port for bi-directional communication, and a guaranteed stable connection which will not be severed by proxies or firewalls.<p /> In conclusion, this was a web application which I built in 24 hours to help me understand the potential of technologies such as Redis, and especially HTML5's WebSockets. It is clear that the technology holds great potential for all web developers. What the world now needs is to see Firefox, Internet Explorer and Safari release their own WebSockets implementations. Then we can expect to see the technology take off very quickly indeed.</p>
	
</p>

<p><a href="http://rfw.posterous.com/musictweets-a-rediswebsocket-powered-experime">Permalink</a> 

	| <a href="http://rfw.posterous.com/musictweets-a-rediswebsocket-powered-experime#comment">Leave a comment&nbsp;&nbsp;&raquo;</a>

</p>]]>
      </description>
      <posterous:author>
        <posterous:userImage>http://posterous.com/images/profile/missing-user-75.png</posterous:userImage>
        <posterous:profileUrl>http://posterous.com/users/3sIZjvZRR8A1</posterous:profileUrl>
        <posterous:firstName>Rob</posterous:firstName>
        <posterous:lastName>W</posterous:lastName>
        <posterous:nickName>robw</posterous:nickName>
        <posterous:displayName>Rob W</posterous:displayName>
      </posterous:author>
    </item>
    <item>
      <pubDate>Thu, 17 Dec 2009 04:18:00 -0800</pubDate>
      <title>I miss DJ mixtapes</title>
      <link>http://rfw.posterous.com/i-miss-dj-mixtapes</link>
      <guid>http://rfw.posterous.com/i-miss-dj-mixtapes</guid>
      <description>
        <![CDATA[<p>
	<p>I really, really miss DJ mixtapes.</p>
<p><img src="http://img132.imageshack.us/img132/8499/269393517b826beea65o.jpg" alt="" /></p>
<div class="gmail_quote">In particular, I miss making mixtapes. Recording a mixtape was really no trivial matter: first of all recording and re-recording ninety minutes of music, at a good sound quality and with no jumping needles, scratches or mixing disasters. There was no digital editing software that could rescue a previously high-quality mix that had gone Pete Tong towards the end of a side, so the only option was to get it right all the way through. It was tough.<p /> Once I had a mix, I would make myself listen to the recording from start to end - usually a number of times - weighing up whether or not I had achieved the standard I was aiming for. Usually I hadn't. Sometimes, it would literally take me weeks to get a mix the way I wanted it.<p /> And this was all <em>before</em> I had to duplicate the master tape. Copying a ninety minute tape took, well, ninety minutes, and only having one dual tapedeck meant I could only copy one cassette at a time. So if I was going to a party, and wanted to take five mixtapes, that was almost eight hours of continuous copying that I had to schedule before leaving the house. It was a bit of a mission.<p /> But it was always worth it. Those tapes seemed to contain real value, in part because of the amount of effort that went into making them - and people recognised that. I always sold the tapes I made, for a standard &pound;5 each, and I never had trouble selling them either. I wish could have produced lots more, because I'm pretty sure I could have found a way to sell them too.<p /> It is less easy to sell music these days, and near enough impossible to sell mixtapes. But I'm not too interested in the financial difference, because I'm never going to get rich selling DJ mixes one-by-one. And the cassette tape, as a medium of distribution, is long gone, and I'll concede that is probably for the best.<p /> <em>What I'm interested in is how I could <strong>make people value</strong> a mix as much as they used to.<p /> </em>I'm not talking about the standard of either mixing or music here - a high level of both are probably assumed qualities of a valuable mix. And there are thousands of mix sets lying around the internet which exhibit these attributes, but it doesn't necessarily mean that people value them more than others.<p /> What I want to understand is how to make a piece of digitally-distributed music really special. How to help it stand apart from all the others, and make people feel that they are getting something different. How to give it some personality that goes a little way beyond the digitized audio.<p /> Times have changed, of course: file-sharing and digital downloads have altered the way that music is distributed and consumed for ever, but I don't believe that these changes have necessarily changed what music means to people on a fundamental, cultural level. In other words, I believe that people will still value music if they're given the opportunity and a reason to.<p /> I suppose this is partly a personal question, but it must also be an issue not just for DJs, but for musicians of all varieties. I'm sure there's a lot of ideas out there already, but I've never had reason to think about it, at least not up until now.<p /> So if you're reading and you've got an opinion, then I'd like to hear it.<p /><span style="font-size: xx-small;"><br />Photo by <a href="http://www.flickr.com/photos/esparta/">Esparta</a>.</span><br /></div>
	
</p>

<p><a href="http://rfw.posterous.com/i-miss-dj-mixtapes">Permalink</a> 

	| <a href="http://rfw.posterous.com/i-miss-dj-mixtapes#comment">Leave a comment&nbsp;&nbsp;&raquo;</a>

</p>]]>
      </description>
      <posterous:author>
        <posterous:userImage>http://posterous.com/images/profile/missing-user-75.png</posterous:userImage>
        <posterous:profileUrl>http://posterous.com/users/3sIZjvZRR8A1</posterous:profileUrl>
        <posterous:firstName>Rob</posterous:firstName>
        <posterous:lastName>W</posterous:lastName>
        <posterous:nickName>robw</posterous:nickName>
        <posterous:displayName>Rob W</posterous:displayName>
      </posterous:author>
    </item>
    <item>
      <pubDate>Wed, 16 Dec 2009 02:42:00 -0800</pubDate>
      <title>Why I return fashionably late</title>
      <link>http://rfw.posterous.com/why-i-return-fashionably-late</link>
      <guid>http://rfw.posterous.com/why-i-return-fashionably-late</guid>
      <description>
        <![CDATA[<p>
	<p>Ruby is a language with a very flexible, forgiving syntax. This allows for a great deal of expressiveness, and is one of the reasons I love writing Ruby code.</p>
<p>I appreciate being able to choose between employing <span style="font-family: courier new,monospace;">do</span>/<span style="font-family: courier new,monospace;">end</span> or <span style="font-family: courier new,monospace;">{}</span>, the subtle differences between <span style="font-family: courier new,monospace;">or</span> and <span style="font-family: courier new,monospace;">||</span>, and the rather elegant simplicity of the <span style="font-family: courier new,monospace;">unless</span> clause, amongst many others.</p>
<p>When I first started to code in Ruby, I was fluent in Java, C and Python - all languages which allow considerably less freedom when designing code. When I look back on old code from three years ago, It is fair to say that I became a bit drunk on the syntactic possibilities.</p>
<p>Actually, I probably went a bit over the top.</p>
<p>I could barely put together a dozen lines of code without incorporating one of the exciting idioms I had learnt. I used <span style="font-family: courier new,monospace;">unless</span> clauses, and then immediately double-negated them with an else. I used <span style="font-family: courier new,monospace;">defined?</span> when simply referencing the instance variable would have done perfectly well, if not better. And especially, at the start of every method, I would invariably have a couple of <a href="http://ozmm.org/posts/return_early.html">one-line if statements</a> - just because I could. This is a really simple example, but something like:</p>
<p><div class="data type-ruby">
    
      <table class="lines" cellspacing="0" cellpadding="0">
        <tr>
          <td>
            <pre class="line_numbers"><span rel="#L1" id="L1">1</span>
<span rel="#L2" id="L2">2</span>
<span rel="#L3" id="L3">3</span>
<span rel="#L4" id="L4">4</span>
<span rel="#L5" id="L5">5</span>
</pre>
          </td>
          <td width="100%">
            
              
                <div class="highlight"><pre /><div class="line" id="LC1"><span class="k">def</span> <span class="nf">save_with_validation</span></div><div class="line" id="LC2">&nbsp;&nbsp;<span class="k">return</span> <span class="k">unless</span> <span class="n">valid?</span></div><div class="line" id="LC3">&nbsp;&nbsp;<span class="k">return</span> <span class="k">unless</span> <span class="n">able_to_save?</span></div><div class="line" id="LC4">&nbsp;&nbsp;<span class="n">save</span></div><div class="line" id="LC5"><span class="k">end</span></div></pre></div>
              
            
          </td>
        </tr>
      </table>
    
  </div></p>
<p>I'm not entirely sure that it's with good reason, but this style really bugs me now. <p />  Maybe it's the fact that it removes all indentation from the code: the ability to zoom out and get a bird's eye view of the code is gone, the only saving a couple of lines of code.<p />  Or perhaps it's because it seems so easy, further down the line, to throw in a couple of these to prevent some duff data being passed into the method, when what's really required is a proper validation structure further back down the path, or some other thorough refactoring of the code.<p />I know that the occasional instance of this is unlikely to affect the quality of a piece of code, but like the <a href="http://en.wikipedia.org/wiki/Fixing_Broken_Windows">broken windows theory</a> described in the Pragmatic Programmer, bad code is usually a cumulative process  which starts with neglecting the small, seemingly insignificant points. That's why I like to be as explicit as I can be with the control flow and layout of my code. I like to take every possibility to underline and accentuate what's happening, even if it costs me a couple of lines of whitespace.<p />  I think I've ended up going full circle. When I started out I wrote Ruby code that looked like Java. Then I wrote Ruby code that looked as little like Java as I could make it. And now, I find myself wanting to write Ruby code that looks like Java again.</p>
	
</p>

<p><a href="http://rfw.posterous.com/why-i-return-fashionably-late">Permalink</a> 

	| <a href="http://rfw.posterous.com/why-i-return-fashionably-late#comment">Leave a comment&nbsp;&nbsp;&raquo;</a>

</p>]]>
      </description>
      <posterous:author>
        <posterous:userImage>http://posterous.com/images/profile/missing-user-75.png</posterous:userImage>
        <posterous:profileUrl>http://posterous.com/users/3sIZjvZRR8A1</posterous:profileUrl>
        <posterous:firstName>Rob</posterous:firstName>
        <posterous:lastName>W</posterous:lastName>
        <posterous:nickName>robw</posterous:nickName>
        <posterous:displayName>Rob W</posterous:displayName>
      </posterous:author>
    </item>
    <item>
      <pubDate>Thu, 10 Dec 2009 10:31:21 -0800</pubDate>
      <title>hello world</title>
      <link>http://rfw.posterous.com/hello-world-2054</link>
      <guid>http://rfw.posterous.com/hello-world-2054</guid>
      <description>
        <![CDATA[<p>
	
	
</p>

<p><a href="http://rfw.posterous.com/hello-world-2054">Permalink</a> 

	| <a href="http://rfw.posterous.com/hello-world-2054#comment">Leave a comment&nbsp;&nbsp;&raquo;</a>

</p>]]>
      </description>
      <posterous:author>
        <posterous:userImage>http://posterous.com/images/profile/missing-user-75.png</posterous:userImage>
        <posterous:profileUrl>http://posterous.com/users/3sIZjvZRR8A1</posterous:profileUrl>
        <posterous:firstName>Rob</posterous:firstName>
        <posterous:lastName>W</posterous:lastName>
        <posterous:nickName>robw</posterous:nickName>
        <posterous:displayName>Rob W</posterous:displayName>
      </posterous:author>
    </item>
  </channel>
</rss>

