<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	xmlns:georss="http://www.georss.org/georss" xmlns:geo="http://www.w3.org/2003/01/geo/wgs84_pos#" xmlns:media="http://search.yahoo.com/mrss/"
	>

<channel>
	<title>The Knut Hellan Blog</title>
	<atom:link href="http://knuthellan.com/feed/" rel="self" type="application/rss+xml" />
	<link>http://knuthellan.com</link>
	<description>General geekyness and starting a company</description>
	<lastBuildDate>Mon, 05 Dec 2011 15:23:00 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.com/</generator>
<cloud domain='knuthellan.com' port='80' path='/?rsscloud=notify' registerProcedure='' protocol='http-post' />
<image>
		<url>http://s2.wp.com/i/buttonw-com.png</url>
		<title>The Knut Hellan Blog</title>
		<link>http://knuthellan.com</link>
	</image>
	<atom:link rel="search" type="application/opensearchdescription+xml" href="http://knuthellan.com/osd.xml" title="The Knut Hellan Blog" />
	<atom:link rel='hub' href='http://knuthellan.com/?pushpress=hub'/>
		<item>
		<title>Case statement pitfall when migrating to Ruby 1.9</title>
		<link>http://knuthellan.com/2011/08/01/case-statement-pitfall-when-migrating-to-ruby-1-9/</link>
		<comments>http://knuthellan.com/2011/08/01/case-statement-pitfall-when-migrating-to-ruby-1-9/#comments</comments>
		<pubDate>Mon, 01 Aug 2011 14:34:19 +0000</pubDate>
		<dc:creator>knuthellan</dc:creator>
				<category><![CDATA[code example]]></category>
		<category><![CDATA[ruby]]></category>

		<guid isPermaLink="false">http://knuthellan.com/?p=329</guid>
		<description><![CDATA[I have been using Rubinius 2.0 to run machine learning experiments with libsvm lately. When running in Ruby 1.9.2, I noticed that my classifier always classified all samples as negative. I though this was caused by issues with libsvm-ruby-swig so I recompiled libsvm-ruby-swig from scratch including rerunning swig, but nothing changed. Next, I changed to [...]<img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=knuthellan.com&amp;blog=6371883&amp;post=329&amp;subd=knuthellan&amp;ref=&amp;feed=1" width="1" height="1" />]]></description>
			<content:encoded><![CDATA[<p>I have been using Rubinius 2.0 to run machine learning experiments with libsvm lately. When running in Ruby 1.9.2, I noticed that my classifier always classified all samples as negative. I though this was caused by issues with <a href="https://github.com/tomz/libsvm-ruby-swig">libsvm-ruby-swig</a> so I recompiled libsvm-ruby-swig from scratch including rerunning swig, but nothing changed. Next, I changed to use <a href="https://github.com/bdigital/libsvmffi">libsvmffi</a> instead, but the result was the same. Realizing that I actually had some tests running av very simple classifier and that these tests passed on 1.9.2 made me look closer at the code. What I found was that the behavior of the Ruby case statement has changed from 1.8.7 to 1.9.2.</p>
<p>For if statements, 1 is equal to 1.0 in both 1.8.7 and 1.9, but while 1 matches 1.0 in 1.8.7 case statements, it does not in 1.9.2.</p>
<p>Code snippet that shows the difference:</p>
<blockquote>
<pre>
#!/usr/bin/env ruby

puts case 1.0
when 1
  "yay"
else
  "nay"
end
</pre>
</blockquote>
<p>First the output of irb when running 1.8.7:</p>
<blockquote>
<pre>
$ rvm use ruby-1.8.7
Using /usr/local/rvm/gems/ruby-1.8.7-p334
$ ./case.rb
<b>yay</b>
</pre>
</blockquote>
<p>And the same in 1.9.2:</p>
<blockquote>
<pre>
$ rvm use ruby-1.9.2
Using /usr/local/rvm/gems/ruby-1.9.2-p180
$ ./case.rb
<b>nay</b>
</pre>
</blockquote>
<p>Needless to say, I was puzzled by this result, but I was more surprised by the 1.8.7 behavior than 1.9.2. My assumption when I wrote the code was that I was dealing with integer values and since it worked, I forgot about it. Next time you see different behavior between 1.8.7 and 1.9.2 it might be worth reviewing case statements.</p>
<br />  <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/knuthellan.wordpress.com/329/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/knuthellan.wordpress.com/329/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/knuthellan.wordpress.com/329/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/knuthellan.wordpress.com/329/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gofacebook/knuthellan.wordpress.com/329/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/facebook/knuthellan.wordpress.com/329/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gotwitter/knuthellan.wordpress.com/329/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/twitter/knuthellan.wordpress.com/329/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/knuthellan.wordpress.com/329/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/knuthellan.wordpress.com/329/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/knuthellan.wordpress.com/329/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/knuthellan.wordpress.com/329/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/knuthellan.wordpress.com/329/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/knuthellan.wordpress.com/329/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=knuthellan.com&amp;blog=6371883&amp;post=329&amp;subd=knuthellan&amp;ref=&amp;feed=1" width="1" height="1" />]]></content:encoded>
			<wfw:commentRss>http://knuthellan.com/2011/08/01/case-statement-pitfall-when-migrating-to-ruby-1-9/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
	
		<media:content url="" medium="image">
			<media:title type="html">knuthellan</media:title>
		</media:content>
	</item>
		<item>
		<title>CouchDB and the web</title>
		<link>http://knuthellan.com/2010/09/09/couchdb-and-the-web/</link>
		<comments>http://knuthellan.com/2010/09/09/couchdb-and-the-web/#comments</comments>
		<pubDate>Thu, 09 Sep 2010 12:59:52 +0000</pubDate>
		<dc:creator>knuthellan</dc:creator>
				<category><![CDATA[code example]]></category>
		<category><![CDATA[couchdb]]></category>

		<guid isPermaLink="false">http://knuthellan.com/?p=316</guid>
		<description><![CDATA[This is my presentation from JavaZone 2010 Note that during my presentation, I showed the view section and basic replication directly in Futon instead of showing the fallback in the slides. What I did show was mostly the same, but naturally I showed some variations on the mappers as well.<img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=knuthellan.com&amp;blog=6371883&amp;post=316&amp;subd=knuthellan&amp;ref=&amp;feed=1" width="1" height="1" />]]></description>
			<content:encoded><![CDATA[<p><a href="https://docs.google.com/present/view?id=ddjngdz7_60cgznwj85">This is my presentation from JavaZone 2010</a></p>
<p>Note that during my presentation, I showed the view section and basic replication directly in Futon instead of showing the fallback in the slides. What I did show was mostly the same, but naturally I showed some variations on the mappers as well.</p>
<br />  <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/knuthellan.wordpress.com/316/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/knuthellan.wordpress.com/316/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/knuthellan.wordpress.com/316/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/knuthellan.wordpress.com/316/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gofacebook/knuthellan.wordpress.com/316/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/facebook/knuthellan.wordpress.com/316/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gotwitter/knuthellan.wordpress.com/316/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/twitter/knuthellan.wordpress.com/316/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/knuthellan.wordpress.com/316/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/knuthellan.wordpress.com/316/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/knuthellan.wordpress.com/316/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/knuthellan.wordpress.com/316/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/knuthellan.wordpress.com/316/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/knuthellan.wordpress.com/316/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=knuthellan.com&amp;blog=6371883&amp;post=316&amp;subd=knuthellan&amp;ref=&amp;feed=1" width="1" height="1" />]]></content:encoded>
			<wfw:commentRss>http://knuthellan.com/2010/09/09/couchdb-and-the-web/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
	
		<media:content url="" medium="image">
			<media:title type="html">knuthellan</media:title>
		</media:content>
	</item>
		<item>
		<title>Feature prioritization for Pillow the CouchDB shard manager</title>
		<link>http://knuthellan.com/2010/07/14/feature-prioritization-for-pillow-the-couchdb-shard-manager/</link>
		<comments>http://knuthellan.com/2010/07/14/feature-prioritization-for-pillow-the-couchdb-shard-manager/#comments</comments>
		<pubDate>Wed, 14 Jul 2010 11:31:59 +0000</pubDate>
		<dc:creator>knuthellan</dc:creator>
				<category><![CDATA[couchdb]]></category>
		<category><![CDATA[scaling]]></category>
		<category><![CDATA[Storage]]></category>

		<guid isPermaLink="false">http://knuthellan.com/?p=303</guid>
		<description><![CDATA[I have now reached the end of my todo list for Pillow. That doesn&#8217;t mean it&#8217;s finished and ready to be stamped version 1.0. In it&#8217;s current incarnation it is fully usable and production ready, but in order to earn a 1.0 it needs to do a bit more. The current resharding always doubles the [...]<img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=knuthellan.com&amp;blog=6371883&amp;post=303&amp;subd=knuthellan&amp;ref=&amp;feed=1" width="1" height="1" />]]></description>
			<content:encoded><![CDATA[<p>I have now reached the end of my todo list for <a href="http://github.com/khellan/Pillow">Pillow</a>. That doesn&#8217;t mean it&#8217;s finished and ready to be stamped version 1.0. In it&#8217;s current incarnation it is fully usable and production ready, but in order to earn a 1.0 it needs to do a bit more.</p>
<p>The current resharding always doubles the number of servers required. Since you may overshard, that doesn&#8217;t necessarily mean you have to double the number of physical servers, but you need to organize more <a href="http://couchdb.apache.org/">CouchDB</a> instances than you might otherwise need. Smoother sharding algorithms that enable addition of single additional servers exist (<a href="http://en.wikipedia.org/wiki/Consistent_hashing">consistent hashing</a>) so Pillow should support this.</p>
<p>Pillow currently only supports rereducers written in Erlang. It would really be nice to support JavaScript for rereducers. A summing rereducer exists and mappers without reducers works just like in CouchDB. However when you have more complex reduction needs, copying the reducer code from your CouchDB into Pillow beats writing (and maintaining) them again in a new language.</p>
<p>Pillow should really support the bulk document API of CouchDB. I haven&#8217;t used this one myself, but adding support should be pretty straightforward.</p>
<p><a href="http://wiki.github.com/couchapp/couchapp/">CouchApp</a> support is harder since it requires JavaScript support and then some. I probably need to play around with a CouchApp or two to find out more, but since I haven&#8217;t done so, it&#8217;s hard to determine how much work it would take.</p>
<p>While I do hope that there are no non-replicated CouchDB servers in production out there, reality is that there probably are lots. I like the three-way replication minimum myself and with CouchDB&#8217;s master-master scheme, it works really well. Pillow however is currently happily ignorant of any replication you have set up. I would really like to have Pillow manage such replication. In addition to managing replication, sets of Pillow servers should be controllable from a random server in the same master-master way ensuring full control of your cluster from any single Pillow node.</p>
<p>There is no clear prioritized list right now, all features listed above (and probably more) would be beneficial. However, as I am currently the only one developing Pillow and the time I can spend on Pillow is limited, I have to prioritize. The five features can be grouped:</p>
<ul>
<li>CouchDB API compatibility: JavaScript views, bulk documents, CouchApp</li>
<li>Production flexibility and scaling: Consistent Hashing and Replication management</li>
</ul>
<p>It is not hard to admit that API compatibility is important, but the core of the API is supported. Production flexibility and scaling is more important for me at the moment and I will probably focus on that. I also think that replication management is slightly more useful than consistent hashing. Choosing between the API features is harder since I don&#8217;t need them myself, but JavaScript views is a prerequisite of CouchApp and bulk document support is straightforward in comparison to CouchApp leading to this priority list:</p>
<ol>
<li>Replication management</li>
<li>Consistent hashing</li>
<li>JavaScript views</li>
<li>Bulk documents</li>
<li>CouchApp</li>
</ol>
<p>This list is the result of my needs at the time of writing. Others may convince me to adjust the priorities. Better yet, others may jump in and add support for the features they need.</p>
<br />  <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/knuthellan.wordpress.com/303/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/knuthellan.wordpress.com/303/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/knuthellan.wordpress.com/303/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/knuthellan.wordpress.com/303/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gofacebook/knuthellan.wordpress.com/303/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/facebook/knuthellan.wordpress.com/303/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gotwitter/knuthellan.wordpress.com/303/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/twitter/knuthellan.wordpress.com/303/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/knuthellan.wordpress.com/303/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/knuthellan.wordpress.com/303/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/knuthellan.wordpress.com/303/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/knuthellan.wordpress.com/303/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/knuthellan.wordpress.com/303/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/knuthellan.wordpress.com/303/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=knuthellan.com&amp;blog=6371883&amp;post=303&amp;subd=knuthellan&amp;ref=&amp;feed=1" width="1" height="1" />]]></content:encoded>
			<wfw:commentRss>http://knuthellan.com/2010/07/14/feature-prioritization-for-pillow-the-couchdb-shard-manager/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
	
		<media:content url="" medium="image">
			<media:title type="html">knuthellan</media:title>
		</media:content>
	</item>
		<item>
		<title>A functional approach to Ruby</title>
		<link>http://knuthellan.com/2010/06/22/a-functional-approach-to-ruby/</link>
		<comments>http://knuthellan.com/2010/06/22/a-functional-approach-to-ruby/#comments</comments>
		<pubDate>Tue, 22 Jun 2010 09:51:02 +0000</pubDate>
		<dc:creator>knuthellan</dc:creator>
				<category><![CDATA[ruby]]></category>

		<guid isPermaLink="false">http://knuthellan.com/?p=276</guid>
		<description><![CDATA[Several articles and blog posts have been written about functional Ruby. They tend to focus either on whether Ruby is a functional language or how to do functional programming in Ruby. I am not planning to do either. This post will look into the benefits of a functional approach to Ruby and the transition from [...]<img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=knuthellan.com&amp;blog=6371883&amp;post=276&amp;subd=knuthellan&amp;ref=&amp;feed=1" width="1" height="1" />]]></description>
			<content:encoded><![CDATA[<p>Several articles and blog posts have been written about functional Ruby. They tend to focus either on whether Ruby is a functional language or how to do functional programming in Ruby. I am not planning to do either. This post will look into the benefits of a functional approach to Ruby and the transition from thinking classic object-oriented to functional.</p>
<p>I consider the discussion around Ruby being a functional language or not academic with no effect on my use of the language. There is no doubt that functional programming is possible in Ruby, but bear in mind that it does not enforce pure functions that do not have side-effects. As for good overviews of functional programming in Ruby, I suggest <a href="http://www.khelll.com/">Khaled alHabache’s</a> post <a href="http://www.khelll.com/blog/ruby/ruby-and-functional-programming/">Ruby and Functional Programming</a>. </p>
<p>When I originally started developing in Ruby, I was used to object-oriented programming. I tended to make classes for all kinds of data objects and the result looked like a nicer, more readable version of Java code. While this works, it is not the most effective way of developing in Ruby (or other dynamic programming languages). </p>
<p>One of the benefits of Ruby (and many other dynamic programming languages), is their lack of static typing. In classic object-oriented development, you would define member variables and methods to operate on the variables. You don&#8217;t have to do that in Ruby since you have a flexible hash class that can store most of what you need. Once you have replaced all member variables with a hash, the hash is your object and the methods of the old class are just functions that could operate on your hash.</p>
<p>There are situations where a hash doesn&#8217;t make sense. If you build an abstraction class, i.e. a storage system abstraction class, you might want to keep some information internally. Connection parameters for the storage system could be kept in a hash, but that doesn&#8217;t feel right. Interestingly, since there is normally only a single storage system of a particular type in use, you could make the storage abstraction class a singleton and keep all the connection parameters internally in traditional member variables.</p>
<p>Some information that you would stored in a database or on a different server might be used often enough to keep a memory cache. Let me stress that I don&#8217;t like caches and I try to avoid them whenever possible since they add complexity and the potential for inconsistent data among different servers. That being said, I do add caches when they are needed and once again, caches can also be implemented as a hash. Some code needs to control the caches, but since you normally cache data coming either from storage or a different server or service, you could put them in the abstraction class for that. If you need to write and use common code to manage the caches, you can easily build a mixin module.</p>
<p>Interestingly, the model you end up with if you only have hashes you send around and keep consistent data in singletons is similar to the Erlang gen_server behavior. This behavior is a general server template for Erlang, a pure functional language with immutable variables. The state variable is given as a parameter to all the general server functions. This allows the gen_server to maintain information in a pure functional setting.</p>
<p>When you get used to keeping abstractions in singletons and your data in hashes, you can also use modules instead of classes to modularize your code to keep related functionality together. If you also use blocks to define exact behavior inside function you end up with flexible code that is very easy to reuse.</p>
<p>Keeping your object data in a hash and implementing functions without side-effects make testing easy. What you get back from a function call is only a result of the function parameters and there is no need to test combinations of operations. In an object-oriented setting, member variables might not be observable and even if the returned value of a method call is correct the object might be in an undesired state that you cannot test without modifying the class to make internal variables accessible in your test system. Clearly the functional approach is cleaner and requires less test code.</p>
<p>The Sincerial system being a request handling system is built mostly functionally. There are singletons guarding the storage system and other cached data. The system also uses classic objects where that makes sense. Ruby is an object-oriented language and I believe in using the language features available when appropriate. This might sound like a contradiction to the whole post, but it&#8217;s not. My point is that you should avoid creating traditional classes when a hash can do the job and use functional programming techniques actively to improve maintainability, testability and readability of your code.</p>
<br />  <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/knuthellan.wordpress.com/276/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/knuthellan.wordpress.com/276/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/knuthellan.wordpress.com/276/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/knuthellan.wordpress.com/276/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gofacebook/knuthellan.wordpress.com/276/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/facebook/knuthellan.wordpress.com/276/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gotwitter/knuthellan.wordpress.com/276/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/twitter/knuthellan.wordpress.com/276/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/knuthellan.wordpress.com/276/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/knuthellan.wordpress.com/276/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/knuthellan.wordpress.com/276/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/knuthellan.wordpress.com/276/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/knuthellan.wordpress.com/276/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/knuthellan.wordpress.com/276/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=knuthellan.com&amp;blog=6371883&amp;post=276&amp;subd=knuthellan&amp;ref=&amp;feed=1" width="1" height="1" />]]></content:encoded>
			<wfw:commentRss>http://knuthellan.com/2010/06/22/a-functional-approach-to-ruby/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
	
		<media:content url="" medium="image">
			<media:title type="html">knuthellan</media:title>
		</media:content>
	</item>
		<item>
		<title>Pillow, the CouchDB shard manager</title>
		<link>http://knuthellan.com/2010/06/10/pillow-the-couchdb-shard-manager/</link>
		<comments>http://knuthellan.com/2010/06/10/pillow-the-couchdb-shard-manager/#comments</comments>
		<pubDate>Thu, 10 Jun 2010 11:12:39 +0000</pubDate>
		<dc:creator>knuthellan</dc:creator>
				<category><![CDATA[couchdb]]></category>

		<guid isPermaLink="false">http://knuthellan.com/?p=268</guid>
		<description><![CDATA[If there is one thing that has bothered me about my choice of CouchDB as the main storage system for Sincerial, it&#8217;s the lack of an automatic system for shard management. In the early days of a startup, a single server is probably capable of handling all the necessary data. However, a successful service that [...]<img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=knuthellan.com&amp;blog=6371883&amp;post=268&amp;subd=knuthellan&amp;ref=&amp;feed=1" width="1" height="1" />]]></description>
			<content:encoded><![CDATA[<p>If there is one thing that has bothered me about my choice of <a href="http://couchdb.apache.org/">CouchDB</a> as the main storage system for <a href="http://www.sincerial.com/">Sincerial</a>, it&#8217;s the lack of an automatic system for shard management. In the early days of a startup, a single server is probably capable of handling all the necessary data. However, a successful service that is built around the harvesting and analysis of data will sooner or later have to shard the dataset across multiple servers. And for a new service, the sooner you have to start sharding, the better. Several good distributed storage systems exist. <a href="http://labs.google.com/papers/bigtable.html">Google&#8217;s original bigtable</a>, <a href="http://hbase.apache.org/">HBase (Hadoop&#8217;s bigtable equivalent)</a>, <a href="http://cassandra.apache.org/">Cassandra</a> and more solve this particular problem, but there is more to choosing and running a storage system than just data volume scaling. CouchDB has other strengths that made it a good choice for our application, but that is not the topic of this post.</p>
<p><a href="http://tilgovi.github.com/couchdb-lounge/">CouchDB-lounge</a> originally written by Kevin Ferguson (macfergus), Vijay Ragunathan (lukatmyshu) and Shaun Lindsay (srlindsay) at Meebo.com will handle distribution of requests to the right servers in a cluster of servers, but you still have to handle resharding or repartitioning manually. CouchDB-lounge consists of two components, dumbproxy handling reading and writing of documents and smartproxy handling views. These require Nginx and Twisted (a Python framework) respectively. If you overshard appropriately, you can scale your data volume a long way before you have to start resharding.</p>
<p>While manually repartitioning a CouchDB database is doable, I&#8217;d rather have an automatic way of doing it since I don&#8217;t want to make mistakes. In addition, the Sincerial system uses Ruby running with<a href="http://www.modrails.com/"> Phusion Passenger</a> in <a href="http://httpd.apache.org/">Apache</a> and I didn&#8217;t want to add two more frameworks on top of that. This might sound like a not-invented here excuse, but it isn&#8217;t or at least I don&#8217;t think it is.</p>
<p>When I started developing <a href="http://github.com/khellan/Pillow">Pillow</a>, I chose to do so in erlang to match couchdb. The reason was two-fold. First of all I was curious about erlang and I like functional programming. Secondly, CouchDB was written in erlang and there had to be a reason for that. Now I&#8217;ve released version 0.3 of Pillow. This version supports automatic resharding, routing of requests to the right shard and views. Reducers need to be written in erlang, but a summing reducer is in place and mappers without reducers are supported out of the box. As such, this version of Pillow has all the functionality I set out to develop, but it does not support the full CouchDB API.</p>
<p>The bulk document API is not supported. And I haven&#8217;t tried running standalone CouchApps. The reason being that I am focusing on our needs. I intend to have full support of the CouchDB API eventually, but it might be that I integrate more tightly with CouchDB and use CouchDB as a library to make this happen. This will make supporting javascript reducers easier as well. It would of course be interesting to have Pillow become an integral part of CouchDB as well providing one can still access each CouchDB server directly for maintenance purposes. The latter being one of the reasons I&#8217;m in general a bit sceptical to distributed systems that hide the inner workings since often hard to fix problems that may occur.</p>
<br />  <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/knuthellan.wordpress.com/268/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/knuthellan.wordpress.com/268/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/knuthellan.wordpress.com/268/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/knuthellan.wordpress.com/268/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gofacebook/knuthellan.wordpress.com/268/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/facebook/knuthellan.wordpress.com/268/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gotwitter/knuthellan.wordpress.com/268/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/twitter/knuthellan.wordpress.com/268/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/knuthellan.wordpress.com/268/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/knuthellan.wordpress.com/268/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/knuthellan.wordpress.com/268/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/knuthellan.wordpress.com/268/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/knuthellan.wordpress.com/268/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/knuthellan.wordpress.com/268/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=knuthellan.com&amp;blog=6371883&amp;post=268&amp;subd=knuthellan&amp;ref=&amp;feed=1" width="1" height="1" />]]></content:encoded>
			<wfw:commentRss>http://knuthellan.com/2010/06/10/pillow-the-couchdb-shard-manager/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
	
		<media:content url="" medium="image">
			<media:title type="html">knuthellan</media:title>
		</media:content>
	</item>
		<item>
		<title>CouchDB Replication Monitor</title>
		<link>http://knuthellan.com/2009/11/24/couchdb-replication-monitor/</link>
		<comments>http://knuthellan.com/2009/11/24/couchdb-replication-monitor/#comments</comments>
		<pubDate>Tue, 24 Nov 2009 11:52:44 +0000</pubDate>
		<dc:creator>knuthellan</dc:creator>
				<category><![CDATA[couchdb]]></category>

		<guid isPermaLink="false">http://knuthellan.com/?p=247</guid>
		<description><![CDATA[CouchDB does replication, but replication needs to be set up after each server restart. This means you need to ensure that replication is restarted whenever the daemon restarts CouchDB. I have never seen replication stop working without a restart, but I prefer being safe to being sorry about replication. To be perfectly honest, I do [...]<img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=knuthellan.com&amp;blog=6371883&amp;post=247&amp;subd=knuthellan&amp;ref=&amp;feed=1" width="1" height="1" />]]></description>
			<content:encoded><![CDATA[<p>CouchDB does replication, but replication needs to be set up after each server restart. This means you need to ensure that replication is restarted whenever the daemon restarts CouchDB. I have never seen replication stop working without a restart, but I prefer being safe to being sorry about replication. To be perfectly honest, I do not trust that my replication initiation after a soft CouchDB restart works properly either so I prefer to monitor the replication and have a safety mechanism in place to restart replication if needed.</p>
<p>There are several ways to monitor replication. You could fetch the status page of all servers and restart replication on servers with an empty page, but that is a kind of brute force approach in my world. A better solution is to use the replication itself to monitor that it works. </p>
<p>Each server updates their timestamp in CouchDB and this is again replicated to the other servers. This gets us a bit of the way, but not all the way. The server you are checking might have received updates from all the other servers, but you don&#8217;t know if it&#8217;s pushed out anything to the other servers. To solve this, you can add information about the other servers to the local server as well. This will give you a matrix of server replication status.</p>
<p>For each server, you will see the timestamp replicated from the server and a list of timestamps replicated to that server. The latter often being a generation older than the former. Cron can be used to update this data. The cronjob reads all the server timestamps and updates this servers timestamp followed by a list of the other servers timestamp.</p>
<p>A mapper to get a server id to server status out of the db.<br />
<code></p>
<pre>
map: function(doc) {
  emit(doc._id, doc);
}
</pre>
<p></code></p>
<p>Our monitroing database is called server_status. The design containing the mapper is called collections and the view server_list.</p>
<p>A Ruby database checker that can run on cron.<br />
<code></p>
<pre>
require 'rubygems'
require 'couchrest'
require 'json'
require 'open-uri'

STATUS_DB = 'http://localhost:5984/server_status'
COLLECTIONS = 'collections'
SERVER_LIST = 'server_list'

hostname = ARGV[0]

status_db = CouchRest.database!(STATUS_DB)
status_view = "#{STATUS_DB}/_design/#{COLLECTIONS}/_view/#{SERVER_LIST}"

# Get the current information about this server if available
server_status = begin
  status_db.get(hostname)
rescue RestClient::ResourceNotFound
  {'_id' =&gt; hostname}
end

server_status['time'] = Time.new.to_i
# Get the current times of the other servers and update this server's
# view of them
JSON(open(status_view).read)['rows'].map do |row|
  {'server' =&gt; row['id'], 'status' =&gt; row['value']}
end.each do |status|
  unless status['server'] == hostname
    server_status['servers'][status['server']] = status['status']['time']
  end
end
status_db.save_doc(server_status)
</pre>
<p></code></p>
<p>Now you need to determine when to trigger replication restart. This can be handled in the watchdog cronjob. If the highest timestamp seen for this server at other servers is above a threshold, restart replication.</p>
<p>The final loop triggering when the age is above a threshold. The init_replication method just posts a continuous replication trigger to the db:<br />
<code></p>
<pre>
JSON(open(status_view).read)['rows'].map do |row|
  {'server' =&gt; row['id'], 'status' =&gt; row['value']}
end.each do |status|
  if server_status['time'] - status['status']['time'] &gt; THRESHOLD
    init_replication(status['server'])
  end
  unless status['server'] == hostname
    server_status['servers'][status['server']] = status['status']['time']
  end
end
</pre>
<p></code></p>
<p>Rudimentary init_replication method.<br />
<code></p>
<pre>
def init_replication(server)
  target = "http://#{server}:5984"
  databases = ['server_status']
  databases.each do |db|
    config = {
            'source' =&gt; "#{db}",
            'target' =&gt; "#{target}/#{db}",
            'continuous' =&gt; true
    }
    payload = JSON.generate(config)
    result = Net::HTTP.new('127.0.0.1', '5984').post(
      '/_replicate', payload, {'content-type' =&gt; 'text/x-json'})
    unless result.code == 200
      p "replication to #{target}/#{db} failed with #{result.code}"
    end
  end
end
</pre>
<p></code></p>
<p>We have a monitoring view of replication ages in our system. It shows the matrix of timestamps as age in seconds rather than the actual timestamp since the age is the important metric.<br />
<a href="http://knuthellan.files.wordpress.com/2009/11/server_status.jpg"><img src="http://knuthellan.files.wordpress.com/2009/11/server_status.jpg?w=300&#038;h=104" alt="Server Status" title="Server Status" width="300" height="104" class="alignnone size-medium wp-image-250" /></a></p>
<p>A bonus of this replication monitoring system is that we can access the status page from a mobil phone and get an accurate picture of the replication status. This doesn&#8217;t worry me now, but it did when we first set it up. Now it&#8217;s just a part of our general monitoring view.</p>
<br />  <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/knuthellan.wordpress.com/247/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/knuthellan.wordpress.com/247/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/knuthellan.wordpress.com/247/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/knuthellan.wordpress.com/247/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gofacebook/knuthellan.wordpress.com/247/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/facebook/knuthellan.wordpress.com/247/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gotwitter/knuthellan.wordpress.com/247/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/twitter/knuthellan.wordpress.com/247/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/knuthellan.wordpress.com/247/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/knuthellan.wordpress.com/247/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/knuthellan.wordpress.com/247/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/knuthellan.wordpress.com/247/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/knuthellan.wordpress.com/247/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/knuthellan.wordpress.com/247/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=knuthellan.com&amp;blog=6371883&amp;post=247&amp;subd=knuthellan&amp;ref=&amp;feed=1" width="1" height="1" />]]></content:encoded>
			<wfw:commentRss>http://knuthellan.com/2009/11/24/couchdb-replication-monitor/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
	
		<media:content url="" medium="image">
			<media:title type="html">knuthellan</media:title>
		</media:content>

		<media:content url="http://knuthellan.files.wordpress.com/2009/11/server_status.jpg?w=300" medium="image">
			<media:title type="html">Server Status</media:title>
		</media:content>
	</item>
		<item>
		<title>Sincerial Launched</title>
		<link>http://knuthellan.com/2009/11/02/sincerial-launched/</link>
		<comments>http://knuthellan.com/2009/11/02/sincerial-launched/#comments</comments>
		<pubDate>Mon, 02 Nov 2009 14:25:56 +0000</pubDate>
		<dc:creator>knuthellan</dc:creator>
				<category><![CDATA[couchdb]]></category>
		<category><![CDATA[deployment]]></category>
		<category><![CDATA[startup]]></category>

		<guid isPermaLink="false">http://knuthellan.com/?p=235</guid>
		<description><![CDATA[Since we launched Sincerial with one connected online store, fundies.no on Thursday, I feel it&#8217;s time to go through the system and the choices we made on our way to launch. Hosting The obvious and easy choice was Amazon Web Services (AWS) Elastic Compute Cloud (EC2). A hosting service such as EC2 allows a lot [...]<img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=knuthellan.com&amp;blog=6371883&amp;post=235&amp;subd=knuthellan&amp;ref=&amp;feed=1" width="1" height="1" />]]></description>
			<content:encoded><![CDATA[<p>Since we launched <a href="http://www.sincerial.com/">Sincerial</a> with one connected online store, <a href="http://www.fundies.no/">fundies.no</a> on Thursday, I feel it&#8217;s time to go through the system and the choices we made on our way to launch.</p>
<p><strong>Hosting</strong><br />
The obvious and easy choice was <em>Amazon Web Services (AWS) Elastic Compute Cloud (EC2)</em>. A hosting service such as <em>EC2</em> allows a lot of flexibility in server solutions including quick ramp up if the luxury problem of needing more servers occurs. Freedom to choose operating system and providing a virtual server resembling what you get from a traditional hosting services were also important for us. There were alternatives, traditional hosting services where you book physical servers would incur a larger fixed cost than we wanted and at the same time, we would have lost the flexibility. We currently fire up servers and test things in no time just to shut those servers down after the test. With a traditional hosting provider, we would need spare servers for testing or used the live servers.<em> Windows Azure</em> could have been an alternative, but it&#8217;s just a <em>.NET Windows</em> hosting environment and not flexible enough. I feel I should mention Google App Engine as well, but we didn&#8217;t really consider it since it&#8217;s too restricted and even more locked in than <em>Windows Azure</em>.</p>
<p><strong>Operating System</strong><br />
We decided to use <em>CentOS</em> for the servers. The reason for this was favorable experience with <em>Fedora</em> over the last year and <em>Ubuntu</em> getting more painful at the same time.<em> Red Hat Enterprise Linux (RHEL)</em> was a contender, but <em>CentOS</em> provides us with the part of <em>RHEL</em> that we need without all the parts that we would pay for, but not use. <em>Debian</em> would have been our choice a few years back, but the <em>Fedora</em> and <em>Ubuntu</em> experiences over the last year brought us down on the <em>Fedora</em>, <em>RHEL</em>, <em>CentOS</em> side and as mentioned, <em>CentOS</em> was the best fit among those three. <em>Windows</em> wasn&#8217;t considered. We had absolutely no need for anything <em>Windows</em> in there and remote management and configuration of <em>Linux</em> systems is so much easier while being extremely stable. The <em>Amazon Machine Image (AMI)</em> we use was created by <a href="http://www.rightscale.com/">RightScale</a>.</p>
<p><strong>Web Serving Environment</strong><br />
We chose <em>Apache</em> because that&#8217;s what we&#8217;ve used in the past and <em>Apache</em> is solid and dependable. The downside of <em>Apache</em> is that it&#8217;s a big beast that might be overkill for our need. An alternative that we looked a bit at is <em>nginx</em>, but we didn&#8217;t want to spend time on that before launch. We will however look more closely at <em>nginx</em> in the future.</p>
<p><strong>Progamming Language</strong><br />
I am a rubyist and we chose <em>Ruby</em>. The first time I tried <em>Ruby</em>, I wrote a few scripts that I would normally have written in <em>Perl</em>. These weren&#8217;t big scripts, but far from one-liners. They downloaded some content and analyzed it. Writing the scripts in <em>Ruby</em> took me a bit longer than it would have taken writing them in <em>Perl</em>, but they worked right away. In <em>Perl</em> there is always something wrong unless you have a one-liner. The strongest alternative was <em>Python</em> and between <em>Ruby</em> and <em>Python</em> it comes down to taste. The first readability I got as a Googler was in <em>Python</em> and I did write a lot of <em>Python</em>, but it still doesn&#8217;t feel right. I was very happy when I got a chance to sneak myself to a <em>Ruby</em> readability. <em>Java</em> was sort of not considered, but would have been the choice if the lightweight short time-to-market alternatives hadn&#8217;t been available. We use<em> Phusion Passenger</em> to serve a combination of pure <em>Ruby</em> racks and <em>Sinatra</em> apps in <em>Apache</em>. </p>
<p><strong>Storage</strong><br />
<em>CouchDB</em> won this one. Key value storage is what we need so relational databases are a complication that we can happily forget about. Selling points of <em>CouchDB</em> was that it was easy to get started, it has an <em>HTTP RESTful API</em> and views are written as <em>Javascript</em> map-reduce. Map reduces is well suited to our calculation needs so we get a lot more done inside the database than we would do with i.e. <em>SQL</em>. The strongest alternative was definitely <em>MongoDB</em> which is very similar to <em>CouchDB</em>, but uses a more classical query language. We also briefly looked at <em>Voldemort</em>, but our calculation need is not suited to <em>Voldemort</em>&#8216;s simple key lookup scheme. <em>SimpleDB</em> ties us to <em>Amazon</em> and <em>Hadoop</em> is a bit too heavy for our needs. We didn&#8217;t consider <em>MySQL</em>, but that would have been our choice had we needed a relational database.</p>
<p><strong>Conclusion</strong><br />
Our <em>LARC</em> stack (<em>Linux</em> <em>Apache</em>, <em>Ruby</em>, <em>CouchDB</em>) works very well for us and that combination has enabled us to develop our service very quickly with the result being an easy system to maintain. We haven&#8217;t really looked back at those choices except for a few moments after upgrading to <em>CouchDB</em> 0.10.0 and seeing that our map reduces that put a lot of work in the reducers had to be rewritten. One look at <em>MongoDB</em>&#8216;s query language stopped those regrets. As mentioned when discussing web serving environments, we do consider switching from <em>Apache</em> to <em>nginx</em>, but it&#8217;s not a high priority thing and we are happy with <em>Apache</em> and the consideration comes more from curiosity than need. As for <em>CentOS</em> and <em>EC2</em>, they just work and gets out of the way which is exactly what they should do.</p>
<br />  <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/knuthellan.wordpress.com/235/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/knuthellan.wordpress.com/235/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/knuthellan.wordpress.com/235/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/knuthellan.wordpress.com/235/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gofacebook/knuthellan.wordpress.com/235/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/facebook/knuthellan.wordpress.com/235/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gotwitter/knuthellan.wordpress.com/235/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/twitter/knuthellan.wordpress.com/235/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/knuthellan.wordpress.com/235/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/knuthellan.wordpress.com/235/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/knuthellan.wordpress.com/235/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/knuthellan.wordpress.com/235/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/knuthellan.wordpress.com/235/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/knuthellan.wordpress.com/235/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=knuthellan.com&amp;blog=6371883&amp;post=235&amp;subd=knuthellan&amp;ref=&amp;feed=1" width="1" height="1" />]]></content:encoded>
			<wfw:commentRss>http://knuthellan.com/2009/11/02/sincerial-launched/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
	
		<media:content url="" medium="image">
			<media:title type="html">knuthellan</media:title>
		</media:content>
	</item>
		<item>
		<title>CouchDB on Amazon EC2 CentOS server with Sprinkle</title>
		<link>http://knuthellan.com/2009/08/31/couchdb-on-amazon-ec2-centos-server-with-sprinkle/</link>
		<comments>http://knuthellan.com/2009/08/31/couchdb-on-amazon-ec2-centos-server-with-sprinkle/#comments</comments>
		<pubDate>Mon, 31 Aug 2009 13:18:26 +0000</pubDate>
		<dc:creator>knuthellan</dc:creator>
				<category><![CDATA[code example]]></category>
		<category><![CDATA[couchdb]]></category>

		<guid isPermaLink="false">http://knuthellan.com/?p=219</guid>
		<description><![CDATA[Read the Getting Started part of Till Klampäckel&#8217;s CouchDB on Ubuntu on AWS blog post for some general information. I see no reason to repeat those things here. Till stresses the need for a security group opening port 80, but you should also enable ssh at port 22, otherwise it will be impossible to isntall [...]<img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=knuthellan.com&amp;blog=6371883&amp;post=219&amp;subd=knuthellan&amp;ref=&amp;feed=1" width="1" height="1" />]]></description>
			<content:encoded><![CDATA[<p>Read the Getting Started part of Till Klampäckel&#8217;s <a href="http://till.klampaeckel.de/blog/archives/55-CouchDB-on-Ubuntu-on-AWS.html">CouchDB on Ubuntu on AWS</a> blog post for some general information. I see no reason to repeat those things here.</p>
<p>Till stresses the need for a security group opening port 80, but you should also enable ssh at port 22, otherwise it will be impossible to isntall anything. The AMI I use is rightscale&#8217;s CentOS 5.2 i386 v4.2.4. If you need a 64-bit image, that should work just as well.</p>
<p>Make sure you have Sprinkle installed on you the system you are installing from. Put this in your spinkle file and name it something reasonable. I called it couchdb.rb. If not, gem install sprinkle. Sprinkle is written in Ruby so if you don&#8217;t have Ruby, you should start by installing that.</p>
<p><code><br />
# Sprinkle provisioning and deployment for CouchDB on<br />
# an Amazon EC2 CentOS server</p>
<p>package :spidermonkey do<br />
&nbsp;&nbsp;source 'http://ftp.mozilla.org/pub/mozilla.org/js/js-1.7.0.tar.gz' do<br />
&nbsp;&nbsp;&nbsp;&nbsp;custom_dir 'js/src'<br />
&nbsp;&nbsp;&nbsp;&nbsp;custom_install "make BUILD_OPT=1 -f Makefile.ref &amp;&amp; cp *.{h,tbl} /usr/include/ &amp;&amp; cd Linux_All_OPT.OBJ &amp;&amp; cp *.h /usr/include/ &amp;&amp; mkdir -p /usr/{bin,lib}/ &amp;&amp; cp js /usr/bin/ &amp;&amp; cp libjs.so /usr/lib/"<br />
end</p>
<p>&nbsp;&nbsp;verify do<br />
&nbsp;&nbsp;&nbsp;&nbsp;has_executable 'js'<br />
&nbsp;&nbsp;&nbsp;&nbsp;has_file '/usr/include/jsapi.h'<br />
&nbsp;&nbsp;&nbsp;&nbsp;has_file '/usr/lib/libjs.so'<br />
&nbsp;&nbsp;end<br />
end</p>
<p>package :erlang_dependencies do<br />
&nbsp;&nbsp;yum %w( ncurses-devel openssl-devel)<br />
end</p>
<p>package :erlang do<br />
&nbsp;&nbsp;description 'Erlang, the programming language'<br />
&nbsp;&nbsp;source 'http://erlang.org/download/otp_src_R13B01.tar.gz'</p>
<p>&nbsp;&nbsp;verify do<br />
&nbsp;&nbsp;&nbsp;&nbsp;has_executable '/usr/local/bin/erl'<br />
&nbsp;&nbsp;end</p>
<p>&nbsp;&nbsp;requires :erlang_dependencies<br />
end</p>
<p>package :couchdb_dependencies do<br />
&nbsp;&nbsp;yum %w( curl curl-devel icu libicu-devel )<br />
end</p>
<p>#   - CouchDB 0.9.1<br />
package :couchdb, :provides =&gt; :database do<br />
&nbsp;&nbsp;description 'CouchDB'<br />
&nbsp;&nbsp;version '0.9.1'<br />
&nbsp;&nbsp;source 'http://mirrorservice.nomedia.no/apache.org/couchdb/0.9.1/apache-couchdb-0.9.1.tar.gz' do<br />
&nbsp;&nbsp;&nbsp;&nbsp;post :install, 'adduser -r -d /usr/local/var/lib/couchdb -M -s /bin/bash -c "CouchDB Administrator" couchdb'<br />
&nbsp;&nbsp;&nbsp;&nbsp;post :install, 'touch /usr/local/var/log/couchdb/couch.log'<br />
&nbsp;&nbsp;&nbsp;&nbsp;post :install, 'chown couchdb /usr/local/var/log/couchdb/couch.log'<br />
&nbsp;&nbsp;&nbsp;&nbsp;post :install, 'mkdir -p /usr/local/var/lib/couchdb'<br />
&nbsp;&nbsp;&nbsp;&nbsp;post :install, 'chown couchdb /usr/local/var/lib/couchdb'<br />
&nbsp;&nbsp;&nbsp;&nbsp;post :install, '/usr/local/etc/rc.d/couchdb start'<br />
&nbsp;&nbsp;&nbsp;&nbsp;post :install, 'ln -s /usr/local/etc/rc.d/couchdb /etc/init.d/couchdb'<br />
&nbsp;&nbsp;&nbsp;&nbsp;post :install, 'chkconfig --add couchdb'<br />
&nbsp;&nbsp;end</p>
<p>&nbsp;&nbsp;verify do<br />
&nbsp;&nbsp;&nbsp;&nbsp;has_executable '/usr/local/bin/couchdb'<br />
&nbsp;&nbsp;end</p>
<p>&nbsp;&nbsp;requires :couchdb_dependencies<br />
&nbsp;&nbsp;requires :erlang<br />
&nbsp;&nbsp;requires :spidermonkey<br />
end</p>
<p>package :rubygems do<br />
&nbsp;&nbsp;description 'Ruby Gems Package Management System'<br />
&nbsp;&nbsp;yum 'rubygems'<br />
end</p>
<p>package :couchrest do<br />
&nbsp;&nbsp;description 'Rest API for CouchDB'<br />
&nbsp;&nbsp;version '0.33'<br />
&nbsp;&nbsp;gem 'couchrest'<br />
end</p>
<p>policy :db, :roles =&gt; :db do<br />
&nbsp;&nbsp;requires :database<br />
&nbsp;&nbsp;requires :couchrest<br />
end</p>
<p># Deployment<br />
deployment do<br />
&nbsp;&nbsp;delivery :capistrano do<br />
&nbsp;&nbsp;&nbsp;&nbsp;set :user, 'root'<br />
&nbsp;&nbsp;&nbsp;&nbsp;set :use_sudo, false<br />
&nbsp;&nbsp;&nbsp;&nbsp;set :run_method, :run</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;role :db, 'ec2-x-y-z-w.eu-west-1.compute.amazonaws.com'<br />
&nbsp;&nbsp;end</p>
<p>&nbsp;&nbsp;# source based package installer defaults<br />
&nbsp;&nbsp;source do<br />
&nbsp;&nbsp;&nbsp;&nbsp;prefix   '/usr/local'           # where all source packages will be configured to install<br />
&nbsp;&nbsp;&nbsp;&nbsp;archives '/usr/local/sources'   # where all source packages will be downloaded to<br />
&nbsp;&nbsp;&nbsp;&nbsp;builds   '/usr/local/build'     # where all source packages will be built<br />
&nbsp;&nbsp;end<br />
end<br />
</code></p>
<p>Replace <em>ec2-x-y-z-w.eu-west-1.compute.amazonaws.com</em> with the public DNS name listed on the Amazon Web Services instance view. You don&#8217;t need rubygems and couchrest unless you are going to use Ruby, but I decided to leave them since CouchRest is a nice libarary to use when talking to CouchDB from Ruby.</p>
<p>Run it in a shell with <code>sprinkle -s couchdb.rb</code>. Might be interesting to check the powder cloud like this first: <code>sprinkle -cts couchdb.rb</code>. The expected cloud looks liek this:<br />
<code><br />
--&gt; Cloud hierarchy for policy db</p>
<p>Policy db requires package database<br />
Selecting couchdb for virtual package database<br />
&nbsp;&nbsp;Package couchdb requires couchdb_dependencies<br />
&nbsp;&nbsp;Package couchdb requires erlang<br />
&nbsp;&nbsp;&nbsp;&nbsp;Package erlang requires erlang_dependencies<br />
&nbsp;&nbsp;Package couchdb requires spidermonkey</p>
<p>Policy db requires package couchrest<br />
&nbsp;&nbsp;Package couchrest requires rubygems<br />
</code></p>
<p>Set up an SSH tunnel to get the remote futonI tend to use 5994 locally to avoid conflicts with the local CouchDB. <code>ssh -L 5994:localhost:5984 root@ec2-x-y-z-w.eu-west-1.compute.amazonaws.com</code> where once again, <em>ec2-x-y-z-w.eu-west-1.compute.amazonaws.com</em> should be replaced with the public DNS name listed on the Amazon Web Services instance view. Point your browser to <em>http://localhost:5994/_utils/</em> for that familiar futon view.</p>
<br />  <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/knuthellan.wordpress.com/219/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/knuthellan.wordpress.com/219/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/knuthellan.wordpress.com/219/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/knuthellan.wordpress.com/219/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gofacebook/knuthellan.wordpress.com/219/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/facebook/knuthellan.wordpress.com/219/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gotwitter/knuthellan.wordpress.com/219/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/twitter/knuthellan.wordpress.com/219/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/knuthellan.wordpress.com/219/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/knuthellan.wordpress.com/219/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/knuthellan.wordpress.com/219/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/knuthellan.wordpress.com/219/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/knuthellan.wordpress.com/219/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/knuthellan.wordpress.com/219/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=knuthellan.com&amp;blog=6371883&amp;post=219&amp;subd=knuthellan&amp;ref=&amp;feed=1" width="1" height="1" />]]></content:encoded>
			<wfw:commentRss>http://knuthellan.com/2009/08/31/couchdb-on-amazon-ec2-centos-server-with-sprinkle/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
	
		<media:content url="" medium="image">
			<media:title type="html">knuthellan</media:title>
		</media:content>
	</item>
		<item>
		<title>Finding the one in an ocean of provisioning and deployment systems</title>
		<link>http://knuthellan.com/2009/08/21/finding-the-one-in-an-ocean-of-provisioning-and-deployment-systems/</link>
		<comments>http://knuthellan.com/2009/08/21/finding-the-one-in-an-ocean-of-provisioning-and-deployment-systems/#comments</comments>
		<pubDate>Fri, 21 Aug 2009 07:37:37 +0000</pubDate>
		<dc:creator>knuthellan</dc:creator>
				<category><![CDATA[deployment]]></category>
		<category><![CDATA[provisioning]]></category>

		<guid isPermaLink="false">http://knuthellan.com/?p=209</guid>
		<description><![CDATA[I currently use some shell scripts for deployment. This works well on my development server. It also works when starting to run things in a data center, but there are limitations and shortcomings. For one, my deployment scripts do not have any verification. I can add that, but then again I would be duplicating work [...]<img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=knuthellan.com&amp;blog=6371883&amp;post=209&amp;subd=knuthellan&amp;ref=&amp;feed=1" width="1" height="1" />]]></description>
			<content:encoded><![CDATA[<p>I currently use some shell scripts for deployment. This works well on my development server. It also works when starting to run things in a data center, but there are limitations and shortcomings. For one, my deployment scripts do not have any verification. I can add that, but then again I would be duplicating work that others have done well before. More important is the fact that my deployment scripts assume a prepared installation environment with all the packages and gems needed installed. I want a deployment system that also provisions the target server with all my prerequisites like Apache or nginx, Passenger Phusion, Sinatra and CouchDB. Note that our application does not use Rails, the storage is handled by CouchDB and non-UI work is handled by racks running directly in Phusion. The UI is handled by Sinatra.</p>
<p>We are looking at running our service in Amazon EC2 when we are ready to launch. As we use Fedora and Ubuntu for development, the obvious candidate distros are the same or related distros like CentOS, RHEL and Debian. If the deployment system handles package installation through an abstractions system, this system must support both yum and apt. </p>
<p><a href="http://wiki.opscode.com/display/chef/Home">Chef</a><br />
Chef is built around a chef server hosting the deployment scripts (cookbooks) and chef clients managing the worker nodes. It uses CouchDB in the background for storing information. From what I see, Chef is very powerful and looks like a nice way of handling huge clusters. The power also seems to have the side-effects that doing simple things in a smallish cluster is a lot of work. Uses <a href="http://rake.rubyforge.org/">Rake</a> underneath</p>
<p><a href="http://www.capify.org/">Capistrano</a><br />
Capistrano uses it&#8217;s own DSL in combination with Ruby. Capistrano is Rails-centric, but have instructions showing how to use it for non-Rails deployment. The deployment instructions mostly run shell scripts which makes it very flexible.</p>
<p><a href="http://rubyhitsquad.com/Vlad_the_Deployer.html">Vlad the Deployer</a><br />
Vlad seems to be an extension to <a href="http://rake.rubyforge.org/">Rake</a>. Aims to be a better Capistrano. It seems very subversion oriented. One of the features is <strong>Turnkey deployment for mongrel+apache+svn</strong>. Since we&#8217;re not using mongrel or svn and although we use apache now might switch to nginx soon, this all seems wrong.</p>
<p><a href="http://reductivelabs.com/trac/puppet/">Puppet</a><br />
Puppet is an extremely flexible and powerful provisioning system. However, as is often the case, this comes at a cost. Doing simple things like our deployment is very time consuming in Puppet. It uses it&#8217;s proprietary DSL to specify very accurately how the target environment should be after running puppet. It is capable of running in the background and syncing automatically every 30 minutes or so. The feature set is overkill in our case right now and if we need those features later I&#8217;ll be a happy man who will gladly spend time moving to puppet.</p>
<p><a href="http://github.com/crafterm/sprinkle/tree/master">Sprinkle</a><br />
Sprinkle uses Ruby directly instead of a task specific DSL. Full support of the main deployment systems is included. What puzzled me at first was that it supports Capistrano and Vlad the deployer for remote command delivery. Later I realized that Sprinkle is a provisioning system and leaves application deployment to other specialized systems. Nice separation.</p>
<p><a href="http://github.com/railsmachine/moonshine/tree/master">Moonshine</a><br />
One of requirements of Moonshine is <strong>A server running Ubuntu 8.10</strong> which effectively disqualifies it for our use. I&#8217;m sure supporting other distros is not much work, but I&#8217;m not willing to do that work at this point anyway.</p>
<p><a href="http://github.com/benschwarz/passenger-stack/tree/master">Passenger_stack</a><br />
Might seem weird to include this in the list, but we use Passenger and anything that can get us there faster is considered. This would get us apache, passenger and lots of other things we do not use like MySQL. Since I do not want to install things we don&#8217;t need and passenger_stack only works on Ubuntu (and not even Debian), it is disqualified.</p>
<p><strong>Conclusion</strong><br />
With Moonshine out due to lack of distro support and Vlad the Deployer out due to being mongrel+svn focused and puppet being too difficult, the following candidates are still contestants: Chef, Capistrano and Sprinkle.</p>
<p>Sprinkle has nice dry-run functionality allowing you to see what will happen to your remote servers before you do anything. Capistrano gets praise for easy of application deployment so the Sprinkle and Capistrano combo sounds interesting. My choice is now down to Sprinkle or Chef for the server provisioning and the winner is Sprinkle. The reason for my choice is that it is simpler and does what I currently need it to do. Migrating to Chef in the future is still an option.</p>
<br />  <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/knuthellan.wordpress.com/209/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/knuthellan.wordpress.com/209/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/knuthellan.wordpress.com/209/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/knuthellan.wordpress.com/209/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gofacebook/knuthellan.wordpress.com/209/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/facebook/knuthellan.wordpress.com/209/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gotwitter/knuthellan.wordpress.com/209/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/twitter/knuthellan.wordpress.com/209/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/knuthellan.wordpress.com/209/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/knuthellan.wordpress.com/209/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/knuthellan.wordpress.com/209/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/knuthellan.wordpress.com/209/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/knuthellan.wordpress.com/209/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/knuthellan.wordpress.com/209/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=knuthellan.com&amp;blog=6371883&amp;post=209&amp;subd=knuthellan&amp;ref=&amp;feed=1" width="1" height="1" />]]></content:encoded>
			<wfw:commentRss>http://knuthellan.com/2009/08/21/finding-the-one-in-an-ocean-of-provisioning-and-deployment-systems/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
	
		<media:content url="" medium="image">
			<media:title type="html">knuthellan</media:title>
		</media:content>
	</item>
		<item>
		<title>The CouchDB indexer &#8211; lightweight search engine in hours</title>
		<link>http://knuthellan.com/2009/07/09/the-couchdb-indexer-lightweight-search-engine-in-hours/</link>
		<comments>http://knuthellan.com/2009/07/09/the-couchdb-indexer-lightweight-search-engine-in-hours/#comments</comments>
		<pubDate>Thu, 09 Jul 2009 10:55:42 +0000</pubDate>
		<dc:creator>knuthellan</dc:creator>
				<category><![CDATA[code example]]></category>
		<category><![CDATA[couchdb]]></category>

		<guid isPermaLink="false">http://knuthellan.com/?p=185</guid>
		<description><![CDATA[Have you ever been in a situation where you needed to create a reverse lookup index of some documents you had lying around? A reverse lookup index is the kind of index used by the search engines (or Googles if you like) of this world. Creating a reverse lookup index isn&#8217;t hard, but you would [...]<img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=knuthellan.com&amp;blog=6371883&amp;post=185&amp;subd=knuthellan&amp;ref=&amp;feed=1" width="1" height="1" />]]></description>
			<content:encoded><![CDATA[<p>Have you ever been in a situation where you needed to create a reverse lookup index of some documents you had lying around?</p>
<p>A reverse lookup index is the kind of index used by the search engines (or Googles if you like) of this world. Creating a reverse lookup index isn&#8217;t hard, but you would normally expect to spend a couple of weeks writing code to create one when needed unless you decide to use an existing indexer like lucene. Using lucene is a bit heavy if the indexing is not core to your application. If you store your documents in CouchDB, you can create an indexer writing just 4 lines of JavaScript. You should have at least one more line for safeguarding your input values, but a search engine indexer in 5 lines of JavaScript is IMHO still pretty good.</p>
<p>The prerequisite for this indexer is that the documents you want to index all have a vector field containing a document vector in the form of a hash mapping term to term weight {&lt;term0&gt; =&gt; &lt;tweight0&gt;, &lt;tterm1&gt; =&gt; &lt;tweight1&gt;, &#8230;,&lt;ttermn&gt; =&gt; &lt;tweightn&gt;}. The weight could be just the number of times a term occurs in the document or a some metric indicating how important a word is to a document relative to all possible documents. The traditional weight used in search engines is tf-idf (term frequency multiplied by inverse document frequency). Head over to <a href="http://en.wikipedia.org/wiki/Tf%E2%80%93idf">Wikipedia</a> if you want to learn more about tf-idf. Of course, if you just want to find all documents matching a query, you can ignore the weights completely.</p>
<p>If you have document vectors, you have all the input data you need to create a reverse lookup index. This is the mapper if you just want to get all documents matching a query. Note that it completely ignores the term weights: </p>
<pre><code>
function(doc) {
    if (!('vector' in doc)) return;
    var vector = doc.vector;
    for (var term in vector) {
        emit(term, doc._id);
    }
}
</code></pre>
<p>The function operates on each document in the database. The first statement is a simple safeguard ensuring that we don&#8217;t try to access vector if the document doesn&#8217;t have that property. Since CouchDB is schema free, different types of documents with different fields may be stored in the same database. If the vector property is there, we store it in a variable called vector. For each element in vector, we emit the key which is the term and the id of the document we are operating on. If you run just this mapper, you will get a list of term to single document id mappings. This is a major step forward since we now have a reverse mapping of the database. </p>
<p>To get the reverse database map into something that is quick and easy to lookup, we need a reducer. It&#8217;s purpose is to convert the list of term, document id pairs into a single term to document id list.</p>
<pre><code>
function(keys, values) {
    var docs = [];
    for (var i = 0; i &lt; values.length; ++i) {
        docs.push(values[i]);
    }
    return docs;
}
</code></pre>
<p>In this situation, we don&#8217;t care about the keys. CouchDB handles that for us. We need an array to store all the document ids. Then we iterate over all the values and push them into our array. Finally we return the array. CouchDB ensures that this is only called once for a single term ensuring we end up with a single document id list for each term.</p>
<p>This index can be used to find all documents matching a given set of terms. Note that there is not much sophistication in this method so the only rank score you can get is the number of matching terms. Adding term weights to the index will give you something to use for ranking. Change the emit line in the mapper to<br />
<code><br />
emit(term, [doc._id, vector[term]]);<br />
</code><br />
This will give you a list of document id, term weight pairs for each term instead of just the document ids.</p>
<p>That mapper is 7 lines of code, 4 lines if you don&#8217;t count the function declaration line and lines only containing curly braces. Ignoring the safe guard as well, the mapper body can be reduced to this single line by also skipping the temporary variable assignment:<br />
<code><br />
for (var term in doc.vector) emit(term, [doc._id, doc.vector[term]]);<br />
</code><br />
In the same manner, the reduce function may be reduced to a body of just 3 lines of code<br />
<code><br />
var docs = [];<br />
for (var i = 0; i &lt; values.length; ++i) docs.push(values[i]);<br />
return docs;<br />
</code><br />
That&#8217;s the power and beauty of CouchDB map reduce. You can write a search engine indexer in 4 lines of JavaScript. Granted, you need to create vectors of your documents in advance, but that&#8217;s just a matter of parsing a text string and splitting on whitespace and/or punctuation into an array and reducing the term array into a hash of &lt;term&gt; =&gt; &lt;weight&gt; pairs. Sure, you can do this with a map reduce as well, but that might be overkill since you will only be operating on a single document at a time.</p>
<p>One last important point is that while Futon, the CouchDB browser client will show the expected results, you have to explicitly tell CouchDB to group the result if you want to use this in your application. My database is called <code>pages</code>, the design is called <code>demos</code> and the view <code>index</code> making the output of the map reduce available as json at<br />
<code>http://localhost:5984/pages/_design/demos/_view/index?group=true</code><br />
Thanks to <a href="http://twitter.com/jchris">J. Chris Anderson</a> for clarifying and pointing out the grouping query usage.</p>
<br />  <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/knuthellan.wordpress.com/185/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/knuthellan.wordpress.com/185/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/knuthellan.wordpress.com/185/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/knuthellan.wordpress.com/185/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gofacebook/knuthellan.wordpress.com/185/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/facebook/knuthellan.wordpress.com/185/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gotwitter/knuthellan.wordpress.com/185/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/twitter/knuthellan.wordpress.com/185/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/knuthellan.wordpress.com/185/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/knuthellan.wordpress.com/185/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/knuthellan.wordpress.com/185/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/knuthellan.wordpress.com/185/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/knuthellan.wordpress.com/185/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/knuthellan.wordpress.com/185/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=knuthellan.com&amp;blog=6371883&amp;post=185&amp;subd=knuthellan&amp;ref=&amp;feed=1" width="1" height="1" />]]></content:encoded>
			<wfw:commentRss>http://knuthellan.com/2009/07/09/the-couchdb-indexer-lightweight-search-engine-in-hours/feed/</wfw:commentRss>
		<slash:comments>9</slash:comments>
	
		<media:content url="" medium="image">
			<media:title type="html">knuthellan</media:title>
		</media:content>
	</item>
	</channel>
</rss>
