<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">

 <title>Dustin Sallings</title>
 <link rel="hub" href="http://pubsubhubbub.appspot.com/" />
 <link href="http://dustin.github.com/atom.xml" rel="self"/>
 <link href="http://dustin.github.com/"/>
 <updated>2009-11-16T23:51:08-08:00</updated>
 <id>http://dustin.github.com/</id>
 <author>
   <name>Dustin Sallings</name>
   <email>dustin@spy.net</email>
 </author>

 
 <entry>
   <title>Hello World in Go -- A Memcached Server</title>
   <link href="http://dustin.github.com/2009/11/12/gomemcached.html"/>
   <updated>2009-11-12T00:00:00-08:00</updated>
   <id>http://dustin.github.com/2009/11/12/gomemcached</id>
   <content type="html">&lt;h1 id='hello_world_in_go__a_memcached_server'&gt;Hello World in Go &amp;#8211; A Memcached Server&lt;/h1&gt;
&lt;div&gt;
  &lt;img class='floatright' src='/images/gomemcached.png' alt='go memcached' /&gt;
&lt;/div&gt;
&lt;p&gt;I sat down last night to learn &lt;a href='http://golang.org/'&gt;go&lt;/a&gt;. I&amp;#8217;ve been a fan of concurrency-oriented languages since I wrote my &lt;a href='http://github.com/dustin/environ'&gt;first erlang program&lt;/a&gt; in early 2004 (which I still use). I&amp;#8217;ve been itching to give &lt;a href='http://golang.org/'&gt;go&lt;/a&gt; a go since the announcement.&lt;/p&gt;

&lt;p&gt;The first thing I thought of that could naturally be modeled as a concurrent program was a &lt;a href='http://memcached.org/'&gt;memcached&lt;/a&gt; server. That has sort of become my &amp;#8220;hello, world&amp;#8221; as I have implemented the &lt;a href='http://github.com/dustin/memcached-test'&gt;first python server&lt;/a&gt; for the memcached binary protocol (using asyncore), the &lt;a href='http://memcached.org/'&gt;main server&lt;/a&gt; (which &lt;a href='http://blogs.sun.com/trond/'&gt;Trond&lt;/a&gt; made beautiful), an &lt;a href='/2009/10/11/ememcached.html'&gt;erlang server&lt;/a&gt;, and a &lt;a href='http://github.com/dustin/twisted-memcached'&gt;twisted server&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;My &lt;a href='http://github.com/dustin/gomemcached'&gt;go server&lt;/a&gt; has a lot in common with &lt;a href='/2009/10/11/ememcached.html'&gt;ememcached&lt;/a&gt; since both languages are concurrency oriented. There is one process/goroutine for managing the actual data storage, one for accepting TCP connections, and one for each connected user.&lt;/p&gt;

&lt;p&gt;The TCP listener just accepts and spins up new processes or goroutines to handle the IO on the connections and then they wait for data. Once they&amp;#8217;ve read a single request, they dispatch to the storage mechanism which will sequentually process each operation, regardless of input concurrency. Very simple, very easy to understand.&lt;/p&gt;

&lt;p&gt;I mostly liked what I saw. The code feels a little C-like, but in practice is fairly intuitive and pleasant to work with. There are many &lt;a href='http://scienceblogs.com/goodmath/2009/11/googles_new_language_go.php'&gt;reviews&lt;/a&gt; of the language and I&amp;#8217;m not attempting to write one, but I will say that I got hung up on the lack of exceptions which required me to do &lt;a href='http://github.com/dustin/gomemcached/blob/master/mc_conn_handler.go#L72'&gt;weird&lt;/a&gt; error handling in sequential code.&lt;/p&gt;

&lt;p&gt;The concurrency primitives remind me a bit of &lt;a href='http://en.wikipedia.org/wiki/Alef_%28programming_language%29'&gt;alef&lt;/a&gt; which I remember from early &lt;a href='http://plan9.bell-labs.com/plan9/'&gt;plan9&lt;/a&gt; distributions, though I never actually got a chance to work with it before it got killed off. The one part of alef I really liked lived on in go, which further motivated me.&lt;/p&gt;</content>
 </entry>
 
 <entry>
   <title>ZFS for MacOS X</title>
   <link href="http://dustin.github.com/2009/10/23/mac-zfs.html"/>
   <updated>2009-10-23T00:00:00-07:00</updated>
   <id>http://dustin.github.com/2009/10/23/mac-zfs</id>
   <content type="html">&lt;h1 id='zfs_for_macos_x'&gt;ZFS for MacOS X&lt;/h1&gt;
&lt;div&gt;
  &lt;img class='floatright' src='/images/zfs.png' alt='ZFS ROX' /&gt;
&lt;/div&gt;
&lt;p&gt;So I&amp;#8217;m a pretty heavy &lt;a href='http://en.wikipedia.org/wiki/ZFS'&gt;ZFS&lt;/a&gt; user. I&amp;#8217;ve got a FreeBSD box with a ZFS root that all my important stuff is on, and a mac mini with ZFS that all my movies are on.&lt;/p&gt;

&lt;p&gt;Around Snow Leopard, Apple seemed to have not only dropped support for it, but the download from &lt;a href='http://macosforge.org/'&gt;macosforge&lt;/a&gt; seemed to not work, either.&lt;/p&gt;

&lt;p&gt;I&amp;#8217;ve been hoping someone would come around and fix this, but nothing&amp;#8217;s happened.&lt;/p&gt;

&lt;p&gt;Then today, I read this:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;The ZFS project has been discontinued. The mailing list and
repository will also be removed shortly.&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;That made me very sad, so I decided to do something about it.&lt;/p&gt;

&lt;h2 id='github_project'&gt;Github Project&lt;/h2&gt;

&lt;p&gt;I set up a &lt;a href='http://github.com/dustin/mac-zfs'&gt;mac-zfs&lt;/a&gt; project on github and started hacking. It didn&amp;#8217;t take long and I was able to get to my zpool that has all of my music. (for the record, Bad Religion was my ZFS test case)&lt;/p&gt;

&lt;h2 id='downloads'&gt;Downloads&lt;/h2&gt;
&lt;div&gt;
  &lt;a href='http://cloud.github.com/downloads/dustin/mac-zfs/ZFS-119-SnowLeopard.pkg'&gt;
    &lt;img class='floatright' src='/images/pkg.png' alt='Install Me' /&gt;
  &lt;/a&gt;
&lt;/div&gt;
&lt;p&gt;I wanted to make it a bit easier to get going, so I created an &lt;a href='http://cloud.github.com/downloads/dustin/mac-zfs/ZFS-119-SnowLeopard.pkg'&gt;installer&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;You still need to kind of know what you&amp;#8217;re doing to make use of it. But I&amp;#8217;m hoping we can work together to make it easy for everybody.&lt;/p&gt;

&lt;h2 id='update'&gt;Update:&lt;/h2&gt;

&lt;p&gt;We&amp;#8217;ve got a &lt;a href='http://code.google.com/p/maczfs/'&gt;google code&lt;/a&gt; page up for bug tracking and what-not and a &lt;a href='http://groups.google.com/group/zfs-macos'&gt;mailing list&lt;/a&gt; for general discussion.&lt;/p&gt;

&lt;p&gt;Come join us.&lt;/p&gt;</content>
 </entry>
 
 <entry>
   <title>Memcached Report Card</title>
   <link href="http://dustin.github.com/2009/10/22/memcached-reportcard.html"/>
   <updated>2009-10-22T00:00:00-07:00</updated>
   <id>http://dustin.github.com/2009/10/22/memcached-reportcard</id>
   <content type="html">&lt;h1 id='memcached__report_card_for_an_open_source_project'&gt;Memcached - Report Card for an Open Source Project&lt;/h1&gt;

&lt;p&gt;While approaching our &lt;a href='http://code.google.com/p/memcached/wiki/ReleaseNotes140'&gt;1.4&lt;/a&gt; release, the memcached project slipped into a state where we weren&amp;#8217;t pushing out new code very frequently.&lt;/p&gt;

&lt;p&gt;Some members of the community demanded releases occur more frequently, so &lt;a href='http://consoleninja.net/'&gt;dormando&lt;/a&gt; published a proposal for a release cycle for memcached that would help drive us into the direction of universal happiness.&lt;/p&gt;

&lt;p&gt;I had a feeling that his plan and efforts to keep us on it were quite effective, but the purpose of this post is to sort of take an objective retrospective look and see how the reality of the progress of the project holds up to my perception.&lt;/p&gt;

&lt;h2 id='release_scheduling'&gt;Release Scheduling&lt;/h2&gt;

&lt;p&gt;&lt;img src='/images/memcached-velocity.png' alt='Release Velocity' /&gt;&lt;/p&gt;

&lt;p&gt;This chart pretty much speaks for itself. It&amp;#8217;s clear to see that we very quickly approached the stated goal of 30 days between releases.&lt;/p&gt;

&lt;p&gt;I should note that &lt;a href='http://code.google.com/p/memcached/wiki/ReleaseNotes141'&gt;1.4.1&lt;/a&gt; was released later than expected because a user hit a real live failure scenario in a production system that took a while to isolate. Once we did, we wrote some tests to ensure that the entire class of bug described can&amp;#8217;t happen again.&lt;/p&gt;

&lt;p&gt;These releases are bigger than they appear, but very well tested on our &lt;a href='http://code.google.com/p/memcached/wiki/BuildFarm'&gt;build farm&lt;/a&gt; and in production environments. To see what goes into each release, check the release notes for &lt;a href='http://code.google.com/p/memcached/wiki/ReleaseNotes140'&gt;1.4.0&lt;/a&gt;, &lt;a href='http://code.google.com/p/memcached/wiki/ReleaseNotes141'&gt;1.4.1&lt;/a&gt;, and &lt;a href='http://code.google.com/p/memcached/wiki/ReleaseNotes142'&gt;1.4.2&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id='bug_status'&gt;Bug Status&lt;/h2&gt;

&lt;p&gt;We&amp;#8217;ve been taking bugs very seriously.&lt;/p&gt;

&lt;p&gt;Crash bugs are resolved &lt;em&gt;very&lt;/em&gt; quickly, but bugs in general are resolved within our release cycle.&lt;/p&gt;

&lt;p&gt;Please let us know via &lt;a href='http://groups.google.com/group/memcached'&gt;our mailing list&lt;/a&gt; or our &lt;a href='http://code.google.com/p/memcached/issues/list'&gt;bug tracker&lt;/a&gt; if you&amp;#8217;re having issues or can&amp;#8217;t figure something out. We fix things very quickly, but &lt;strong&gt;we can&amp;#8217;t fix what we don&amp;#8217;t hear about&lt;/strong&gt;. Remember that goes for the &lt;a href='http://code.google.com/p/memcached/wiki/Start'&gt;wiki docs&lt;/a&gt;, too.&lt;/p&gt;

&lt;p&gt;The following chart shows the average age (time between when they were filed and when they were closed) of bugs by month in memcached. The error bars are showing the minimum and maximum age for any given month. Issues marked as invalid, won&amp;#8217;tfix, and had other indicators of not being actual defects or missing features in the software were excluded.&lt;/p&gt;

&lt;p&gt;Four anomalies were identified and described briefly in the chart. These are all considered trivial issues that were not detrimental to the state of the server, but they do show that we&amp;#8217;re sometimes not too quick in cases like these. (more details on &lt;a href='http://code.google.com/p/memcached/issues/detail?id=9'&gt;A&lt;/a&gt;, &lt;a href='http://code.google.com/p/memcached/issues/detail?id=42'&gt;B&lt;/a&gt;, &lt;a href='http://code.google.com/p/memcached/issues/detail?id=53'&gt;C&lt;/a&gt;, and &lt;a href='http://code.google.com/p/memcached/issues/detail?id=59'&gt;D&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;&lt;img src='/images/memcached-issue-age.png' alt='Bug Age' /&gt;&lt;/p&gt;

&lt;p&gt;I also found it useful to consider what I called the &amp;#8220;bug load.&amp;#8221; That is, the number of bugs going in and out of the project by month.&lt;/p&gt;

&lt;p&gt;The following chart is showing bugs activity in the memcached project.&lt;/p&gt;

&lt;p&gt;Positive numbers are bugs being reported to the project. Negative numbers are bugs that we closed (bugs removed from the project). The line indicates the net number of bugs at the end of the given month.&lt;/p&gt;

&lt;p&gt;&lt;img src='/images/memcached-bug-load.png' alt='Bug Load' /&gt;&lt;/p&gt;

&lt;h2 id='so_whats_the_grade'&gt;So What&amp;#8217;s the Grade?&lt;/h2&gt;

&lt;p&gt;I&amp;#8217;d say we&amp;#8217;ve got a solid B. We have room for improvement and I&amp;#8217;m confident we&amp;#8217;re making it.&lt;/p&gt;

&lt;p&gt;If you disagree (and can articulate why specifically) or otherwise have feedback, a warm, active community awaits.&lt;/p&gt;

&lt;p&gt;Contact us on irc &lt;a href='irc://irc.freenode.net/memcached'&gt;(freenode.net #memcached)&lt;/a&gt;, email suggestions or questions to our &lt;a href='http://groups.google.com/group/memcached'&gt;list&lt;/a&gt;, or just go &lt;a href='http://code.google.com/p/memcached/issues/list'&gt;file a bug&lt;/a&gt;.&lt;/p&gt;</content>
 </entry>
 
 <entry>
   <title>EMemcached</title>
   <link href="http://dustin.github.com/2009/10/11/ememcached.html"/>
   <updated>2009-10-11T00:00:00-07:00</updated>
   <id>http://dustin.github.com/2009/10/11/ememcached</id>
   <content type="html">&lt;h1 id='why_an_erlang_memcached_implementation'&gt;Why an Erlang Memcached Implementation?&lt;/h1&gt;
&lt;div&gt;
  &lt;img class='floatright' src='/images/a-o-k.png' alt='Memcached! Do you speak it?' /&gt;
&lt;/div&gt;
&lt;p&gt;I wrote an &lt;a href='http://github.com/dustin/ememcached'&gt;erlang implementation&lt;/a&gt; of a memcached server speaking the binary protocol over the weekend. You&amp;#8217;re all probably wondering why.&lt;/p&gt;

&lt;p&gt;It was half a learning exercise, and half a way to plug a &lt;a href='http://blogs.sun.com/trond/entry/memcapable'&gt;memcapable&lt;/a&gt; interface into more applications. In its current form, I consider it a framework to take existing erlang backend stores (e.g dict, ets, dets, mnesia, &lt;a href='http://riak.basho.com/'&gt;riak&lt;/a&gt;, etc&amp;#8230;) and put a memcached interface on them. On its own, it&amp;#8217;s a little boring.&lt;/p&gt;

&lt;p&gt;The first implementation of the binary protocol was in my &lt;a href='http://github.com/dustin/memcached-test'&gt;memcached-test&lt;/a&gt; project (which I still actively use as a reference implementation when playing around with features and testing clients and things). It&amp;#8217;s a simple asyncore based server in python (with a sample synchronous client).&lt;/p&gt;

&lt;p&gt;A bit later, came the actual &lt;a href='http://code.google.com/p/memcached/'&gt;production C&lt;/a&gt; server version we all know and love (which also had my &lt;a href='http://code.google.com/p/spymemcached/'&gt;java client&lt;/a&gt; to talk to).&lt;/p&gt;

&lt;p&gt;I wrote &lt;a href='http://github.com/dustin/twisted-memcached'&gt;twisted memcached&lt;/a&gt; and built an S3-backed server one weekend.&lt;/p&gt;

&lt;p&gt;That leads us back to the present &lt;a href='http://github.com/dustin/ememcached'&gt;erlang-based server&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id='but_wait_theres_more'&gt;But Wait, There&amp;#8217;s More!&lt;/h2&gt;

&lt;p&gt;If you have a basic understanding of erlang, you may find this implementation to be the best documentation of the protocol in existence.&lt;/p&gt;

&lt;p&gt;In the main loop of a connection, all I&amp;#8217;m doing is calling &lt;code&gt;process_message&lt;/code&gt; in a loop with the connected socket, a reference to the storage server (an erlang &lt;code&gt;gen_server&lt;/code&gt; implementation), and the result of a call to &lt;code&gt;gen_tcp:recv(Socket, 24)&lt;/code&gt;. That last call will either return &lt;code&gt;{ok, SomeData}&lt;/code&gt; or &lt;code&gt;{error, SomeReason}&lt;/code&gt;.&lt;/p&gt;

&lt;h3 id='the_fun_part'&gt;The Fun Part&lt;/h3&gt;

&lt;p&gt;The only definition of &lt;code&gt;process_message/3&lt;/code&gt; I have is shown below. The &lt;em&gt;only&lt;/em&gt; valid way to call this is when the third argument is the &lt;code&gt;{ok,
Data}&lt;/code&gt; tuple where &lt;code&gt;Data&lt;/code&gt; in this case is a binary pattern. Some of the values are filled in (which means they &lt;em&gt;must&lt;/em&gt; match for this function body to be invoked), and some are bindings which will receive the value.&lt;/p&gt;

&lt;p&gt;In the code below (extracted from &lt;a href='http://github.com/dustin/ememcached/blob/master/src/mc_connection.erl'&gt;mc_connection.erl&lt;/a&gt;), you&amp;#8217;ll see that the first 8 bits must exactly be the defined &lt;code&gt;REQ_MAGIC&lt;/code&gt; constant, and then the next 8 bits are stored in &lt;code&gt;OpCode&lt;/code&gt;, and so on.&lt;/p&gt;

&lt;p&gt;Any attempt to process a message not in this form will result in the connection processing crashing (the effect of which is your client being disconnected from it).&lt;/p&gt;

&lt;p&gt;So you can see how erlang easily allows us to rip the bits we need out of the header for dispatch, so the next thing is to ask for the remaining data (extra headers, key, and body) before dispatching to the storage server process.&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;span class='nf'&gt;process_message&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='nv'&gt;Socket&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='nv'&gt;StorageServer&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt;
                &lt;span class='p'&gt;{&lt;/span&gt;&lt;span class='n'&gt;ok&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='o'&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class='no'&gt;?REQ_MAGIC&lt;/span&gt;&lt;span class='p'&gt;:&lt;/span&gt;&lt;span class='mi'&gt;8&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='nv'&gt;OpCode&lt;/span&gt;&lt;span class='p'&gt;:&lt;/span&gt;&lt;span class='mi'&gt;8&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='nv'&gt;KeyLen&lt;/span&gt;&lt;span class='p'&gt;:&lt;/span&gt;&lt;span class='mi'&gt;16&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt;
                      &lt;span class='nv'&gt;ExtraLen&lt;/span&gt;&lt;span class='p'&gt;:&lt;/span&gt;&lt;span class='mi'&gt;8&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='mi'&gt;0&lt;/span&gt;&lt;span class='p'&gt;:&lt;/span&gt;&lt;span class='mi'&gt;8&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='mi'&gt;0&lt;/span&gt;&lt;span class='p'&gt;:&lt;/span&gt;&lt;span class='mi'&gt;16&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt;
                      &lt;span class='nv'&gt;BodyLen&lt;/span&gt;&lt;span class='p'&gt;:&lt;/span&gt;&lt;span class='mi'&gt;32&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt;
                      &lt;span class='nv'&gt;Opaque&lt;/span&gt;&lt;span class='p'&gt;:&lt;/span&gt;&lt;span class='mi'&gt;32&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt;
                      &lt;span class='nv'&gt;CAS&lt;/span&gt;&lt;span class='p'&gt;:&lt;/span&gt;&lt;span class='mi'&gt;64&lt;/span&gt;&lt;span class='o'&gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class='p'&gt;})&lt;/span&gt; &lt;span class='o'&gt;-&amp;gt;&lt;/span&gt;

    &lt;span class='c'&gt;% After the header is extras, key, and then body&lt;/span&gt;
    &lt;span class='nv'&gt;Extra&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='n'&gt;read_data&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='nv'&gt;Socket&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='nv'&gt;ExtraLen&lt;/span&gt;&lt;span class='p'&gt;),&lt;/span&gt;
    &lt;span class='nv'&gt;Key&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='n'&gt;read_data&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='nv'&gt;Socket&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='nv'&gt;KeyLen&lt;/span&gt;&lt;span class='p'&gt;),&lt;/span&gt;
    &lt;span class='c'&gt;% Note that the length of the body from the header includes&lt;/span&gt;
    &lt;span class='c'&gt;% the lengths of the key and extras.&lt;/span&gt;
    &lt;span class='nv'&gt;Body&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='n'&gt;read_data&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='nv'&gt;Socket&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='nv'&gt;BodyLen&lt;/span&gt; &lt;span class='o'&gt;-&lt;/span&gt; &lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='nv'&gt;KeyLen&lt;/span&gt; &lt;span class='o'&gt;+&lt;/span&gt; &lt;span class='nv'&gt;ExtraLen&lt;/span&gt;&lt;span class='p'&gt;)),&lt;/span&gt;

    &lt;span class='c'&gt;% Dispatch the read data to a gen_server process.&lt;/span&gt;
    &lt;span class='nv'&gt;Res&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='nn'&gt;gen_server&lt;/span&gt;&lt;span class='p'&gt;:&lt;/span&gt;&lt;span class='n'&gt;call&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='nv'&gt;StorageServer&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt;
                          &lt;span class='p'&gt;{&lt;/span&gt;&lt;span class='nv'&gt;OpCode&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='nv'&gt;Extra&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='nv'&gt;Key&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='nv'&gt;Body&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='nv'&gt;CAS&lt;/span&gt;&lt;span class='p'&gt;}),&lt;/span&gt;

    &lt;span class='n'&gt;respond&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='nv'&gt;Socket&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='nv'&gt;OpCode&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='nv'&gt;Opaque&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='nv'&gt;Res&lt;/span&gt;&lt;span class='p'&gt;).&lt;/span&gt;
&lt;/pre&gt;
&lt;/div&gt;
&lt;h3 id='the_storage_server'&gt;The Storage Server&lt;/h3&gt;

&lt;p&gt;A storage server process is a &lt;code&gt;gen_server&lt;/code&gt; implementation whose &lt;code&gt;handle_call&lt;/code&gt; implementations look will take a tuple of &lt;code&gt;{OpCode,
ExtraHeader, Key, Value, CAS}&lt;/code&gt; and return a &lt;code&gt;mc_response&lt;/code&gt; record.&lt;/p&gt;

&lt;p&gt;For an example storage server, consider my two &lt;code&gt;flush&lt;/code&gt; implementations in my &lt;a href='http://github.com/dustin/ememcached/blob/master/src/mc_handler_hashtable.erl'&gt;hashtable store&lt;/a&gt; (noting that flash has one 32-bit integer in extra header, no key, and no value):&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;span class='c'&gt;% Immediate flush. Ignore the current state and make a new one.&lt;/span&gt;
&lt;span class='nf'&gt;handle_call&lt;/span&gt;&lt;span class='p'&gt;({&lt;/span&gt;&lt;span class='no'&gt;?FLUSH&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='o'&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class='mi'&gt;0&lt;/span&gt;&lt;span class='p'&gt;:&lt;/span&gt;&lt;span class='mi'&gt;32&lt;/span&gt;&lt;span class='o'&gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='o'&gt;&amp;lt;&amp;lt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='o'&gt;&amp;lt;&amp;lt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='p'&gt;_&lt;/span&gt;&lt;span class='nv'&gt;CAS&lt;/span&gt;&lt;span class='p'&gt;},&lt;/span&gt;
            &lt;span class='p'&gt;_&lt;/span&gt;&lt;span class='nv'&gt;From&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='p'&gt;_&lt;/span&gt;&lt;span class='nv'&gt;State&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt; &lt;span class='o'&gt;-&amp;gt;&lt;/span&gt;
    &lt;span class='p'&gt;{&lt;/span&gt;&lt;span class='n'&gt;reply&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='nl'&gt;#mc_response&lt;/span&gt;&lt;span class='p'&gt;{},&lt;/span&gt; &lt;span class='nl'&gt;#mc_state&lt;/span&gt;&lt;span class='p'&gt;{}};&lt;/span&gt;
&lt;span class='c'&gt;% Delayed flush. Keep the current state and schedule a&lt;/span&gt;
&lt;span class='c'&gt;% flush to occur (via handle_info) in the future.&lt;/span&gt;
&lt;span class='nf'&gt;handle_call&lt;/span&gt;&lt;span class='p'&gt;({&lt;/span&gt;&lt;span class='no'&gt;?FLUSH&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='o'&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class='nv'&gt;Delay&lt;/span&gt;&lt;span class='p'&gt;:&lt;/span&gt;&lt;span class='mi'&gt;32&lt;/span&gt;&lt;span class='o'&gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='o'&gt;&amp;lt;&amp;lt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='o'&gt;&amp;lt;&amp;lt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='p'&gt;_&lt;/span&gt;&lt;span class='nv'&gt;CAS&lt;/span&gt;&lt;span class='p'&gt;},&lt;/span&gt;
            &lt;span class='p'&gt;_&lt;/span&gt;&lt;span class='nv'&gt;From&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='nv'&gt;State&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt; &lt;span class='o'&gt;-&amp;gt;&lt;/span&gt;
    &lt;span class='nn'&gt;erlang&lt;/span&gt;&lt;span class='p'&gt;:&lt;/span&gt;&lt;span class='nb'&gt;send_after&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='nv'&gt;Delay&lt;/span&gt; &lt;span class='o'&gt;*&lt;/span&gt; &lt;span class='mi'&gt;1000&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='n'&gt;self&lt;/span&gt;&lt;span class='p'&gt;(),&lt;/span&gt; &lt;span class='n'&gt;flush&lt;/span&gt;&lt;span class='p'&gt;),&lt;/span&gt;
    &lt;span class='p'&gt;{&lt;/span&gt;&lt;span class='n'&gt;reply&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='nl'&gt;#mc_response&lt;/span&gt;&lt;span class='p'&gt;{},&lt;/span&gt; &lt;span class='nv'&gt;State&lt;/span&gt;&lt;span class='p'&gt;};&lt;/span&gt;
&lt;span class='c'&gt;% More stuff Follows&lt;/span&gt;
&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;That&amp;#8217;s pretty much it. Even if nobody uses this code, it&amp;#8217;s useful to me as a protocol reference since it&amp;#8217;s easier to read than even the &lt;a href='http://cloud.github.com/downloads/memcached/memcached/protocol-binary.txt'&gt;binary specification&lt;/a&gt;.&lt;/p&gt;</content>
 </entry>
 
 <entry>
   <title>spymemcached Optimizations</title>
   <link href="http://dustin.github.com/2009/09/23/spymemcached-optimizations.html"/>
   <updated>2009-09-23T00:00:00-07:00</updated>
   <id>http://dustin.github.com/2009/09/23/spymemcached-optimizations</id>
   <content type="html">&lt;p&gt;I got around to pushing out a new RC of &lt;a href='http://code.google.com/p/spymemcached/'&gt;spymemcached&lt;/a&gt; today. It&amp;#8217;s been a while, but I&amp;#8217;m glad I got around to it.&lt;/p&gt;

&lt;p&gt;The &lt;a href='http://groups.google.com/group/spymemcached/browse_thread/thread/9d93e5658e813c29'&gt;announcement&lt;/a&gt; has the release notes (also in the tag), but there is a particular optimization I&amp;#8217;ve been thinking about for a while, and would like to go over here somewhere below.&lt;/p&gt;

&lt;p&gt;But first, I&amp;#8217;ll frame it with a bit of memcached protocol fundamentals.&lt;/p&gt;

&lt;h2 id='introduction_to_quiet_operations'&gt;Introduction to Quiet Operations&lt;/h2&gt;
&lt;div&gt;
  &lt;img class='floatleft' src='/images/memcached-sparse-get.png' alt='Ask, don&amp;apos;t tell.' /&gt;
&lt;/div&gt;
&lt;p&gt;Back when we were initially designing the binary protocol, we were considering how we&amp;#8217;d handle the multi-gets. We went through several proposals until we realized that the actual essense of multi-get model was really just a class of operation that allowed us to infer some of the responses.&lt;/p&gt;

&lt;p&gt;The above diagram shows a simple case of a multi-get. We ask for the values behind four keys. The server sends us responses for two of those keys and then says it&amp;#8217;s done. In a client, we can safely assume that it just didn&amp;#8217;t have the other two. It doesn&amp;#8217;t need to actually tell us that.&lt;/p&gt;

&lt;p&gt;So in the binary protocol model, we just made a type of command that didn&amp;#8217;t respond with &amp;#8220;uninteresting responses.&amp;#8221; It&amp;#8217;s easy to see how in a &lt;code&gt;get&lt;/code&gt; operation, the &lt;code&gt;not found&lt;/code&gt; response is uninteresting as we can infer it.&lt;/p&gt;

&lt;h2 id='other_quiet_operations'&gt;Other Quiet Operations&lt;/h2&gt;

&lt;p&gt;Shortly before the actual 1.4.0 release of memcached, we defined semantics for all &amp;#8220;quiet&amp;#8221; operations in such a way that allowed us to maximize efficiency without compromising correctness.&lt;/p&gt;

&lt;p&gt;The &lt;a href='http://code.google.com/p/memcached/wiki/MemcacheBinaryProtocol'&gt;binary protocol definition&lt;/a&gt; goes through these in tremendous detail, but those familiar with the Unix philosophy will probably find such things intuitive.&lt;/p&gt;

&lt;p&gt;For example, in Unix, the &lt;code&gt;rm&lt;/code&gt; command does not print out any output on success. If it completes and didn&amp;#8217;t say otherwise, you can assume it was successful.&lt;/p&gt;

&lt;p&gt;Similarly, a quiet &lt;code&gt;delete&lt;/code&gt; operation doesn&amp;#8217;t need to tell us that it successfully deleted something. That&amp;#8217;s its job. We want to know when it fails to do it.&lt;/p&gt;

&lt;h2 id='optimizing_with_a_quiet_set'&gt;Optimizing with a Quiet Set&lt;/h2&gt;
&lt;div&gt;
  &lt;img class='floatright' src='/images/multiset-perf.png' alt='Look. Faster!' /&gt;
&lt;/div&gt;
&lt;p&gt;The optimization I was interested in was making a multi-set type operation that worked similarly to the multi-get functionality. After struggling with what such an API might look like, I finally decided that the right thing to do is not change the API at all.&lt;/p&gt;

&lt;p&gt;Instead, I do something similar to &lt;a href='http://code.google.com/p/spymemcached/wiki/Optimizations'&gt;multiget escalation&lt;/a&gt; &amp;#8211; an optimization that&amp;#8217;s been part of &lt;a href='http://code.google.com/p/spymemcached/'&gt;spymemcached&lt;/a&gt; for a long time now. If many threads are pushing sets in (or even a single-thread since the typical use-case of set is async), the packetization of these commands escalates a sequence of similar commands into a single sparse operation working on all of the items together.&lt;/p&gt;

&lt;p&gt;While YMMV, my cache loader test ran consistently twice as fast.&lt;/p&gt;

&lt;p&gt;Previously, one million requests would require the client to process one million responses. Now, one million requests (assume none fail) will require the client to process one tiny response.&lt;/p&gt;

&lt;p&gt;If any &lt;em&gt;do&lt;/em&gt; fail, the respective callers will, of course, be notified. The ones that don&amp;#8217;t fail receive synthetic callbacks as the client infers their success.&lt;/p&gt;

&lt;h2 id='what_you_need_to_do'&gt;What You Need to Do&lt;/h2&gt;

&lt;p&gt;If you&amp;#8217;re using &lt;a href='http://code.google.com/p/spymemcached/'&gt;spymemcached&lt;/a&gt;, upgrade and you get the optimizations.&lt;/p&gt;

&lt;p&gt;If you&amp;#8217;re a client author, see how much better things are for your users as you make broader use of quiet operations of the binary protocol.&lt;/p&gt;</content>
 </entry>
 
 <entry>
   <title>Tornado on Twisted</title>
   <link href="http://dustin.github.com/2009/09/12/tornado.html"/>
   <updated>2009-09-12T00:00:00-07:00</updated>
   <id>http://dustin.github.com/2009/09/12/tornado</id>
   <content type="html">&lt;div&gt;
  &lt;a href='http://www.nataliedee.com/index.php?date=050906'&gt;&lt;img class='floatright' src='/images/snake-tornado.png' alt='Twisted Tornados' /&gt;&lt;/a&gt;
&lt;/div&gt;
&lt;p&gt;So what&amp;#8217;s this about &lt;a href='http://www.tornadoweb.org/'&gt;tornado&lt;/a&gt;?&lt;/p&gt;

&lt;p&gt;The &lt;a href='http://friendfeed.com/'&gt;friendfeed&lt;/a&gt; guys created an awesome web site with what was obviously (from the outside) quite awesome technology. A couple days ago, they released the &lt;a href='http://bret.appspot.com/entry/tornado-web-server'&gt;the technology&lt;/a&gt; behind the site.&lt;/p&gt;

&lt;h2 id='the_problems'&gt;The Problems&lt;/h2&gt;

&lt;p&gt;Tornado really is two different things:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;A great framework for building web sites.&lt;/li&gt;

&lt;li&gt;A low-level networking toolkit.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Most of us who use &lt;a href='http://twistedmatrix.com/'&gt;twisted&lt;/a&gt; were quite surprised to find out that rather than using twisted&amp;#8217;s awesome networking core, they reimplemented a bunch of it. Moreover, they kind of had really vague negative things to say about twisted. It&amp;#8217;s not clear what problem they had with it, but as stated the logic kind of fails:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Twisted doesn&amp;#8217;t have a good web framework.&lt;/li&gt;

&lt;li&gt;No major web sites run on twisted.&lt;/li&gt;

&lt;li&gt;So&amp;#8230; we&amp;#8217;re going to build something completely from scratch.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Now, I certainly build stuff from scratch unnecessarily all the time, and I also don&amp;#8217;t want to seem unthankful for the free gift brought to us from our friendfeed friends, but in its current state, it&amp;#8217;s a technological island.&lt;/p&gt;

&lt;p&gt;On one hand, you have low-level missing pieces. From the bottom, you&amp;#8217;ve got multiplexing implementations. twisted has been around a while, so it supports native multiplexors including those for select, poll, epoll, kqueue, CoreFoundation, wxPython, win32, gtk, glib, qt, and probably more. While some of them are less interesting than others, that&amp;#8217;s a lot of catching up to do. Some user already began adding &lt;a href='http://github.com/rphillips/tornado/commit/41aa49a8dcfe4f5fa91dfe1da9e05797d3397d25'&gt;kqueue support&lt;/a&gt;, but it&amp;#8217;s more of an example of the kinds of things that you don&amp;#8217;t get for free.&lt;/p&gt;

&lt;p&gt;At the high end, there is an incredible selection of protocol implementations from twisted you just can&amp;#8217;t use. If you&amp;#8217;re building a web site on an asynchronous framework, you don&amp;#8217;t ever want to block to get further data. So whatever your networking framework, you need it to have a sane way of asychronously communicating to all of your dependencies.&lt;/p&gt;

&lt;p&gt;They wrote an http client, though there are things I do with the twisted http client that I don&amp;#8217;t see a way to do with the tornado client such as &lt;a href='http://github.com/dustin/twitty-twister/blob/master/lib/twitter.py#L331'&gt;parse infinite xml streams&lt;/a&gt; from http responses.&lt;/p&gt;

&lt;h2 id='the_solution'&gt;The Solution&lt;/h2&gt;

&lt;p&gt;So instead of just generally being frustrated, I thought I&amp;#8217;d see what it&amp;#8217;d take to rip out the stuff that was reimplemented and use just core twisted.&lt;/p&gt;

&lt;p&gt;The original tornado code doesn&amp;#8217;t have much in the way of tests, so it&amp;#8217;s currently in the &amp;#8220;appears to work&amp;#8221; state, but this is what got me there:&lt;/p&gt;

&lt;p&gt;&lt;img src='/images/tornado-diffstat.png' alt='Diffstat' /&gt;&lt;/p&gt;

&lt;p&gt;That is, with an offset of -1,297 lines of code, it can be observed to work for the cases I tried, although I&amp;#8217;m sure there are still lines that need to be deleted before everything works.&lt;/p&gt;

&lt;p&gt;The good news is that this means that you get all the richness of twisted and the good parts of tornado combined.&lt;/p&gt;

&lt;p&gt;Everybody wins.&lt;/p&gt;

&lt;p&gt;So go grab &lt;a href='http://github.com/dustin/tornado'&gt;the code&lt;/a&gt; and see if it works in your app, or send me some patches for parts you were able to explore a bit more deeply.&lt;/p&gt;</content>
 </entry>
 
 <entry>
   <title>Buildbot and Git Repositories</title>
   <link href="http://dustin.github.com/2009/09/06/buildbot-git.html"/>
   <updated>2009-09-06T00:00:00-07:00</updated>
   <id>http://dustin.github.com/2009/09/06/buildbot-git</id>
   <content type="html">&lt;h1 id='buildbot_and_git_repositories'&gt;Buildbot and Git Repositories&lt;/h1&gt;
&lt;div&gt;
  &lt;img class='floatright' src='/images/RefuseToApologize.png' alt='I refuse to apologize.' /&gt;
&lt;/div&gt;
&lt;p&gt;In a recent conversation with the GitHub guys, I was talking about how my &lt;a href='http://buildbot.net/'&gt;buildbot&lt;/a&gt; setup was hitting GitHub and how a recent filesystem glitch of theirs caused my screen to turn red with &lt;a href='http://growl.info/'&gt;growl&lt;/a&gt; alerts from &lt;a href='http://code.google.com/p/buildwatch/'&gt;buildwatch&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The response was a tongue-in-cheek &amp;#8220;I refuse to apologize.&amp;#8221;&lt;/p&gt;

&lt;p&gt;The thing is, that response is absolutely the right one. This is distributed revision control. Why did I have a screen full of growl alerts because of a failure of a filesystem completely unrelated to what I was doing?&lt;/p&gt;

&lt;p&gt;I was relying on GitHub to be highly and quickly available to my seventeen (and growing in number) buildbot slaves for this project.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Most&lt;/em&gt; of the time, there&amp;#8217;s nothing for them to actually get from a centralized revision control &amp;#8211; quite simply, they were asking for information that they had cryptographically verifiable assurance that they already had.&lt;/p&gt;

&lt;p&gt;Today I made a &lt;a href='http://github.com/dustin/buildbot/commit/fabad2476cebc077d58c9293ce389d465648b019'&gt;small change&lt;/a&gt; to buildbot that prevents the slaves from ever talking to any network service to pick out a reference version in our most common use cases, thus realigning myself with the thing that initially sold me on GitHub&amp;#8217;s service: It enhances collaboration without causing me to be dependent on the service.&lt;/p&gt;

&lt;p&gt;For this failure, I am thankful. This new code will always be faster and more reliable for the common case even when GitHub works absolutely flawlessly.&lt;/p&gt;

&lt;h2 id='see_also'&gt;See Also&lt;/h2&gt;

&lt;p&gt;&lt;a href='http://ozmm.org/posts/when_github_goes_down.html'&gt;When GitHub Goes Down&lt;/a&gt;.&lt;/p&gt;</content>
 </entry>
 
 <entry>
   <title>Memcached 1.4.0</title>
   <link href="http://dustin.github.com/2009/07/16/memcached-1.4.html"/>
   <updated>2009-07-16T00:00:00-07:00</updated>
   <id>http://dustin.github.com/2009/07/16/memcached-1.4</id>
   <content type="html">&lt;h1 id='memcached_140'&gt;Memcached 1.4.0&lt;/h1&gt;
&lt;div&gt;
  &lt;img class='floatright' src='/images/cream-med.png' alt='c.r.e.a.m' /&gt;
&lt;/div&gt;
&lt;p&gt;I&amp;#8217;m a bit late to the blogging party here, but we finally released memcached 1.4.0. Check out &lt;a href='http://code.google.com/p/memcached/wiki/ReleaseNotes140'&gt;the release notes&lt;/a&gt; for more details.&lt;/p&gt;

&lt;p&gt;The release notes cover quite a lot of the interesting stuff, but they don&amp;#8217;t properly reflect the time and effort that went into making this all happen.&lt;/p&gt;

&lt;p&gt;There are a lot of bug fixes, as one might expect after some time. A lot of testing has shown that performance is better pretty much all around, but very few people have ever seen memcached be a performance bottleneck in their applications, so that&amp;#8217;s not too exciting.&lt;/p&gt;

&lt;p&gt;The biggest part of this release, however, is something I&amp;#8217;ve been working on for about two years: &lt;a href='http://cloud.github.com/downloads/memcached/memcached/protocol-binary.txt'&gt;the binary protocol&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id='the_binary_protocol'&gt;The Binary Protocol&lt;/h2&gt;

&lt;p&gt;So what&amp;#8217;s the big deal about the binary protocol?&lt;/p&gt;

&lt;p&gt;The most obvious thing for protocol implementors is that it&amp;#8217;s now &lt;em&gt;really&lt;/em&gt; easy to parse the protocol. After reading a fixed-size header, a low-level packet processor can figure out where to dispatch the input and split it into all of its major components (key, value, opaque, cas, extras, etc&amp;#8230;).&lt;/p&gt;

&lt;p&gt;That&amp;#8217;s great for the (small) number of developers who write servers and clients, but what about random people out there who just want to use memcached? Semantic enhancements in the new protocol allow us to build some really cool stuff.&lt;/p&gt;

&lt;p&gt;The first example of such a thing is Trond&amp;#8217;s &lt;a href='http://blogs.sun.com/trond/date/20090625'&gt;replication&lt;/a&gt; functionality for &lt;a href='http://launchpad.net/libmemcached'&gt;libmemcached&lt;/a&gt;. We now have a clean fire-and-mostly-forget protocol semantics that allows for improvements like efficient client-side replication. It also makes it safe to make bulk-loaders (even with CAS).&lt;/p&gt;

&lt;h2 id='go_try_it'&gt;Go Try It&lt;/h2&gt;

&lt;p&gt;We&amp;#8217;ve run tons of tests, others have run tests, there&amp;#8217;ve been various deployments large and small, but if you&amp;#8217;re running something older, it&amp;#8217;s your turn.&lt;/p&gt;

&lt;p&gt;We work hard to make sure that the development versions work on all platforms we can find anyone to care about. Each change is built and tested on all supported platforms before the change is accepted into our master branch.&lt;/p&gt;

&lt;p&gt;Do note that 1.4.0 has some build issues on OpenBSD, but someone graciously donated a builder to our buildbot farm so they&amp;#8217;re all cleared up for 1.4.1 (which is planned for later this month).&lt;/p&gt;

&lt;p&gt;In the meantime, there are several ways to pick it up:&lt;/p&gt;

&lt;h3 id='packages'&gt;Packages&lt;/h3&gt;

&lt;p&gt;Package systems are slow to pick up&amp;#8230; anything it seems. If your system&amp;#8217;s package manager is shipping memcached 1.4.0, please let me know.&lt;/p&gt;

&lt;p&gt;In the meantime&amp;#8230;&lt;/p&gt;

&lt;h3 id='use_the_source'&gt;Use the Source&lt;/h3&gt;

&lt;p&gt;You can download &lt;a href='http://memcached.googlecode.com/files/memcached-1.4.0.tar.gz'&gt;the source distribution&lt;/a&gt; from the google code &lt;a href='http://code.google.com/p/memcached/downloads/list'&gt;download site&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Building and installing is quite simple:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;./configure
sudo make install&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Then just run &lt;code&gt;/usr/local/bin/memcached&lt;/code&gt; whichever way suits your fancy. Personally, I like upstart on Linux, launchd on OS X, smf on Solaris, etc&amp;#8230;&lt;/p&gt;

&lt;h3 id='deploying_on_amazon_ec2aws'&gt;Deploying on Amazon EC2/AWS?&lt;/h3&gt;

&lt;p&gt;I&amp;#8217;ve put together some Amazon AMIs that are production ready and ridiculously simple to get going.&lt;/p&gt;

&lt;p&gt;Each AMI allocates all but 512MB of RAM on the system to memcached and just starts up happy and running. These images are based on Ubuntu 9.04 and have an upstart config for the actual daemon execution so if we somehow have some kind of crashing bug, they&amp;#8217;ll automatically and instantly restart.&lt;/p&gt;

&lt;p&gt;Depending on your needs, you can select one of the following:&lt;/p&gt;

&lt;h4 id='us'&gt;US&lt;/h4&gt;

&lt;p&gt;I&amp;#8217;ve assembled a 32-bit AMI (&lt;code&gt;ami-39c52450&lt;/code&gt;) for small instances, and a 64-bit AMI (&lt;code&gt;ami-1fc52476&lt;/code&gt;) for large instances. They show up as the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;ami-39c52450 - northscale/community-memcached-1.4.0-i386.manifest.xml&lt;/li&gt;

&lt;li&gt;ami-1fc52476 - northscale/community-memcached-1.4.0-x86_64.manifest.xml&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For example, using the ec2 command-line tools can start an extra large 64-bit instance with the following invocation:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;ec2-run-instances ami-1fc52476 --instance-type m1.xlarge&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;After this instance comes up, you&amp;#8217;ll find memcached 1.4.0 listening on port 11211 with about 15GB of RAM at your disposal.&lt;/p&gt;

&lt;p&gt;No maintenance should be necessary, but &lt;code&gt;ec2-run-instance&lt;/code&gt;&amp;#8217;s &lt;code&gt;-k&lt;/code&gt; parameter for supplying a root ssh key is still honored in case you want to still look around.&lt;/p&gt;

&lt;h4 id='eu'&gt;EU&lt;/h4&gt;

&lt;p&gt;There are European versions of the same images as &lt;code&gt;ami-818ba3f5&lt;/code&gt; for 32-bit and &lt;code&gt;ami-838ba3f7&lt;/code&gt; for 64-bit.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;ami-818ba3f5 - northscale-eu/community-memcached-1.4.0-i386.manifest.xml&lt;/li&gt;

&lt;li&gt;ami-838ba3f7 - northscale-eu/community-memcached-1.4.0-x86_64.manifest.xml&lt;/li&gt;
&lt;/ul&gt;</content>
 </entry>
 
 <entry>
   <title>Project Skyscraper</title>
   <link href="http://dustin.github.com/2009/07/11/skyscraper.html"/>
   <updated>2009-07-11T00:00:00-07:00</updated>
   <id>http://dustin.github.com/2009/07/11/skyscraper</id>
   <content type="html">&lt;h1 id='project_skyscraper'&gt;Project Skyscraper&lt;/h1&gt;

&lt;p&gt;It occurred to me that there&amp;#8217;s a lot of value in building xmpp services &amp;#8211; much like web services, but using existing connections and xmpp instead of http.&lt;/p&gt;

&lt;p&gt;In collaboration with &lt;a href='http://github.com/ga2arch'&gt;ga2arch&lt;/a&gt;, I launched an xmpp service called skyscraper.im. This has actually been running for a while now, but I&amp;#8217;ve been too caught up in writing code to write anything about code.&lt;/p&gt;

&lt;h2 id='translateskyscraperim'&gt;translate.skyscraper.im&lt;/h2&gt;
&lt;div&gt;
  &lt;img class='floatright' src='/images/skyscraper.png' alt='skyscraper' /&gt;
&lt;/div&gt;
&lt;p&gt;The first part of this service is an xmpp &lt;a href='http://xmpp.org/extensions/xep-0050.html'&gt;adhoc&lt;/a&gt; interface to google translate. It actually does support IM, but that&amp;#8217;s incidental, the real value is in the adhoc interface.&lt;/p&gt;

&lt;p&gt;If you&amp;#8217;re unfamiliar with xmpp adhoc, you can think of it much like CGI, but using xmpp as a transport. You take a bunch of simple key/multi-value pairs and send them to a resource somewhere, and it sends you something back. The nice thing about xmpp, though, is that the mechanism for determining what things exist and what parameters they take are very programmatically accessible.&lt;/p&gt;

&lt;p&gt;You can discover available commands through &lt;code&gt;translate.skyscraper.im&lt;/code&gt; as shown in &lt;a href='http://www.vimeo.com/5558475'&gt;this video&lt;/a&gt;, but I&amp;#8217;ll just tell you what it&amp;#8217;ll tell you:&lt;/p&gt;

&lt;h3 id='the_input'&gt;The Input&lt;/h3&gt;

&lt;p&gt;There is one field called &lt;code&gt;in&lt;/code&gt; which is the input language in the form of a two-character language code. You may have only one of these (it is of type &lt;code&gt;list-single&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;There is one field called &lt;code&gt;out&lt;/code&gt; which is the output language and is also in the form of a two-character language code. You can have as many of these as you like (it is of type &lt;code&gt;list-multi&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;Finally there&amp;#8217;s a field called &lt;code&gt;text&lt;/code&gt; which is the stuff you want to translate. You may only have one of these.&lt;/p&gt;

&lt;h3 id='the_output'&gt;The Output&lt;/h3&gt;

&lt;p&gt;The response is a form much like the one you sent it, the keys are language codes and the values are the text translated in that language.&lt;/p&gt;

&lt;p&gt;Note that you will not receive more language translations than you asked for, but you may receive fewer in the case where the upstream translation service can&amp;#8217;t perform such a translation.&lt;/p&gt;

&lt;p&gt;The obvious benefit here over doing it yourself is that you get full translations all at the same time without having to do any kind of coordination as things are completing (i.e., I do that for you).&lt;/p&gt;

&lt;h2 id='conferenceskyscraperim'&gt;conference.skyscraper.im&lt;/h2&gt;
&lt;div&gt;
  &lt;img class='floatright' src='/images/skyscraper-chat.png' alt='skyscraper chat' /&gt;
&lt;/div&gt;
&lt;p&gt;A fun thing built atop the translate component is the skyscraper muc &amp;#8211; an xmpp multi-user chat with automatic translation.&lt;/p&gt;

&lt;p&gt;What this means is that you can have several people enter a room with no room in common, all speaking and reading their native language.&lt;/p&gt;

&lt;p&gt;Of course, the dream is limited by the translation service, but it &lt;em&gt;does&lt;/em&gt; work within the reasonable limits.&lt;/p&gt;

&lt;p&gt;If you&amp;#8217;d like to try it out, find a friend who speaks another language and both join a chat room at &lt;code&gt;conference.skyscraper.im&lt;/code&gt;. Start by each of you telling it your respective languages (e.g. &lt;code&gt;/lang en&lt;/code&gt;) and then talk.&lt;/p&gt;

&lt;p&gt;I&amp;#8217;ve spent very little time on this, so I imagine it falls apart in all kinds of places, but it was &lt;em&gt;really&lt;/em&gt; easy to get going with the translate service from above, and as it&amp;#8217;s an xmpp server component, it does all this with just one file descriptor and the necessary state to keep up with who&amp;#8217;s in what room and what translations are outstanding.&lt;/p&gt;</content>
 </entry>
 
 <entry>
   <title>My Github Anniversary (Sort Of)</title>
   <link href="http://dustin.github.com/2009/03/01/github-anniversary.html"/>
   <updated>2009-03-01T00:00:00-08:00</updated>
   <id>http://dustin.github.com/2009/03/01/github-anniversary</id>
   <content type="html">&lt;h1 id='my_github_anniversary_sort_of'&gt;My Github Anniversary (Sort Of)&lt;/h1&gt;
&lt;div&gt;
  &lt;img class='floatright' src='http://img.skitch.com/20090301-jqf9yrkfniqf2ysaa88ebshwqe.png' alt='[anniversary]' /&gt;
&lt;/div&gt;
&lt;p&gt;I joined &lt;a href='http://github.com/'&gt;github&lt;/a&gt; about a year ago today. Kind of. It was actually February 29th, but there isn&amp;#8217;t one of those this year, so I&amp;#8217;m going to have to wait a few more years before I can properly have an anniversary.&lt;/p&gt;

&lt;p&gt;Between the time I joined and the time I typed this line, I&amp;#8217;ve generated 206 pages of activity (&lt;a href='http://github.com/dustin?page=174'&gt;174 pages public&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;By the end of my first day, I had migrated two &lt;a href='http://github.com/dustin/java-memcached-client'&gt;java&lt;/a&gt; &lt;a href='http://github.com/dustin/photo'&gt;projects&lt;/a&gt; over from mercurial, converted a &lt;a href='http://github.com/dustin/ruby-freebase'&gt;ruby project&lt;/a&gt; from subversion, had that repo forked, watched another &lt;a href='http://github.com/mojombo/god'&gt;ruby project&lt;/a&gt;, started a &lt;a href='http://github.com/dustin/buildwatch'&gt;new objective c project&lt;/a&gt;, wrote some new code for some of my projects and pushed it and invited a &lt;a href='http://github.com/chriseppstein'&gt;couple&lt;/a&gt; of &lt;a href='http://github.com/verbal'&gt;friends&lt;/a&gt; (both of whom now share my leap-year-only start date) and added them to a couple of my projects.&lt;/p&gt;

&lt;p&gt;&lt;a href='http://github.com/chriseppstein'&gt;Chris&lt;/a&gt; became somewhat a github evangelist and made some really &lt;a href='http://github.com/chriseppstein/compass'&gt;cool&lt;/a&gt; &lt;a href='http://github.com/chriseppstein/freebase'&gt;stuff&lt;/a&gt; stuff there (some if it&amp;#8217;s cooler than most people can comprehend). &lt;a href='http://github.com/verbal'&gt;Ian&lt;/a&gt; throws awesome parties (I&amp;#8217;ll eventually make him give me code).&lt;/p&gt;

&lt;p&gt;At the point where I started using github, I&amp;#8217;d probably been a (somewhat casual) git user for about two weeks. git is great, but the documentation and tutorials were more about laying out an infinitely complex decision tree &amp;#8211; that is, git itself is easy to do anything with, but you can do a lot with it, so it comes across as unnecessarily complex.&lt;/p&gt;

&lt;p&gt;Github has been really good about making really common paths really easy so that you naturally fall into workflows that minimize the work required to contribute to open source projects down to the point where you can clone a repo, branch, edit some stuff, and notify the maintainer of a project in just a few clicks on the web site.&lt;/p&gt;

&lt;p&gt;Overall, it&amp;#8217;s been a pretty &lt;a href='http://calendaraboutnothing.com/~dustin'&gt;good year&lt;/a&gt;. Just three more until my real anniversary.&lt;/p&gt;</content>
 </entry>
 
 <entry>
   <title>Making Use of Caps Lock</title>
   <link href="http://dustin.github.com/2009/02/09/caps-lock.html"/>
   <updated>2009-02-09T00:00:00-08:00</updated>
   <id>http://dustin.github.com/2009/02/09/caps-lock</id>
   <content type="html">&lt;h1 id='making_use_of_caps_lock'&gt;Making Use of Caps Lock&lt;/h1&gt;
&lt;div&gt;
  &lt;img class='floatright' src='/images/capslock-pref.png' alt='caps lock' /&gt;
&lt;/div&gt;
&lt;p&gt;If you&amp;#8217;re like me (and who isn&amp;#8217;t), the caps lock key is an annoying waste of plastic. Its only value seems to be to type things to offend people. Luckily, most operating systems allow you to map it to control, or another useful key.&lt;/p&gt;

&lt;p&gt;As a fairly new emacs user (and a long-term shell user), having a control key near where my fingers already makes many things far more accessible to me. Highly recommended.&lt;/p&gt;

&lt;p&gt;But there&amp;#8217;s another thing that the caps lock provides that quickly moves from annoyance to useful feature:&lt;/p&gt;
&lt;div&gt;
  &lt;img class='floatleft' src='/images/capslock-key.jpg' alt='caps lock' /&gt;
&lt;/div&gt;
&lt;p&gt;Just about every keyboard ever made has a caps lock indicator. Such a wonderful thing when used correctly.&lt;/p&gt;

&lt;p&gt;Amit Singh over at google had a blog post about &lt;a href='http://googlemac.blogspot.com/2008/04/manipulating-keyboard-leds-through.html'&gt;manipulating keyboard LEDs&lt;/a&gt; which inspired me to add this feature to my &lt;a href='http://code.google.com/p/buildwatch/'&gt;buildwatch&lt;/a&gt; app pretty much immediately.&lt;/p&gt;

&lt;p&gt;Due to a fairly dumb bug I fixed today, it hasn&amp;#8217;t been working (and I wasn&amp;#8217;t paying attention to it anyway), but now, when anyone does a build against my build farm and the build breaks, my keyboard light will come on.&lt;/p&gt;

&lt;p&gt;Sort of makes me want to write some bad code.&lt;/p&gt;</content>
 </entry>
 
 <entry>
   <title>Buildbot</title>
   <link href="http://dustin.github.com/2009/01/30/buildbot.html"/>
   <updated>2009-01-30T00:00:00-08:00</updated>
   <id>http://dustin.github.com/2009/01/30/buildbot</id>
   <content type="html">&lt;h1 id='buildbot'&gt;Buildbot&lt;/h1&gt;

&lt;p&gt;I&amp;#8217;ve used a few different continous integration systems, but &lt;a href='http://buildbot.net/'&gt;buildbot&lt;/a&gt; has been my favorite for quite a while. It&amp;#8217;s got a really nice architecture, a great codebase, and all the tools I need.&lt;/p&gt;
&lt;div&gt;
  &lt;img class='floatright' src='/images/buildbot.png' alt='buildwatch' /&gt;
&lt;/div&gt;
&lt;p&gt;Most of these things seem to assume that if it builds anywhere, you&amp;#8217;ve done your job. buildbot assumes there may be a relatively large number of build workers and an even larger number of configurations.&lt;/p&gt;

&lt;p&gt;For example, I&amp;#8217;m building out a buildbot configuration for a project I&amp;#8217;m working on now for some portable software that seems to be most popular on Linux, possibly followed by OS X. However, depending on the distribution, toolchain, architecture, and compile-time options, some things just don&amp;#8217;t work correctly. I also use it on FreeBSD, but having added a slave for FreeBSD, I found a small compiler error.&lt;/p&gt;

&lt;h2 id='tbyb'&gt;TBYB&lt;/h2&gt;

&lt;p&gt;Most CI systems are all about telling you when you&amp;#8217;ve committed code that breaks the build for other people. Isn&amp;#8217;t it rather late by that point?&lt;/p&gt;

&lt;p&gt;buildbot has a &lt;code&gt;try&lt;/code&gt; command that allows you run a complete build across whichever nodes you want (or all) &lt;em&gt;before&lt;/em&gt; making your code available to anyone else.&lt;/p&gt;

&lt;p&gt;One of the guys I&amp;#8217;m working with on this project does most of his work in Solaris. He wrote some code, tested his code, and sent me a patch. I committed his patch to my local git repo, but before pushing it, ran &lt;code&gt;buildbot try&lt;/code&gt; to make sure nothing weird happened. There were two different problems that caused build and/or test failures on every OS that wasn&amp;#8217;t Solaris.&lt;/p&gt;

&lt;p&gt;I was able to fix up his changes so that they worked everywhere, and they never actually made it into a public tree in their broken form.&lt;/p&gt;

&lt;h2 id='the_code'&gt;The Code&lt;/h2&gt;

&lt;p&gt;buildbot&amp;#8217;s &lt;a href='http://github.com/djmitche/buildbot'&gt;codebase&lt;/a&gt; has some very robust plumbing, and it seems to support just about anything you might want to do (which other systems allow you subscribe to the tail of the current step&amp;#8217;s log in realtime without having access to the slave?).&lt;/p&gt;

&lt;p&gt;I&amp;#8217;ve had to make some changes to get some features working as I expect, or fixing bug in edge cases, though.&lt;/p&gt;

&lt;p&gt;I&amp;#8217;ve been doing some work lately with the &lt;code&gt;git&lt;/code&gt; support in &lt;code&gt;try&lt;/code&gt;. Rather than repeating myself, you can see what I&amp;#8217;ve done in my portion of the changelog and imagine how much better your project would be if every potential change could be tested across all your supported platforms before you publish.&lt;/p&gt;
&lt;pre style='font-size: smaller'&gt;
commit dfb18e6c177d490da9dcab29e431eff22cfedfec
Author: Dustin Sallings &amp;lt;dustin@spy.net&gt;
Date:   Wed Jan 28 20:26:00 2009 -0800

    Allow users to specify the remote git branch.

    This allows for a case where someone has a repository that tracks
    someone else's repository, has arbitrary local branches, but wants to
    run tries with the delta from the reference repository (i.e. the one
    the master knows about) to the local changes.

    Without this, it's likely the reference repository will not have the
    necessary objects to pull down a base revision to be able to apply
    patches for the try to succeed.

    This also ensures that the current client's view of the reference
    repository is honored.  That is, if the reference repository has moved
    forward, the trier's current tip of the remote is used to compute the
    delta, and that's sent along as the baserev.

commit 38a9c7fc719b44e2cdfa47884182da7128b369d2
Author: Dustin Sallings &amp;lt;dustin@spy.net&gt;
Date:   Wed Jan 28 16:30:08 2009 -0800

    Added --dry-run (-n) support to buildbot try.

    Need to be able to try try when I just want to know what it's even
    going to consider doing.

commit f43143835cba3ca5963e07874da17c1416a031c2
Author: Dustin Sallings &amp;lt;dustin@spy.net&gt;
Date:   Wed Jan 28 08:34:04 2009 -0800

    Refactored try buildName validation for reuse.

commit a88238cae5000c3481877aa354e3c76fc45770b8
Author: Dustin Sallings &amp;lt;dustin@spy.net&gt;
Date:   Wed Jan 28 08:25:52 2009 -0800

    Don't require a list of builders for buildbot try.

    This maintains the current restrictions around builder lists that
    prevent one from trying a build that isn't in the list, but allows the
    user to delegate the selection to the server by not listing the
    builders at all.

    I want my users to always try their builds on every build
    configuration, but I don't want to be sending out buildbot options all
    the time.
commit 99240ada38677a143971fe390beb714c3017c20b
Author: Dustin Sallings &amp;lt;dustin@spy.net&gt;
Date:   Sun Jan 25 10:47:57 2009 -0800

    git_buildbot should show the author, not the committer

    When I'm looking at my waterfall, I'd like to see the names of the
    people who wrote code, not just mine because I happened to have
    cherry-picked or am'd a bunch of changes.

commit 2c6865d83e967ca135acd3810e08af2dfab727b3
Author: Dustin Sallings &amp;lt;dustin@spy.net&gt;
Date:   Wed Jan 21 20:23:35 2009 -0800

    Look at the remote tracking branch in git for buildbot try.

    This allows us to try committed, but not pushed code.

commit a079d84d4056dbf5ab3489cb7f2f8f0e20d91b87
Author: Dustin Sallings &amp;lt;dustin@spy.net&gt;
Date:   Thu Jan 22 09:59:59 2009 -0800

    Try to reclobber on retry.

    On a failed git update in clobber mode, I was getting the following
    error on the second try:

    exceptions.OSError: [Errno 17] File exists: '/path/to/build'

    It seems that the clobber only occurs once, and any error that happens
    during the checkout should redo the clobber.

commit 6c36579a63b58bc986ec56e0272362038be08112
Author: Dustin Sallings &amp;lt;dustin@spy.net&gt;
Date:   Wed Nov 19 04:56:38 2008 +0800

    Get rid of git- commands in git_buildbot.

    Signed-off-by: Dustin J. Mitchell &amp;lt;dustin@zmanda.com&gt;

commit ac70a83fa05c2b1b31dd9411ffc28876fb9e9f20
Author: Dustin Sallings &amp;lt;dustin@spy.net&gt;
Date:   Sat Apr 19 08:40:20 2008 +0800

    Send merge changes from git.

    Signed-off-by: Dustin J. Mitchell &amp;lt;dustin@zmanda.com&gt;
&lt;/pre&gt;</content>
 </entry>
 
 <entry>
   <title>Publishing Changelogs</title>
   <link href="http://dustin.github.com/2009/01/17/changelog.html"/>
   <updated>2009-01-17T00:00:00-08:00</updated>
   <id>http://dustin.github.com/2009/01/17/changelog</id>
   <content type="html">&lt;h1 id='publishing_changelogs'&gt;Publishing Changelogs&lt;/h1&gt;

&lt;p&gt;A user filed a bug against my &lt;a href='http://code.google.com/p/spymemcached/'&gt;memcached client&lt;/a&gt; because he couldn&amp;#8217;t find the changelog and wanted to know what went into the new version.&lt;/p&gt;

&lt;p&gt;I have a decent structure around releases, especially with this project. I tag it and write a good summary of changes in the tag including an abbreviated shortlog output, then I send the same out to the mailing list.&lt;/p&gt;

&lt;p&gt;Somehow, I expected anyone not on the mailing list to just dig through my tags to find out what&amp;#8217;s changed. I suppose that&amp;#8217;s asking quite a bit.&lt;/p&gt;

&lt;p&gt;Since I&amp;#8217;ve been keeping good information in my tags since moving over to git (which actually has proper tag objects), I&amp;#8217;ve found it quite easy to automate this process. My new &lt;a href='http://github.com/dustin/bindir/blob/master/git-htmlchangelog'&gt;git htmlchangelog&lt;/a&gt; takes a list of tags and generates a reasonable changelog automatically from this.&lt;/p&gt;

&lt;p&gt;For example, the following command:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;git htmlchangelog `git tag | egrep -v pre\|rc` &amp;gt; changelog.html&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;created &lt;a href='http://dustin.github.com/java-memcached-client/changelog.html'&gt;the changelog&lt;/a&gt; for my memcached client.&lt;/p&gt;</content>
 </entry>
 
 <entry>
   <title>Visualizing Git Contributors</title>
   <link href="http://dustin.github.com/2009/01/16/visualizing-contributors.html"/>
   <updated>2009-01-16T00:00:00-08:00</updated>
   <id>http://dustin.github.com/2009/01/16/visualizing-contributors</id>
   <content type="html">&lt;p&gt;I was looking for something quick to do today, so I started drawing &lt;a href='http://github.com/dustin/bindir/blob/master/git-contributors'&gt;pie charts&lt;/a&gt; showing who commits to various projects.&lt;/p&gt;

&lt;p&gt;Pie charts are particularly terrible for communicating something useful to people, but they kind of look nice, so whatever.&lt;/p&gt;

&lt;p&gt;Here are some examples:&lt;/p&gt;

&lt;h2 id='linux'&gt;Linux&lt;/h2&gt;

&lt;p&gt;&lt;img src='http://chart.apis.google.com/chart?cht=p&amp;amp;chs=600x300&amp;amp;chd=s:CBBBBB2&amp;amp;chl=Linus|Al|David|Adrian|Ralf|Jeff|Other' alt='Linux' /&gt;&lt;/p&gt;

&lt;h2 id='git'&gt;Git&lt;/h2&gt;

&lt;p&gt;&lt;img src='http://chart.apis.google.com/chart?cht=p&amp;amp;chs=600x300&amp;amp;chd=s:WFECBBa&amp;amp;chl=Junio|Shawn|Linus|Johannes|Eric|Jakub|Other' alt='Git' /&gt;&lt;/p&gt;

&lt;h2 id='memcached'&gt;Memcached&lt;/h2&gt;

&lt;p&gt;&lt;img src='http://chart.apis.google.com/chart?cht=p&amp;amp;chs=600x300&amp;amp;chd=s:THGGEEP&amp;amp;chl=Brad|dormando|Paul|Dustin|Trond|Toru|Other' alt='Memcached' /&gt;&lt;/p&gt;

&lt;h2 id='emacs'&gt;Emacs&lt;/h2&gt;

&lt;p&gt;&lt;img src='http://chart.apis.google.com/chart?cht=p&amp;amp;chs=600x300&amp;amp;chd=s:OEEDDDe&amp;amp;chl=Richard|Gerd|Eli|Stefan|Kenichi|Glenn|Other' alt='Emacs' /&gt;&lt;/p&gt;

&lt;h2 id='rails'&gt;Rails&lt;/h2&gt;

&lt;p&gt;&lt;img src='http://chart.apis.google.com/chart?cht=p&amp;amp;chs=600x300&amp;amp;chd=s:VPDDCCO&amp;amp;chl=David|Jeremy|Michael|Rick|Jamis|Joshua|Other' alt='Rails' /&gt;&lt;/p&gt;</content>
 </entry>
 
 <entry>
   <title>Git Timecard</title>
   <link href="http://dustin.github.com/2009/01/11/timecard.html"/>
   <updated>2009-01-11T00:00:00-08:00</updated>
   <id>http://dustin.github.com/2009/01/11/timecard</id>
   <content type="html">&lt;h1 id='git_timecard'&gt;Git Timecard&lt;/h1&gt;

&lt;p&gt;I really like &lt;a href='http://github.com/blog/159-one-more-thing'&gt;github&amp;#8217;s punch card&lt;/a&gt; feature. It&amp;#8217;s a nice way to quickly see when a project is worked on.&lt;/p&gt;

&lt;p&gt;However, it&amp;#8217;s very limited. You can only see everyone&amp;#8217;s work on the master branch. I have &lt;em&gt;lots&lt;/em&gt; of other ways I want to look at my repos (including some that aren&amp;#8217;t on github).&lt;/p&gt;

&lt;p&gt;So I wrote &lt;a href='http://github.com/dustin/bindir/tree/master/git-timecard'&gt;my own&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Here are the neat kinds of things I can do now:&lt;/p&gt;

&lt;h2 id='punch_card_of_my_work_repo'&gt;Punch Card of my Work Repo&lt;/h2&gt;

&lt;pre&gt;&lt;code&gt;~/work-project/ % git timecard&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;&lt;img src='http://chart.apis.google.com/chart?cht=s&amp;amp;chs=800x300&amp;amp;chd=e:CkFIHrKPMzPXR7UeXCZmcKeuhRj1mZo9rhuEwozM1w4U639bCkFIHrKPMzPXR7UeXCZmcKeuhRj1mZo9rhuEwozM1w4U639bCkFIHrKPMzPXR7UeXCZmcKeuhRj1mZo9rhuEwozM1w4U639bCkFIHrKPMzPXR7UeXCZmcKeuhRj1mZo9rhuEwozM1w4U639bCkFIHrKPMzPXR7UeXCZmcKeuhRj1mZo9rhuEwozM1w4U639bCkFIHrKPMzPXR7UeXCZmcKeuhRj1mZo9rhuEwozM1w4U639bCkFIHrKPMzPXR7UeXCZmcKeuhRj1mZo9rhuEwozM1w4U639bCkFIHrKPMzPXR7UeXCZmcKeuhRj1mZo9rhuEwozM1w4U639b,IAIAIAIAIAIAIAIAIAIAIAIAIAIAIAIAIAIAIAIAIAIAIAIAQAQAQAQAQAQAQAQAQAQAQAQAQAQAQAQAQAQAQAQAQAQAQAQAYAYAYAYAYAYAYAYAYAYAYAYAYAYAYAYAYAYAYAYAYAYAYAYAgAgAgAgAgAgAgAgAgAgAgAgAgAgAgAgAgAgAgAgAgAgAgAgAn.n.n.n.n.n.n.n.n.n.n.n.n.n.n.n.n.n.n.n.n.n.n.n.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.3.3.3.3.3.3.3.3.3.3.3.3.3.3.3.3.3.3.3.3.3.3.3.3.................................................,BoAeAAAAAAAAAAAtDQDtGuE4IWMhIlJ-GfMECyGuH4L1L1KNFGGRAPAeAAAeBZCGLmeYgs1GjeiyijnavTbmMEHqIHG9IHGuELB3APAAAAAPAtCjGfcwdrpCZ-fTrXzP8SozSUFzGRCyI0H4CGB3AeAeAeB3AeCGEpYlkLjegdgsv.59..okTBJ-JvHMMhJvD8DfB3AAAAAAAADfHqabX4rIiFkLpRze28drVVQsLXI0J-JREpCUAAAAAPAAAACyGRVkchrXh2jPtc4kw7WCGfDBBoCGCjDtAPBKAAAAAAAAAABoBKD8GCHqGRIHGRGCIHDBGRELBoAeDQCjAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA&amp;amp;chxt=x,y&amp;amp;chxl=0:||0|1|2|3|4|5|6|7|8|9|10|11|12|13|14|15|16|17|18|19|20|21|22|23||1:||Sun|Mon|Tue|Wed|Thu|Fri|Sat|&amp;amp;chm=o,333333,1,1.0,25,0&amp;amp;chds=-1,24,-1,7,0,20' alt='work' /&gt;&lt;/p&gt;

&lt;h2 id='alternate_branch'&gt;Alternate Branch&lt;/h2&gt;

&lt;pre&gt;&lt;code&gt;~/memcached % git timecard rewritten-bin&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;&lt;img src='http://chart.apis.google.com/chart?cht=s&amp;amp;chs=800x300&amp;amp;chd=e:CkFIHrKPMzPXR7UeXCZmcKeuhRj1mZo9rhuEwozM1w4U639bCkFIHrKPMzPXR7UeXCZmcKeuhRj1mZo9rhuEwozM1w4U639bCkFIHrKPMzPXR7UeXCZmcKeuhRj1mZo9rhuEwozM1w4U639bCkFIHrKPMzPXR7UeXCZmcKeuhRj1mZo9rhuEwozM1w4U639bCkFIHrKPMzPXR7UeXCZmcKeuhRj1mZo9rhuEwozM1w4U639bCkFIHrKPMzPXR7UeXCZmcKeuhRj1mZo9rhuEwozM1w4U639bCkFIHrKPMzPXR7UeXCZmcKeuhRj1mZo9rhuEwozM1w4U639bCkFIHrKPMzPXR7UeXCZmcKeuhRj1mZo9rhuEwozM1w4U639b,IAIAIAIAIAIAIAIAIAIAIAIAIAIAIAIAIAIAIAIAIAIAIAIAQAQAQAQAQAQAQAQAQAQAQAQAQAQAQAQAQAQAQAQAQAQAQAQAYAYAYAYAYAYAYAYAYAYAYAYAYAYAYAYAYAYAYAYAYAYAYAYAgAgAgAgAgAgAgAgAgAgAgAgAgAgAgAgAgAgAgAgAgAgAgAgAn.n.n.n.n.n.n.n.n.n.n.n.n.n.n.n.n.n.n.n.n.n.n.n.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.3.3.3.3.3.3.3.3.3.3.3.3.3.3.3.3.3.3.3.3.3.3.3.3.................................................,AAMzIiERAAMzERAAERAAAAAAIiIiREREu7MzERmZMz..iI..qqVVMzIiREERAAERMzZmREERMzZmd3VVREzMMzqqIiMziIu7VVVVMzIiIiERAAERERMzd3VVVVIiREREIiREVVVVREMziIMzMzREMzREERIiAAAAAAIiERmZVVmZAAREVVIiIiREIiIiIiIiVVd3ERERIiERAAERIiIiVVAAIiREIiMzMzERMzAAVVMzAAZmERMzIiIiERAAERAAAAREVVVVMzd3MzMzIiAAREERMzERERAAERAAMzAAERAAAAAAAAAAERAAMzIiERIiIiIiREERIiERMzIiAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA&amp;amp;chxt=x,y&amp;amp;chxl=0:||0|1|2|3|4|5|6|7|8|9|10|11|12|13|14|15|16|17|18|19|20|21|22|23||1:||Sun|Mon|Tue|Wed|Thu|Fri|Sat|&amp;amp;chm=o,333333,1,1.0,25,0&amp;amp;chds=-1,24,-1,7,0,20' alt='rewritten-bin' /&gt;&lt;/p&gt;

&lt;h2 id='one_users_work'&gt;One User&amp;#8217;s Work&lt;/h2&gt;

&lt;pre&gt;&lt;code&gt;~/memcached % git timecard --author=dustin&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;&lt;img src='http://chart.apis.google.com/chart?cht=s&amp;amp;chs=800x300&amp;amp;chd=e:CkFIHrKPMzPXR7UeXCZmcKeuhRj1mZo9rhuEwozM1w4U639bCkFIHrKPMzPXR7UeXCZmcKeuhRj1mZo9rhuEwozM1w4U639bCkFIHrKPMzPXR7UeXCZmcKeuhRj1mZo9rhuEwozM1w4U639bCkFIHrKPMzPXR7UeXCZmcKeuhRj1mZo9rhuEwozM1w4U639bCkFIHrKPMzPXR7UeXCZmcKeuhRj1mZo9rhuEwozM1w4U639bCkFIHrKPMzPXR7UeXCZmcKeuhRj1mZo9rhuEwozM1w4U639bCkFIHrKPMzPXR7UeXCZmcKeuhRj1mZo9rhuEwozM1w4U639bCkFIHrKPMzPXR7UeXCZmcKeuhRj1mZo9rhuEwozM1w4U639b,IAIAIAIAIAIAIAIAIAIAIAIAIAIAIAIAIAIAIAIAIAIAIAIAQAQAQAQAQAQAQAQAQAQAQAQAQAQAQAQAQAQAQAQAQAQAQAQAYAYAYAYAYAYAYAYAYAYAYAYAYAYAYAYAYAYAYAYAYAYAYAYAgAgAgAgAgAgAgAgAgAgAgAgAgAgAgAgAgAgAgAgAgAgAgAgAn.n.n.n.n.n.n.n.n.n.n.n.n.n.n.n.n.n.n.n.n.n.n.n.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.3.3.3.3.3.3.3.3.3.3.3.3.3.3.3.3.3.3.3.3.3.3.3.3.................................................,AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAttAAAAAAAAAAAAbbJJAAAAAAAAAAAAAAAAJJJJAAAAAAAAAAAAAAAAkkAAAAAAAAAAAAAAAAAAAAAAAAAAAA..AAAAAAAAAAAAAAJJSSAAAAJJAAJJAAAAAAAAAAAAAAAAAAAAJJSSSSAAAAAAAAAAAAAAJJAAAAJJAAAAAAAAAAAAAAJJAAAAAAAAAAAAAAAAAAAAAAAAAAAASSAAAAAAAAAAAAAAAAAASSAAJJAAAAAAAAAAAAbbAAAAJJAAAAJJAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA&amp;amp;chxt=x,y&amp;amp;chxl=0:||0|1|2|3|4|5|6|7|8|9|10|11|12|13|14|15|16|17|18|19|20|21|22|23||1:||Sun|Mon|Tue|Wed|Thu|Fri|Sat|&amp;amp;chm=o,333333,1,1.0,25,0&amp;amp;chds=-1,24,-1,7,0,20' alt='dustins timecard' /&gt;&lt;/p&gt;

&lt;h2 id='last_weeks_work'&gt;Last Week&amp;#8217;s Work&lt;/h2&gt;

&lt;pre&gt;&lt;code&gt;~/twitterspy % git timecard &amp;#39;@{1 week ago}&amp;#39;..&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;&lt;img src='http://chart.apis.google.com/chart?cht=s&amp;amp;chs=800x300&amp;amp;chd=e:CkFIHrKPMzPXR7UeXCZmcKeuhRj1mZo9rhuEwozM1w4U639bCkFIHrKPMzPXR7UeXCZmcKeuhRj1mZo9rhuEwozM1w4U639bCkFIHrKPMzPXR7UeXCZmcKeuhRj1mZo9rhuEwozM1w4U639bCkFIHrKPMzPXR7UeXCZmcKeuhRj1mZo9rhuEwozM1w4U639bCkFIHrKPMzPXR7UeXCZmcKeuhRj1mZo9rhuEwozM1w4U639bCkFIHrKPMzPXR7UeXCZmcKeuhRj1mZo9rhuEwozM1w4U639bCkFIHrKPMzPXR7UeXCZmcKeuhRj1mZo9rhuEwozM1w4U639bCkFIHrKPMzPXR7UeXCZmcKeuhRj1mZo9rhuEwozM1w4U639b,IAIAIAIAIAIAIAIAIAIAIAIAIAIAIAIAIAIAIAIAIAIAIAIAQAQAQAQAQAQAQAQAQAQAQAQAQAQAQAQAQAQAQAQAQAQAQAQAYAYAYAYAYAYAYAYAYAYAYAYAYAYAYAYAYAYAYAYAYAYAYAYAgAgAgAgAgAgAgAgAgAgAgAgAgAgAgAgAgAgAgAgAgAgAgAgAn.n.n.n.n.n.n.n.n.n.n.n.n.n.n.n.n.n.n.n.n.n.n.n.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.3.3.3.3.3.3.3.3.3.3.3.3.3.3.3.3.3.3.3.3.3.3.3.3.................................................,AAAAAAAAAAAAAAAAAAAAQAAAAAAAv.AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA..AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAv.v.AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA&amp;amp;chxt=x,y&amp;amp;chxl=0:||0|1|2|3|4|5|6|7|8|9|10|11|12|13|14|15|16|17|18|19|20|21|22|23||1:||Sun|Mon|Tue|Wed|Thu|Fri|Sat|&amp;amp;chm=o,333333,1,1.0,25,0&amp;amp;chds=-1,24,-1,7,0,20' alt='recent twitterspy work' /&gt;&lt;/p&gt;</content>
 </entry>
 
 <entry>
   <title>Git Reroot - When Rebase is Too Gentle</title>
   <link href="http://dustin.github.com/2009/01/06/git-reroot.html"/>
   <updated>2009-01-06T00:00:00-08:00</updated>
   <id>http://dustin.github.com/2009/01/06/git-reroot</id>
   <content type="html">&lt;h1 id='git_reroot__when_rebase_is_too_gentle'&gt;Git Reroot - When Rebase is Too Gentle&lt;/h1&gt;
&lt;div&gt;
  &lt;img class='floatright' src='/images/transplant.jpg' alt='transplant' /&gt;
&lt;/div&gt;
&lt;p&gt;The fun thing about git is that it&amp;#8217;ll do whatever you tell it.&lt;/p&gt;

&lt;p&gt;Many newcomers look at is as this really complicated beast that is impossible to understand, but the less resistant users find that it&amp;#8217;s very happy to just sit back and happily do whatever you ask of it (even if you ask it to do something stupid).&lt;/p&gt;

&lt;p&gt;Recently, I was working on a project, and wanted to rebase a branch that had drifted quite a bit away from the master branch. &lt;code&gt;rebase&lt;/code&gt; itself wasn&amp;#8217;t getting me anywhere due to various conflicts from some partial merges and manual merges.&lt;/p&gt;

&lt;p&gt;As an attempt towards a solution, I created &lt;a href='http://gitorious.org/projects/bindir/repos/mainline/blobs/master/git-reroot'&gt;git reroot&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id='what_does_it_do'&gt;What Does it Do?&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;git reroot&lt;/code&gt; is very similar to &lt;code&gt;rebase&lt;/code&gt; conceptually, with one subtle detail &amp;#8211; &lt;code&gt;rebase&lt;/code&gt; works by rewinding to a merge point and replaying deltas (while dropping duplicates). &lt;code&gt;reroot&lt;/code&gt; works by taking a range of commits and placing the commits at the end of the current &lt;code&gt;HEAD&lt;/code&gt; by exact tree state.&lt;/p&gt;

&lt;p&gt;The distinction is subtle, but important. git does not record changes, it snapshots tree states with some additional metadata. Commit deltas may be computed between any arbitrary trees, so the representations you often see are these deltas.&lt;/p&gt;

&lt;h2 id='when_should_i_use_it'&gt;When Should I Use It?&lt;/h2&gt;

&lt;p&gt;Quite likely never. It was not appropriate for the project for which I created it.&lt;/p&gt;

&lt;p&gt;However, if&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;you find yourself with a branch that has diverged too far,&lt;/li&gt;

&lt;li&gt;you consider the result of this branch to be the desired state, and&lt;/li&gt;

&lt;li&gt;it&amp;#8217;s OK to think of the commits as snapshots of work instead of changes to previous state,&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;then you may find &lt;code&gt;reroot&lt;/code&gt; helpful.&lt;/p&gt;

&lt;h2 id='how_do_i_use_it'&gt;How Do I Use It?&lt;/h2&gt;

&lt;p&gt;The invocation recipes are different from that of &lt;code&gt;rebase&lt;/code&gt; because it&amp;#8217;s more of a &amp;#8220;do what I want&amp;#8221; kind of tool.&lt;/p&gt;

&lt;p&gt;In a really simple case, let&amp;#8217;s say you have a branch &lt;code&gt;new-development&lt;/code&gt; that diverged from &lt;code&gt;master&lt;/code&gt; a while back. Some work has been done on master, but you really just want &lt;code&gt;new-development&lt;/code&gt; to be master. For whatever reason, you don&amp;#8217;t want to do a merge to get it there, and rebase fails you due to conflicts you really don&amp;#8217;t care about.&lt;/p&gt;

&lt;p&gt;You would invoke &lt;code&gt;reroot&lt;/code&gt; as follows:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;git reroot master..new-development&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;You should see some output that&amp;#8217;s showing you progress, and then a line that looks like this:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;The newly created history is available as 2015200[...]&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;This command is &lt;em&gt;completely non-destrutive&lt;/em&gt;, and will not affect &lt;em&gt;any&lt;/em&gt; ref, so it&amp;#8217;s safe to do whenever and wherever you like.&lt;/p&gt;

&lt;p&gt;This output is telling you that the new tree is available, but not linked. You may use log (&lt;code&gt;git log 2015200&lt;/code&gt;) to examine it, and when you&amp;#8217;re ready to overwrite the current ref:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;git reset --hard 2015200&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;If you look through the deltas (&lt;code&gt;git log -p&lt;/code&gt;), you may see some changes that are much larger than you&amp;#8217;d expect (especially towards the beginning, or any merge points), but at any given commit, the source tree is guaranteed to be in the exact state it was in when the author committed it.&lt;/p&gt;</content>
 </entry>
 
 <entry>
   <title>Git Archaeology</title>
   <link href="http://dustin.github.com/2008/12/31/archaeology.html"/>
   <updated>2008-12-31T00:00:00-08:00</updated>
   <id>http://dustin.github.com/2008/12/31/archaeology</id>
   <content type="html">&lt;h1 id='git_archaeology'&gt;Git Archaeology&lt;/h1&gt;
&lt;div&gt;
  &lt;img class='floatright' src='/images/indiana_jones_small.jpg' alt='indy' /&gt;
&lt;/div&gt;
&lt;p&gt;I just spent a while reconstructing the history of my code &lt;a href='http://github.com/dustin/snippets' title='snippets'&gt;junk drawer&lt;/a&gt;. It&amp;#8217;s on its fourth revision control system now (&lt;a href='http://www.nongnu.org/cvs/' title='concurrent version system'&gt;cvs&lt;/a&gt; &amp;#8594; &lt;a href='http://www.gnu.org/software/gnu-arch/' title='gnu arch'&gt;tla&lt;/a&gt; &amp;#8594; &lt;a href='http://www.selenic.com/mercurial/' title='mercurial'&gt;mercurial&lt;/a&gt; &amp;#8594; &lt;a href='http://git-scm.com/' title='git'&gt;git&lt;/a&gt;) and has been through a lot of different tree states.&lt;/p&gt;

&lt;p&gt;CVS really only versions files, but allows you to arrange things into a hierarchy, so I had a natural hierarchy and reflected it in a similar way in CVS.&lt;/p&gt;

&lt;p&gt;Gnu arch favored smaller repositories, so when I did the conversion from CVS, I broke the snippets down into several different &amp;#8220;branches&amp;#8221; and versioned each language independently. I had one container branch that had a build config that would recreate the tree. This codebase lived through three different archives (repositories) and some of the individual snippets had a couple versions within that.&lt;/p&gt;

&lt;p&gt;Once I started using mercurial more, I needed my snippets with me, but mercurial didn&amp;#8217;t have a similar mechanism for managing a collection of repositories (even today, the &lt;a href='http://www.selenic.com/mercurial/wiki/index.cgi/ForestExtension' title='forest'&gt;forest extension&lt;/a&gt; is not distributed with mercurial). I had attempted to use &lt;a href='http://darcs.net/' title='darcs'&gt;darcs&lt;/a&gt; to reconstruct a single tree with full history but the trees renamed, but darcs wouldn&amp;#8217;t ever complete with a subset of what needed to be converted. I ended up just snapshotting what was in gnu arch and dropping it into a single mercurial repository.&lt;/p&gt;

&lt;p&gt;Having moved into git, I finally have the tools to actually put the history back together correctly. By &amp;#8220;correctly&amp;#8221;, I mean I wanted a single repository with all of the changes in it ordered chronologically (the order in which junk was placed in the drawer) without lots of weird merges that didn&amp;#8217;t actually happen. I &lt;em&gt;also&lt;/em&gt; needed to dig up all of the history prior to the snapshot I took for mercurial and get it all in place.&lt;/p&gt;

&lt;h2 id='bringing_up_snippets'&gt;Bringing up Snippets&lt;/h2&gt;

&lt;p&gt;Just to add to the complexity story, keep the following in mind:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;After tla, code was committed into mercurial from a snapshot.&lt;/li&gt;

&lt;li&gt;That snapshot was (cleanly) converted to git, and more code was committed there.&lt;/li&gt;

&lt;li&gt;One failed archaeological excursion had a few commits as well.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;I started by going to the latest gnu arch versions of each snippet set and converting them to git repositories (by way of mercurial &amp;#8211; but that&amp;#8217;s a different story).&lt;/p&gt;

&lt;h2 id='setting_up_the_repo'&gt;Setting up the Repo&lt;/h2&gt;

&lt;p&gt;I created a repo with a single empty commit in it as the eventual root of all of the other repos.&lt;/p&gt;

&lt;p&gt;Once each repository was converted to individual git repositories, I brought added them as remotes to the conversion repository. Each branch needs to be considered related in order to facilitate the eventual merge, so I created grafts that placed the root of each branch atop my empty commit using the following script:&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;span class='c'&gt;#!/bin/sh&lt;/span&gt;

&lt;span class='nv'&gt;empty&lt;/span&gt;&lt;span class='o'&gt;=&lt;/span&gt;6c417dd379ccdb46de57e7a3860379633c270c9e

&lt;span class='k'&gt;for &lt;/span&gt;b in &lt;span class='s2'&gt;&amp;quot;$@&amp;quot;&lt;/span&gt;
&lt;span class='k'&gt;do&lt;/span&gt;
&lt;span class='k'&gt;	&lt;/span&gt;&lt;span class='nv'&gt;oldest&lt;/span&gt;&lt;span class='o'&gt;=&lt;/span&gt;&lt;span class='sb'&gt;`&lt;/span&gt;git rev-list --reverse &lt;span class='nv'&gt;$b&lt;/span&gt; | head -1&lt;span class='sb'&gt;`&lt;/span&gt;
	&lt;span class='nb'&gt;echo&lt;/span&gt; &lt;span class='s2'&gt;&amp;quot;Grafting $b&amp;quot;&lt;/span&gt;
	&lt;span class='nb'&gt;echo&lt;/span&gt; &lt;span class='s2'&gt;&amp;quot;$oldest $empty&amp;quot;&lt;/span&gt; &amp;gt;&amp;gt; .git/info/grafts
&lt;span class='k'&gt;done&lt;/span&gt;
&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;This was run for every remote repo and then each branch was run through &lt;code&gt;git filter-branch&lt;/code&gt; to place the changes atop the empty branch in a real history.&lt;/p&gt;

&lt;h3 id='rewriting_tree_structures'&gt;Rewriting Tree Structures&lt;/h3&gt;

&lt;p&gt;These weren&amp;#8217;t quite ready to merge just yet. Before I could even consider an actual merge, I needed to modify the tree structures (e.g. take all of the stuff at the toplevel of the &lt;code&gt;eiffel&lt;/code&gt; directory and move it under an &lt;code&gt;eiffel/&lt;/code&gt; directory). The previous excursion had done this using a recipe I&amp;#8217;d found on the internet somewhere which &lt;em&gt;worked&lt;/em&gt;, but did the wrong thing with my version of sed. Using gsed cleaned this up.&lt;/p&gt;

&lt;p&gt;For each remote branch, I&amp;#8217;d run the following filter:&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;span class='c'&gt;#!/bin/sh&lt;/span&gt;

git filter-branch -f --index-filter &lt;span class='se'&gt;\&lt;/span&gt;
        &lt;span class='s1'&gt;&amp;#39;git ls-files -s | gsed &amp;quot;s-\t-&amp;amp;eiffel/-&amp;quot; |&lt;/span&gt;
&lt;span class='s1'&gt;                GIT_INDEX_FILE=$GIT_INDEX_FILE.new \&lt;/span&gt;
&lt;span class='s1'&gt;                        git update-index --index-info &amp;amp;&amp;amp;&lt;/span&gt;
&lt;span class='s1'&gt;         mv $GIT_INDEX_FILE.new $GIT_INDEX_FILE&amp;#39;&lt;/span&gt; &lt;span class='nv'&gt;$1&lt;/span&gt;/master
&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt;: Out of pure laziness, I would edit this script for every invocation and then run it for a single remote.&lt;/p&gt;

&lt;h3 id='doing_the_merge'&gt;Doing the Merge&lt;/h3&gt;
&lt;div&gt;
  &lt;img class='floatleft' src='http://img.skitch.com/20090101-6s52spepjx7qgjaj3yuscuasa.png' alt='merge' /&gt;
&lt;/div&gt;
&lt;p&gt;The merge was really rather exciting. The image to the left shows a 24-way octopus merge.&lt;/p&gt;

&lt;p&gt;That is, after grafting the empty changeset to the bottom of every branch, they now had common ancestry, making a merge possible. Since each branch got its paths rewritten all throughout history, there was no chance of conflict.&lt;/p&gt;

&lt;p&gt;So enter the octopus.&lt;/p&gt;
&lt;br clear='both' /&gt;
&lt;h3 id='performing_a_linear_rewrite'&gt;Performing a Linear Rewrite&lt;/h3&gt;

&lt;p&gt;As cool as it was to do a massive octopus merge, I wanted linear history.&lt;/p&gt;

&lt;p&gt;It would be possible to produce a graft file to place each change atop a single parent, but that seemed quite hard.&lt;/p&gt;

&lt;p&gt;The strategy I employed was to dump the entire history using &lt;code&gt;git format-patch&lt;/code&gt; and then write &lt;a href='http://github.com/dustin/snippets/tree/master/python/misc/rewrite-patches.py'&gt;a script&lt;/a&gt; to rename all of the patches to be in chronological order so I could use &lt;code&gt;git am&lt;/code&gt; to reconstruct the tree.&lt;/p&gt;

&lt;p&gt;So I created a new branch from &amp;#8220;empty&amp;#8221;, and ran &lt;code&gt;git am&lt;/code&gt; for a while. A nice bonus is that &lt;code&gt;git apply&lt;/code&gt; strips off trailing whitespace for me, so the changes were slightly cleaned on the way in (I could&amp;#8217;ve disabled that, but I rather liked it).&lt;/p&gt;

&lt;h3 id='removing_emptiness'&gt;Removing Emptiness&lt;/h3&gt;

&lt;p&gt;I no longer needed the &amp;#8220;empty&amp;#8221; changeset after &lt;code&gt;git am&lt;/code&gt; was complete, so I had to get rid of that. The root node is generally a bit difficult to touch, but I sort of guessed that I could add a graft of a hash without a parent and it&amp;#8217;d make that change the new root.&lt;/p&gt;

&lt;p&gt;So another trip through &lt;code&gt;git filter-branch&lt;/code&gt; and I&amp;#8217;ve now got a pretty decent set of history up throgh the snapshot that was taken for the mercurial conversion.&lt;/p&gt;

&lt;h3 id='catching_up_to_the_present'&gt;Catching up to the Present&lt;/h3&gt;

&lt;p&gt;So now that I&amp;#8217;ve got everything up to the snapshot, what do I do?&lt;/p&gt;

&lt;p&gt;I had a lot of options here &amp;#8211; cherry-picking, grafting, format-patch. I think I went with format-patch arbitrarily. Basically, I did a &lt;code&gt;git format-patch&lt;/code&gt; of the full history from the latest git repo and applied those changes to the newly created one.&lt;/p&gt;

&lt;h3 id='verification'&gt;Verification&lt;/h3&gt;

&lt;p&gt;So now that everything has been all hacked up and history is rewritten and changests grafted, etc&amp;#8230; how do I have any idea whether it&amp;#8217;s even close to where it was before?&lt;/p&gt;

&lt;p&gt;This is where git&amp;#8217;s content tracking stuff really saves the day. With the git repo I&amp;#8217;ve been using as a remote, I can do a simple diff across the trees from the latest branches (and various other states). The only differences I saw were some new scripts/etc&amp;#8230; had been added.&lt;/p&gt;

&lt;p&gt;All&amp;#8217;s well. I certainly learned a lot.&lt;/p&gt;</content>
 </entry>
 
 <entry>
   <title>Using Git Alternates</title>
   <link href="http://dustin.github.com/2008/12/30/git-alternates.html"/>
   <updated>2008-12-30T00:00:00-08:00</updated>
   <id>http://dustin.github.com/2008/12/30/git-alternates</id>
   <content type="html">&lt;h1 id='using_git_alternates'&gt;Using Git Alternates&lt;/h1&gt;

&lt;p&gt;Now that you&amp;#8217;re happily using &lt;a href='/2008/12/29/github-sync.html'&gt;github sync&lt;/a&gt; to pull down all your repos into local bare trees, you may want to free up a bit of disk space from duplicate objects (about 120MB for me).&lt;/p&gt;

&lt;p&gt;git has a way for multiple repos to share object space by way of alternates. You can read more about alternates in the &lt;a href='http://www.kernel.org/pub/software/scm/git/docs/gitrepository-layout.html'&gt;repository layout documentation&lt;/a&gt;, but essentially it&amp;#8217;s a text file that contains the location of another &lt;code&gt;objects&lt;/code&gt; directory from which objects may be fetched when needed.&lt;/p&gt;

&lt;h2 id='example'&gt;Example:&lt;/h2&gt;

&lt;p&gt;Let&amp;#8217;s say you&amp;#8217;re me and have checked out my photo album. You&amp;#8217;d end up with a .git directory that looks like this:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;dhcp-39:/tmp/photo 599% du -sh .git
 18M	.git&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;By setting up an alternate using my &lt;a href='http://gitorious.org/projects/bindir/repos/mainline/blobs/master/git-alternate'&gt;git alternate&lt;/a&gt; command:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;dhcp-39:/tmp/photo 600% git alternate ~/prog/github/photo.git
.git/objects -&amp;gt; /Users/dustin/prog/github/photo.git/objects&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;You can then gc and free up gangs of disk:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;dhcp-39:/tmp/photo 601% git gc 
Nothing new to pack.
Removing duplicate objects: 100% (256/256), done.
dhcp-39:/tmp/photo 602% du -sh .git
144K	.git&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;From 18MB to 144KB, and everything pretty much works as it did before.&lt;/p&gt;

&lt;p&gt;You don&amp;#8217;t need my &lt;a href='http://gitorious.org/projects/bindir/repos/mainline/blobs/master/git-alternate'&gt;git alternate&lt;/a&gt; command, for that, of course, but it makes it a bit easier when you&amp;#8217;ve got a lot of them to do.&lt;/p&gt;</content>
 </entry>
 
 <entry>
   <title>Using Github Sync to Track Your Projects</title>
   <link href="http://dustin.github.com/2008/12/29/github-sync.html"/>
   <updated>2008-12-29T00:00:00-08:00</updated>
   <id>http://dustin.github.com/2008/12/29/github-sync</id>
   <content type="html">&lt;h1 id='using_github_sync_to_track_your_projects'&gt;Using Github Sync to Track Your Projects&lt;/h1&gt;
&lt;div&gt;
  &lt;img class='floatright' src='/images/octocat-sync-small.png' alt='octocat syncing' /&gt;
&lt;/div&gt;
&lt;p&gt;When &lt;a href='http://github.com/'&gt;github&lt;/a&gt; announced their &lt;a href='http://github.com/guides/the-github-api'&gt;API&lt;/a&gt;, I very quickly threw together a &lt;a href='http://github.com/dustin/py-github'&gt;python implementation&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I didn&amp;#8217;t end up doing very much with the project as a whole, but I did write one tool in here that I end up using quite a bit: &lt;code&gt;githubsync.py&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;githubsync.py&lt;/code&gt; takes a github username and a directory and make sure I&amp;#8217;ve got a local copy of every public repo that user has on github.&lt;/p&gt;

&lt;p&gt;Grab the repo and try it out:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;git clone git://github.com/dustin/py-github.git
cd py-github
./src/githubsync.py dustin /tmp/dustinatgithub&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Once that finishes, you will have all of my current public repos in &lt;code&gt;/tmp/dustinatgithub&lt;/code&gt; and if you run it periodically, you&amp;#8217;ll see new repos I add appear while the existing ones are being updated.&lt;/p&gt;

&lt;p&gt;But what about private repos, or even repos that aren&amp;#8217;t on github?&lt;/p&gt;

&lt;p&gt;The file &lt;code&gt;~/.github-private&lt;/code&gt; is read as a tab-delimited list of repos and their sources and those will also be synchronized. For example:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;cool-stuff	  git@github.com:dustin/cool-stuff.git&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;With that in place, the &lt;code&gt;cool-stuff&lt;/code&gt; repo will be created and synchronized along with all of the stuff found through the API.&lt;/p&gt;</content>
 </entry>
 
 <entry>
   <title>Wasted Time Developing for iPhone</title>
   <link href="http://dustin.github.com/2008/12/26/wasted-time-on-iphone.html"/>
   <updated>2008-12-26T00:00:00-08:00</updated>
   <id>http://dustin.github.com/2008/12/26/wasted-time-on-iphone</id>
   <content type="html">&lt;div&gt;
	&lt;img class='floatright' src='http://img.skitch.com/20081227-qukmwpnbu6u9qnruimsqnrdj2y.jpg' alt='wasted time' /&gt;
&lt;/div&gt;
&lt;p&gt;OK, everybody&amp;#8217;s written about this, but I just wasted a bunch of time making an iPhone app.&lt;/p&gt;

&lt;p&gt;I don&amp;#8217;t actually feel too bad about it because it was a &lt;a href='http://github.com/dustin/twister-iphone'&gt;pretty stupid&lt;/a&gt; iPhone app, anyway, but I&amp;#8217;m not going to finish it because my first attempt to run it outside of the simulator was going to cost me a hundred bucks.&lt;/p&gt;

&lt;p&gt;The application is an iPhone port of my &lt;a href='http://dustin.github.com/2008/12/25/twister.html'&gt;twister&lt;/a&gt; app, but with worse graphics and sound (though the sound is at least &lt;em&gt;potentially&lt;/em&gt; better). It&amp;#8217;s functional enough to play a few games, but not fully polished.&lt;/p&gt;

&lt;p&gt;I was hoping I could stick it on my daughter&amp;#8217;s iPhone so she could play, but doesn&amp;#8217;t seem to be the case.&lt;/p&gt;

&lt;p&gt;If anyone wants to do something with it, it&amp;#8217;s over on &lt;a href='http://github.com/dustin/twister-iphone'&gt;github&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Of course, I&amp;#8217;d love to find out I was wrong and I can actually run my own program on my own phone without paying more&amp;#8230;&lt;/p&gt;</content>
 </entry>
 
 <entry>
   <title>pfetch</title>
   <link href="http://dustin.github.com/2008/12/26/pfetch.html"/>
   <updated>2008-12-26T00:00:00-08:00</updated>
   <id>http://dustin.github.com/2008/12/26/pfetch</id>
   <content type="html">&lt;h1 id='about_pfetch'&gt;About pfetch&lt;/h1&gt;
&lt;div&gt;
	&lt;img class='floatright' src='/images/octopus.png' alt='octopus' /&gt;
&lt;/div&gt;
&lt;p&gt;For a long time now, I&amp;#8217;ve had various cron jobs running to fetch various web resources with which I&amp;#8217;d build out parts of my own site, or supply myself with custom RSS feeds after a pass through xsltproc.&lt;/p&gt;

&lt;p&gt;This mostly worked OK, but there were a few things wrong with it:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;I had to be careful to avoid putting stuff in place upon fetch failure.&lt;/li&gt;

&lt;li&gt;Fetch failures would send me email unless I put effort into avoiding that.&lt;/li&gt;

&lt;li&gt;Network timeouts would cause cron jobs to start piling up.&lt;/li&gt;

&lt;li&gt;I&amp;#8217;ve actually had cron get sick of running my jobs and just stop altogether.&lt;/li&gt;

&lt;li&gt;Various jobs that ran at various frequencies would be in various scripts and hard to keep up with.&lt;/li&gt;

&lt;li&gt;Running through cron means all jobs start at the exact same moment in time, thus are more likely to cause strain on web servers (if everybody does it).&lt;/li&gt;

&lt;li&gt;Conditional gets require cross-invocation state to be stored (though I wrote &lt;a href='http://github.com/dustin/snippets/tree/master/python/net/http/fetch.py'&gt;a tool&lt;/a&gt; for this).&lt;/li&gt;

&lt;li&gt;Sequential processing meant the whole thing took longer.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;After a while, the problems added to enough of an annoyance that I decided to do something about it, so a couple months ago I started &lt;a href='http://github.com/dustin/pfetch'&gt;pfetch&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;pfetch is a simple &lt;a href='http://twistedmatrix.com/'&gt;twisted&lt;/a&gt; app that does scheduled parallel http requests and optionally runs scripts after successful execution.&lt;/p&gt;

&lt;p&gt;Given a list of URLs each with a destination, frequency, and optional (with arguments) to run after each successful (200) response, each URL will begin a fetch cycle starting at a random offset from the start time and loop on the defined interval.&lt;/p&gt;</content>
 </entry>
 
 <entry>
   <title>Twister</title>
   <link href="http://dustin.github.com/2008/12/25/twister.html"/>
   <updated>2008-12-25T00:00:00-08:00</updated>
   <id>http://dustin.github.com/2008/12/25/twister</id>
   <content type="html">&lt;h1 id='twister'&gt;Twister&lt;/h1&gt;
&lt;div&gt;
	&lt;img class='floatright' src='http://upload.wikimedia.org/wikipedia/en/thumb/0/09/1966_Twister_Cover.jpg/275px-1966_Twister_Cover.jpg' alt='twister' /&gt;
&lt;/div&gt;
&lt;p&gt;So, on Christmas, my kids decided they wanted to play twister. They wanted me to spin the thingy and call out moves for them. That got &lt;em&gt;really&lt;/em&gt; boring after about five minutes.&lt;/p&gt;

&lt;p&gt;I wrote a really simple python script to start calling the moves for me since the spinny thing was getting annoying, and would sometimes end up pointing between two colors or otherwise be too difficult to call.&lt;/p&gt;

&lt;p&gt;The &lt;a href='http://gist.github.com/40015'&gt;first version&lt;/a&gt; of the script looked like this:&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;span class='c'&gt;#!/usr/bin/env python&lt;/span&gt;

&lt;span class='kn'&gt;import&lt;/span&gt; &lt;span class='nn'&gt;random&lt;/span&gt;

&lt;span class='k'&gt;if&lt;/span&gt; &lt;span class='n'&gt;__name__&lt;/span&gt; &lt;span class='o'&gt;==&lt;/span&gt; &lt;span class='s'&gt;&amp;#39;__main__&amp;#39;&lt;/span&gt;&lt;span class='p'&gt;:&lt;/span&gt;
    &lt;span class='n'&gt;colors&lt;/span&gt;&lt;span class='o'&gt;=&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='s'&gt;&amp;#39;red&amp;#39;&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='s'&gt;&amp;#39;green&amp;#39;&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='s'&gt;&amp;#39;yellow&amp;#39;&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='s'&gt;&amp;#39;blue&amp;#39;&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
    &lt;span class='n'&gt;limbs&lt;/span&gt;&lt;span class='o'&gt;=&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='s'&gt;&amp;#39;left foot&amp;#39;&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='s'&gt;&amp;#39;right foot&amp;#39;&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='s'&gt;&amp;#39;left hand&amp;#39;&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='s'&gt;&amp;#39;right hand&amp;#39;&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;

    &lt;span class='k'&gt;print&lt;/span&gt; &lt;span class='n'&gt;random&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;choice&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='n'&gt;limbs&lt;/span&gt;&lt;span class='p'&gt;),&lt;/span&gt; &lt;span class='n'&gt;random&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;choice&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='n'&gt;colors&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;That was fine, but I still had to run it and call it out. Then I remembered that someone made a &lt;a href='http://www.gnufoo.org/macosx/'&gt;talking cat&lt;/a&gt; for OS X. All I needed to do was run the output of this thing through that, and there&amp;#8217;d be speech and then I could go about my business and let the computer call moves for them.&lt;/p&gt;

&lt;p&gt;I thought that was kind of cool, but wanted something a little&amp;#8230;more. I ended up writing a full &lt;a href='http://github.com/dustin/twister'&gt;OS X desktop version&lt;/a&gt; complete with images, icons, a preference pane, etc&amp;#8230;&lt;/p&gt;

&lt;p&gt;The kids finished playing (using the prototype) long before I finished writing the app. It was fun for all of us, though. :)&lt;/p&gt;

&lt;p&gt;If anyone wants to play a two-player version of twister, though, you can grab a copy.&lt;/p&gt;

&lt;h2 id='download'&gt;Download&lt;/h2&gt;

&lt;p&gt;&lt;a href='http://public.west.spy.net/app/Twister_1.1.zip'&gt;Version 1.1&lt;/a&gt;&lt;/p&gt;</content>
 </entry>
 
 <entry>
   <title>Moody Bots</title>
   <link href="http://dustin.github.com/2008/12/24/moody-bots.html"/>
   <updated>2008-12-24T00:00:00-08:00</updated>
   <id>http://dustin.github.com/2008/12/24/moody-bots</id>
   <content type="html">&lt;h1 id='moody_bots'&gt;Moody Bots&lt;/h1&gt;

&lt;p&gt;&lt;a href='/twitterspy/'&gt;Twitterspy&lt;/a&gt; is a rather brute-force way to achieve xmpp functionality for twitter. It makes very heavy use of twitter search to provide track-like functionality to end users.&lt;/p&gt;

&lt;p&gt;I&amp;#8217;ve noticed in watching the logs that I often will get more errors in attempting searches than successes. This was at least the feel I got from looking at the logs. I wanted a way to communicate this to look at this information via xmpp.&lt;/p&gt;

&lt;p&gt;Initially, it seemed like status would be a good way to do this. Currently, the status is used to show stats on how many users and queries the bot knows about. This is already a little weird, and I wouldn&amp;#8217;t want to try to shove too much stuff into it.&lt;/p&gt;

&lt;p&gt;Next, I thought about using the vcard for it. The bio is a fine way to describe such things. That wasn&amp;#8217;t quite right, either. The bio is a better general description of the bot, and not so much status.&lt;/p&gt;

&lt;p&gt;Then I discovered &lt;a href='http://xmpp.org/extensions/xep-0107.html'&gt;XEP-0107&lt;/a&gt; &amp;#8211; user moods. User moods in a &lt;a href='http://xmpp.org/extensions/xep-0163.html'&gt;PEP&lt;/a&gt; transport provides exactly the kind of thing I&amp;#8217;m looking for.&lt;/p&gt;
&lt;div&gt;&lt;img class='floatright' src='http://img.skitch.com/20081225-g8nbh7s3np2amubspgkas2ab1f.png' alt='twitterspy angry' /&gt;&lt;/div&gt;
&lt;p&gt;Twitterspy keeps track of how many of its searches are successful, and how many fail. When many searches are successful, it&amp;#8217;s in a good mood, when few are, it&amp;#8217;s in a bad mood.&lt;/p&gt;
&lt;div&gt;&lt;img class='floatleft' src='http://ralphm.net/images/mood/knology/excited.gif' alt='excited!' /&gt;&lt;/div&gt;
&lt;p&gt;I had my kid look through the XEP to come up with some rules for how to select a mood based on how successful recent searches are. I&amp;#8217;ve applied many of her changes, but some still require me to keep a bit more state than I do currently. It&amp;#8217;s kind of an exciting thing, though few people will ever actually see it.&lt;/p&gt;

&lt;p&gt;The pubsub mechanism will hopefully show itself to be useful, though. I&amp;#8217;m hoping to do something cool like have a web status showing moods and all. &lt;a href='http://ralphm.net/'&gt;Ralph Meijer&amp;#8217;s&lt;/a&gt; &lt;a href='http://ralphm.net/moods'&gt;moods page&lt;/a&gt; is quite inspirational here &amp;#8211; as long as I&amp;#8217;m capturing the data.&lt;/p&gt;

&lt;p&gt;For the rest of you out there: Bring your XMPP services to life. Show their moods.&lt;/p&gt;</content>
 </entry>
 
 <entry>
   <title>Building Your Site Connectivity</title>
   <link href="http://dustin.github.com/2008/12/24/building-site-connectivity.html"/>
   <updated>2008-12-24T00:00:00-08:00</updated>
   <id>http://dustin.github.com/2008/12/24/building-site-connectivity</id>
   <content type="html">&lt;h1 id='building_your_site_connectivity'&gt;Building Your Site Connectivity&lt;/h1&gt;

&lt;p&gt;When I started building this jekyll site, I thought it&amp;#8217;d be nice to link to all the other places I leave junk around the internet. Rather than manually building a list, I took a bit of time to write something to do it for me using the &lt;a href='http://code.google.com/apis/socialgraph/'&gt;google social graph API&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I made a simple &lt;a href='http://public.west.spy.net/autolinks.html'&gt;web form&lt;/a&gt; to do this that generates HTML source so it can be further hand-edited if needed, but more importantly, so that I can actually paste the results into my github page and have it actually count for site connectivity.&lt;/p&gt;

&lt;p&gt;If you don&amp;#8217;t maintain a list of links on your own page, you may find it helpful to link to your &lt;a href='http://friendfeed.com/'&gt;friendfeed&lt;/a&gt; account.&lt;/p&gt;

&lt;p&gt;For example, you can see &lt;a href='http://public.west.spy.net/autolinks.html?u=http://friendfeed.com/dlsspy'&gt;how friendfeed links me&lt;/a&gt;. Change the username from dlsspy to yours for results that make more sense to you.&lt;/p&gt;

&lt;p&gt;As it&amp;#8217;s just a simple chunk of HTML, I&amp;#8217;ve created &lt;a href='http://gist.github.com/39613'&gt;a gist&lt;/a&gt; to house it for now. If you&amp;#8217;d like to change this for the better, do it there and let me know about it.&lt;/p&gt;

&lt;p&gt;I hope someone (else) finds this useful.&lt;/p&gt;</content>
 </entry>
 
 <entry>
   <title>Trying out Jekyll</title>
   <link href="http://dustin.github.com/2008/12/23/trying-jekyll.html"/>
   <updated>2008-12-23T00:00:00-08:00</updated>
   <id>http://dustin.github.com/2008/12/23/trying-jekyll</id>
   <content type="html">&lt;h1 id='trying_out_jekyll'&gt;Trying out Jekyll&lt;/h1&gt;

&lt;p&gt;Since &lt;a href='http://github.com/mojombo/jekyll'&gt;Jekyll&lt;/a&gt; seems to be all the craze, I figured I&amp;#8217;d give it a shot and see if it solved any problems for me.&lt;/p&gt;

&lt;p&gt;So far, I like it.&lt;/p&gt;</content>
 </entry>
 

</feed>
