<?xml version='1.0' encoding='UTF-8'?><?xml-stylesheet href="http://www.blogger.com/styles/atom.css" type="text/css"?><feed xmlns='http://www.w3.org/2005/Atom' xmlns:openSearch='http://a9.com/-/spec/opensearchrss/1.0/' xmlns:georss='http://www.georss.org/georss' xmlns:gd='http://schemas.google.com/g/2005' xmlns:thr='http://purl.org/syndication/thread/1.0'><id>tag:blogger.com,1999:blog-15616477</id><updated>2011-11-28T02:28:31.192+02:00</updated><category term='rubykaigi'/><category term='ruby'/><category term='neverblock'/><category term='AOP'/><category term='activerecord'/><category term='postgres'/><category term='LBS'/><category term='dom'/><category term='javascript'/><category term='Visa'/><category term='ramaze'/><category term='asynchronous'/><category term='connection'/><category term='generator expressions'/><category term='apple'/><category term='CHAM'/><category term='web servers'/><category term='gaza'/><category term='event based I/O'/><category term='continuations'/><category term='tomcat'/><category term='benchmark'/><category term='macruby'/><category term='fibers'/><category term='merb'/><category term='document matching'/><category term='sequel'/><category term='tf-idf'/><category term='freedom'/><category term='palestine'/><category term='appengines'/><category term='cocoa'/><category term='rev'/><category term='iphone'/><category term='passenger'/><category term='python'/><category term='nginx'/><category term='rails'/><category term='sun'/><category term='nonblocking'/><category term='performance'/><category term='programming language'/><category term='reactor pattern'/><category term='meowns'/><category term='wenear'/><category term='chemical programming'/><category term='unicycle'/><category term='Ocaml'/><category term='memory leak'/><category term='generators'/><category term='threads'/><category term='scalability'/><category term='mysql'/><category term='java'/><category term='GAMMA'/><category term='eventmachine'/><category term='thin'/><category term='ruby 1.9'/><category term='property'/><category term='arabesque'/><category term='multicore'/><category term='delivery'/><category term='extend'/><category term='jvm'/><category term='information retreival'/><category term='javascript. Aspect oriented programming'/><category term='rest'/><category term='pagination'/><category term='Rubyconf 2008'/><category term='android'/><category term='Hamza'/><category term='appengine'/><category term='alsaha.com'/><category term='groovy'/><category term='baby'/><category term='mongrel'/><category term='espace'/><category term='location based services'/><category term='gcc'/><category term='asymy'/><category term='massacre'/><category term='webrick'/><category term='US'/><category term='caching'/><category term='ruby 1.9.1'/><category term='ansynchronous'/><category term='prototype'/><category term='interest'/><category term='google'/><title type='text'>oldmoe</title><subtitle type='html'></subtitle><link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='http://oldmoe.blogspot.com/feeds/posts/default'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/15616477/posts/default?max-results=100'/><link rel='alternate' type='text/html' href='http://oldmoe.blogspot.com/'/><link rel='hub' href='http://pubsubhubbub.appspot.com/'/><author><name>oldmoe</name><uri>http://www.blogger.com/profile/14341721253106429644</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='24' height='32' src='http://3.bp.blogspot.com/_XFDHsTWrvBY/SsFeIEFTI_I/AAAAAAAAADU/NphzXmrghkw/S220/oldmoe.png'/></author><generator version='7.00' uri='http://www.blogger.com'>Blogger</generator><openSearch:totalResults>68</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>100</openSearch:itemsPerPage><entry><id>tag:blogger.com,1999:blog-15616477.post-4978923678994878221</id><published>2010-04-09T01:30:00.002+02:00</published><updated>2010-04-09T01:37:12.048+02:00</updated><title type='text'>New Reactor Release</title><content type='html'>&lt;div&gt;Reactor has reached version 0.4, with this release we see a few notable features:&lt;/div&gt;&lt;br /&gt;&lt;ol&gt;&lt;br /&gt;&lt;li&gt;&lt;div&gt;More efficient timer implementation&lt;/div&gt;&lt;br /&gt;&lt;p&gt;&lt;br /&gt;  In this release timers are removed immediately once they are cancelled, this is done is a (semi) efficient manner, more importantly it removes the overhead of lingering cancelled timers. This change enables the implementation of connection timeouts efficiently.&lt;/p&gt;&lt;br /&gt;&lt;/li&gt;&lt;br /&gt;&lt;li&gt;&lt;div&gt;The reactor loop is now fiber (not thread) re-entrant&lt;/div&gt;&lt;br /&gt;&lt;p&gt;&lt;br /&gt; The reactor now may be run inside a fiber and when that fiber yields another one can grab the reactor and call run on it and it will just continue where it left off. This feature enables the building of fibered servers with near zero overhead for fast non-blocking connections.&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;Here's how a fibered server would be written normally&lt;/p&gt;&lt;br /&gt;&lt;pre class="code"&gt;&lt;br /&gt; @socket = TCPServer.new(..,..)&lt;br /&gt; @reactor = Reactor::Base.new&lt;br /&gt; @reactor.attach(:read, @socket) do&lt;br /&gt;  conn = @socket.accept&lt;br /&gt;  Fiber.new do&lt;br /&gt;   # handle connection here&lt;br /&gt;  end.resume&lt;br /&gt; end&lt;br /&gt; @reactor.run&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;p&gt;&lt;br /&gt;As you can see, we spawn a fiber for each connection which adds up when we have many of those since each fiber requires a 4KB stack&lt;br /&gt;&lt;/p&gt;&lt;br /&gt;&lt;p&gt;&lt;br /&gt;Now it can be done like that&lt;/p&gt;&lt;br /&gt;&lt;pre class="code"&gt;&lt;br /&gt; @socket = TCPServer.new(..,..)&lt;br /&gt; @reactor = Reactor::Base.new&lt;br /&gt; @reactor.attach(:read, @socket) do&lt;br /&gt;  conn = @socket.accept&lt;br /&gt;  # handle connection here&lt;br /&gt; end&lt;br /&gt; loop do&lt;br /&gt;  Fiber.new do&lt;br /&gt;   @reactor.run&lt;br /&gt;  end&lt;br /&gt; end&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;p&gt;&lt;br /&gt;Now the whole reactor loop runs in a fiber, if the connection does not block then it will run as if there is no fiber overhead, only when a connection blocks the reactor loop will break, a new fiber will be created and it will run the reactor again. This virtually removes the fiber overheads for non-blocking connections&lt;br /&gt;&lt;/p&gt;&lt;br /&gt;&lt;/li&gt;&lt;br /&gt;&lt;br /&gt;&lt;li&gt;&lt;div&gt;Reactor#next_tick is now thread safe&lt;/div&gt;&lt;br /&gt;&lt;p&gt;&lt;br /&gt; When trying to access the reactor from other threads you can now schedule events using next_tick which will make sure the event is put in place gracefuly even in the presence of multiple threads fighting for the reactor. That's it though, you cannot safely access any other reactor methods from multiple threads, and it is up to you to ensure that the block provided to next_tick wont try to access variables that are shared among threads in an unsafe manner (it will be run in the context of the reactor's thread).&lt;/p&gt;&lt;br /&gt;&lt;/li&gt;&lt;br /&gt;&lt;/ol&gt;&lt;br /&gt;&lt;p&gt;That's beside a few bug fixes here and there.&lt;p&gt; &lt;br /&gt;&lt;br /&gt;Grab it from &lt;a href="http://github.com/oldmoe/reactor"&gt;here&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/15616477-4978923678994878221?l=oldmoe.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://oldmoe.blogspot.com/feeds/4978923678994878221/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=15616477&amp;postID=4978923678994878221' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/15616477/posts/default/4978923678994878221'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/15616477/posts/default/4978923678994878221'/><link rel='alternate' type='text/html' href='http://oldmoe.blogspot.com/2010/04/new-reactor-release.html' title='New Reactor Release'/><author><name>oldmoe</name><uri>http://www.blogger.com/profile/14341721253106429644</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='24' height='32' src='http://3.bp.blogspot.com/_XFDHsTWrvBY/SsFeIEFTI_I/AAAAAAAAADU/NphzXmrghkw/S220/oldmoe.png'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-15616477.post-8637129989680891865</id><published>2009-10-07T20:25:00.004+02:00</published><updated>2009-10-12T20:04:05.990+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ruby'/><category scheme='http://www.blogger.com/atom/ns#' term='mongrel'/><category scheme='http://www.blogger.com/atom/ns#' term='ruby 1.9'/><category scheme='http://www.blogger.com/atom/ns#' term='webrick'/><category scheme='http://www.blogger.com/atom/ns#' term='passenger'/><category scheme='http://www.blogger.com/atom/ns#' term='thin'/><category scheme='http://www.blogger.com/atom/ns#' term='web servers'/><title type='text'>The Ruby 19x Web Servers Booklet</title><content type='html'>I have just finished a draft of my Ruby web server review, I have uploaded it to my &lt;a href="http://www.scribd.com/oldmoe"&gt;scribd&lt;/a&gt; account, This is still a work in progress and I am looking at improving it further and may be include Unicorn tests as well.&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Here is the document:&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;a title="View The Ruby 19x Web Servers Booklet on Scribd" href="http://www.scribd.com/doc/20755982/The-Ruby-19x-Web-Servers-Booklet" style="margin: 12px auto 6px auto; font-family: Helvetica,Arial,Sans-serif; font-style: normal; font-variant: normal; font-weight: normal; font-size: 14px; line-height: normal; font-size-adjust: none; font-stretch: normal; -x-system-font: none; display: block; text-decoration: underline;"&gt;The Ruby 19x Web Servers Booklet&lt;/a&gt; &lt;object codebase="http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=9,0,0,0" id="doc_592426280529560" name="doc_592426280529560" classid="clsid:d27cdb6e-ae6d-11cf-96b8-444553540000" align="middle" height="500" width="100%" style="width: 100%; height: 247px; "&gt;  &lt;param name="movie" value="http://d1.scribdassets.com/ScribdViewer.swf?document_id=20755982&amp;amp;access_key=key-11g4fpp0rh82bj7p3wko&amp;amp;page=1&amp;amp;version=1&amp;amp;viewMode="&gt;   &lt;param name="quality" value="high"&gt;   &lt;param name="play" value="true"&gt;  &lt;param name="loop" value="true"&gt;   &lt;param name="scale" value="showall"&gt;  &lt;param name="wmode" value="opaque"&gt;   &lt;param name="devicefont" value="false"&gt;  &lt;param name="bgcolor" value="#ffffff"&gt;   &lt;param name="menu" value="true"&gt;  &lt;param name="allowFullScreen" value="true"&gt;   &lt;param name="allowScriptAccess" value="always"&gt;   &lt;param name="salign" value=""&gt;        &lt;embed src="http://d1.scribdassets.com/ScribdViewer.swf?document_id=20755982&amp;amp;access_key=key-11g4fpp0rh82bj7p3wko&amp;amp;page=1&amp;amp;version=1&amp;amp;viewMode=" quality="high" pluginspage="http://www.macromedia.com/go/getflashplayer" play="true" loop="true" scale="showall" wmode="opaque" devicefont="false" bgcolor="#ffffff" name="doc_592426280529560_object" menu="true" allowfullscreen="true" allowscriptaccess="always" salign="" type="application/x-shockwave-flash" align="middle" height="500" width="100%"&gt;&lt;/embed&gt; &lt;/object&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;While at the topic of Ruby web servers, I highly recommend &lt;a href="http://tomayko.com/writings/unicorn-is-unix"&gt;this&lt;/a&gt; article by Ryan Tomayko on Unicorn's architecture.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Edit: I didn't know that scribd requires you to login before you can download the files, here is a direct &lt;a href="http://oldmoe.googlepages.com/theruby19xwebserversbooklet.pdf"&gt;link&lt;/a&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/15616477-8637129989680891865?l=oldmoe.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://oldmoe.blogspot.com/feeds/8637129989680891865/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=15616477&amp;postID=8637129989680891865' title='20 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/15616477/posts/default/8637129989680891865'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/15616477/posts/default/8637129989680891865'/><link rel='alternate' type='text/html' href='http://oldmoe.blogspot.com/2009/10/ruby-19x-web-servers-booklet.html' title='The Ruby 19x Web Servers Booklet'/><author><name>oldmoe</name><uri>http://www.blogger.com/profile/14341721253106429644</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='24' height='32' src='http://3.bp.blogspot.com/_XFDHsTWrvBY/SsFeIEFTI_I/AAAAAAAAADU/NphzXmrghkw/S220/oldmoe.png'/></author><thr:total>20</thr:total></entry><entry><id>tag:blogger.com,1999:blog-15616477.post-6377933919771550132</id><published>2009-09-29T02:00:00.002+02:00</published><updated>2009-09-29T02:11:35.410+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ruby'/><category scheme='http://www.blogger.com/atom/ns#' term='alsaha.com'/><category scheme='http://www.blogger.com/atom/ns#' term='rails'/><category scheme='http://www.blogger.com/atom/ns#' term='neverblock'/><title type='text'>NeverBlock Saves The Day</title><content type='html'>&lt;b&gt;It started with too many processes&lt;br /&gt;&lt;/b&gt;&lt;br /&gt;A great proof of how valuable &lt;a href="http://github.com/oldmoe/neverblock"&gt;NeverBlock&lt;/a&gt; is happend just a little while ago. &lt;a href="http://www.alsaha.com"&gt;Alsaha.com&lt;/a&gt; is one of the oldest forums in the middle east. Lately, I helped rebuild the whole thing and move it to Ruby on Rails while I was at eSpace, my previous employer. &lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Many web users rely on sites like Alsaha.com for following commentary on breaking news, thus during such events the daily page views can jump from a couple hundred thousands to millions. Add to that the fact that there are some (unavoidable) slow database operations that must be done online for the administrators. &lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Initially we had different web server instances for the normal users and administrators to avoid stalling Rails processes on long running queries. All in all, since we had a capable back end, we coupled it with a formidable number of Rails processes as front end servers. This was the only way back then to exploit our back end's conncurency. Needless to say, this army of front end processes consumed lots and lots of memory.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Enter NeverBlock&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;After the initial production testing in &lt;a href="http://www.meowns.com"&gt;meOwns.com&lt;/a&gt;, we thought of the gains we can get from using it with Alsaha, so we planned for the move and were able to drastically reduce the Rails instances count. We only use 4 now and this is for utilizing the CPU cores rather than concurrency. Those 4 processes serve all the user and administrative content. Thanks to NeverBlock, no one has to wait for the other.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;The Real Test&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;There was a very important news item lately that resulted in a traffic spike in the order of millions of page views in a few hours. Thankfully, the NeverBlock setup easily accomodated that without noticeable degradation (actually there was some degradation attributed to a bug in the logging logic, that was quickly discovered and fixed). The small 4 instances kept up with the load even though some slow operations were running on them while they were serving loads of quick ones to lots of users.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Conclusion&lt;br /&gt;&lt;/b&gt;&lt;br /&gt;I am grateful to have written code that turned out to be useful to others. I hope this would be a pattern.&lt;br /&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/15616477-6377933919771550132?l=oldmoe.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://oldmoe.blogspot.com/feeds/6377933919771550132/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=15616477&amp;postID=6377933919771550132' title='8 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/15616477/posts/default/6377933919771550132'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/15616477/posts/default/6377933919771550132'/><link rel='alternate' type='text/html' href='http://oldmoe.blogspot.com/2009/09/neverblock-saves-day.html' title='NeverBlock Saves The Day'/><author><name>oldmoe</name><uri>http://www.blogger.com/profile/14341721253106429644</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='24' height='32' src='http://3.bp.blogspot.com/_XFDHsTWrvBY/SsFeIEFTI_I/AAAAAAAAADU/NphzXmrghkw/S220/oldmoe.png'/></author><thr:total>8</thr:total></entry><entry><id>tag:blogger.com,1999:blog-15616477.post-8189069436484437666</id><published>2009-09-19T03:42:00.004+02:00</published><updated>2009-09-19T06:07:33.727+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ruby'/><category scheme='http://www.blogger.com/atom/ns#' term='rubykaigi'/><category scheme='http://www.blogger.com/atom/ns#' term='neverblock'/><category scheme='http://www.blogger.com/atom/ns#' term='arabesque'/><title type='text'>My RubyKaigi 2009 Presentations</title><content type='html'>Better late than never :)&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;I totally forgot to link to my RubyKaigi 2009 presentations, so without further ado, here they are:&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;NeverBlock, the video:&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;embed flashvars="loc=%2F&amp;amp;autoplay=false&amp;amp;vid=1827568" width="435" height="350" allowfullscreen="true" allowscriptaccess="always" src="http://www.ustream.tv/flash/video/1827568" type="application/x-shockwave-flash"&gt;&lt;/embed&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;NeverBlock, the slides:&lt;/div&gt;&lt;a title="View NeverBlock-RubyKaigi2009 on Scribd" href="http://www.scribd.com/doc/18166746/NeverBlockRubyKaigi2009" style="margin: 12px auto 6px auto; font-family: Helvetica,Arial,Sans-serif; font-style: normal; font-variant: normal; font-weight: normal; font-size: 14px; line-height: normal; font-size-adjust: none; font-stretch: normal; -x-system-font: none; display: block; text-decoration: underline;"&gt;NeverBlock-RubyKaigi2009&lt;/a&gt; &lt;object codebase="http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=9,0,0,0" id="doc_879545986578693" name="doc_879545986578693" classid="clsid:d27cdb6e-ae6d-11cf-96b8-444553540000" align="middle" height="435" width="435" &gt;  &lt;param name="movie" value="http://d1.scribdassets.com/ScribdViewer.swf?document_id=18166746&amp;access_key=key-24vsspsx1dt1331gb6zt&amp;page=1&amp;version=1&amp;viewMode=slideshow"&gt;   &lt;param name="quality" value="high"&gt;   &lt;param name="play" value="true"&gt;  &lt;param name="loop" value="true"&gt;   &lt;param name="scale" value="showall"&gt;  &lt;param name="wmode" value="opaque"&gt;   &lt;param name="devicefont" value="false"&gt;  &lt;param name="bgcolor" value="#ffffff"&gt;   &lt;param name="menu" value="true"&gt;  &lt;param name="allowFullScreen" value="true"&gt;   &lt;param name="allowScriptAccess" value="always"&gt;   &lt;param name="salign" value=""&gt;            &lt;param name="mode" value="slideshow"&gt;       &lt;embed src="http://d1.scribdassets.com/ScribdViewer.swf?document_id=18166746&amp;access_key=key-24vsspsx1dt1331gb6zt&amp;page=1&amp;version=1&amp;viewMode=slideshow" quality="high" pluginspage="http://www.macromedia.com/go/getflashplayer" play="true" loop="true" scale="showall" wmode="opaque" devicefont="false" bgcolor="#ffffff" name="doc_879545986578693_object" menu="true" allowfullscreen="true" allowscriptaccess="always" salign="" type="application/x-shockwave-flash" align="middle" mode="slideshow" height="500" width="435"&gt;&lt;/embed&gt; &lt;/object&gt;   &lt;br /&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Arabesque, the slides&lt;/div&gt;&lt;div&gt;&lt;a title="View Arabesque-RubyKaigi2009 on Scribd" href="http://www.scribd.com/doc/18166761/ArabesqueRubyKaigi2009" style="margin: 12px auto 6px auto; font-family: Helvetica,Arial,Sans-serif; font-style: normal; font-variant: normal; font-weight: normal; font-size: 14px; line-height: normal; font-size-adjust: none; font-stretch: normal; -x-system-font: none; display: block; text-decoration: underline;"&gt;Arabesque-RubyKaigi2009&lt;/a&gt; &lt;object codebase="http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=9,0,0,0" id="doc_911458530039683" name="doc_911458530039683" classid="clsid:d27cdb6e-ae6d-11cf-96b8-444553540000" align="middle" height="435" width="435" &gt;  &lt;param name="movie" value="http://d1.scribdassets.com/ScribdViewer.swf?document_id=18166761&amp;access_key=key-22lfitya4nrdaf1wvgg6&amp;page=1&amp;version=1&amp;viewMode=slideshow"&gt;   &lt;param name="quality" value="high"&gt;   &lt;param name="play" value="true"&gt;  &lt;param name="loop" value="true"&gt;   &lt;param name="scale" value="showall"&gt;  &lt;param name="wmode" value="opaque"&gt;   &lt;param name="devicefont" value="false"&gt;  &lt;param name="bgcolor" value="#ffffff"&gt;   &lt;param name="menu" value="true"&gt;  &lt;param name="allowFullScreen" value="true"&gt;   &lt;param name="allowScriptAccess" value="always"&gt;   &lt;param name="salign" value=""&gt;            &lt;param name="mode" value="slideshow"&gt;       &lt;embed src="http://d1.scribdassets.com/ScribdViewer.swf?document_id=18166761&amp;access_key=key-22lfitya4nrdaf1wvgg6&amp;page=1&amp;version=1&amp;viewMode=slideshow" quality="high" pluginspage="http://www.macromedia.com/go/getflashplayer" play="true" loop="true" scale="showall" wmode="opaque" devicefont="false" bgcolor="#ffffff" name="doc_911458530039683_object" menu="true" allowfullscreen="true" allowscriptaccess="always" salign="" type="application/x-shockwave-flash" align="middle" mode="slideshow" height="500" width="435"&gt;&lt;/embed&gt; &lt;/object&gt; &lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Enjoy&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/15616477-8189069436484437666?l=oldmoe.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://oldmoe.blogspot.com/feeds/8189069436484437666/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=15616477&amp;postID=8189069436484437666' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/15616477/posts/default/8189069436484437666'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/15616477/posts/default/8189069436484437666'/><link rel='alternate' type='text/html' href='http://oldmoe.blogspot.com/2009/09/my-rubykaigi-2009-presentations.html' title='My RubyKaigi 2009 Presentations'/><author><name>oldmoe</name><uri>http://www.blogger.com/profile/14341721253106429644</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='24' height='32' src='http://3.bp.blogspot.com/_XFDHsTWrvBY/SsFeIEFTI_I/AAAAAAAAADU/NphzXmrghkw/S220/oldmoe.png'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-15616477.post-4166665711251613793</id><published>2009-08-02T02:40:00.003+03:00</published><updated>2009-08-02T02:48:58.427+03:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ruby'/><category scheme='http://www.blogger.com/atom/ns#' term='gcc'/><category scheme='http://www.blogger.com/atom/ns#' term='ruby 1.9.1'/><title type='text'>Improving Ruby's VM performance using gcc profiling tools</title><content type='html'>Digging through some of the old Python archives I found really interesting post by Mike Pall of &lt;a href="http://luajit.org"&gt;LuaJit&lt;/a&gt;'s fame ( the post can be found &lt;a href="http://markmail.org/message/pgziyttfhv3o6xl4#query:Mike%20Pall+page:1+mid:afrkkojesc4gi5xb+state:results"&gt;here&lt;/a&gt; ). Pall was talking about possible ways to improve Python's performance. One of his findings was that the branching code was one of the major culprits and that affected Python's runtime performance. This is mainly due to the fact that it was not optimized for the commonly used paths. This is understandable, since a compiler like GCC will have no prior knowledge on how the binary will perform. So branch predicition remains as such, a predicition that might be skewed from the true state of affair. In his email Mike offered a technique to show that with a little compiler trickery this effect could be a little marginalized. I followed his steps but this time I did it for the Ruby 1.9.1 p129 binary.&lt;br /&gt;&lt;br /&gt;First I ran configure on the source code and in the Makefile I added &lt;a href="http://gcc.gnu.org/onlinedocs/gcc/Debugging-Options.html"&gt;-fprofile-arcs&lt;/a&gt; to optflags. Then make was run which resulted in a Ruby binary, lots of .o files and lots of .gcda and .gcno files.&lt;br /&gt;&lt;br /&gt;What we have now is a binary that will profile the branching behaviour of the application. Next we need to run the binary in a real life situation such that we it can record how branching will be done in the wild. I tried a simple benchmark applicaition that included lots of math and string operations. After doing that the make file was changed once more, this time -fprofile-arcs was replaced with -&lt;a href="http://gcc.gnu.org/onlinedocs/gcc/Optimize-Options.html#Optimize-Options"&gt;fbranch-probabilities&lt;/a&gt;. This asks the compiler to use the branching probabilities that were generated from the previous run.&lt;br /&gt;&lt;br /&gt;All .o files were deleted and make was run again resulting in a shiny new ruby binary that is ready for testing.&lt;br /&gt;&lt;br /&gt;I ran the new binary head to head against the regular one. I used several ruby scripts for that matter and the result was that the new binary was almost consistently faster than the old one, usually by a little margin but for one benchmark the difference reached 40%. Even though the code samples differed widely from the one used in the profiling it was apparent that the VM itself gained a little from this optimization which would cause all Ruby scripts to gain as well.&lt;br /&gt;&lt;br /&gt;At the end of the day, an average 5% improvement is not something to brag about but the fact remains that this shows that the Ruby VM still has a way to go. Considering that a silly, mostly static attempt did that I think that a JIT engine with tracing optimizations can do wonders for this little language. Since it will adapt to the application usage patterns and should be able to eliminate lots of the slow code paths in ways that static optimization cannot predict before hand.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/15616477-4166665711251613793?l=oldmoe.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://oldmoe.blogspot.com/feeds/4166665711251613793/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=15616477&amp;postID=4166665711251613793' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/15616477/posts/default/4166665711251613793'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/15616477/posts/default/4166665711251613793'/><link rel='alternate' type='text/html' href='http://oldmoe.blogspot.com/2009/08/improving-rubys-vm-performance-using.html' title='Improving Ruby&apos;s VM performance using gcc profiling tools'/><author><name>oldmoe</name><uri>http://www.blogger.com/profile/14341721253106429644</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='24' height='32' src='http://3.bp.blogspot.com/_XFDHsTWrvBY/SsFeIEFTI_I/AAAAAAAAADU/NphzXmrghkw/S220/oldmoe.png'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-15616477.post-1826716301986950529</id><published>2009-04-18T23:34:00.003+02:00</published><updated>2009-04-22T12:51:47.178+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='memory leak'/><category scheme='http://www.blogger.com/atom/ns#' term='extend'/><category scheme='http://www.blogger.com/atom/ns#' term='ruby 1.9.1'/><title type='text'>Object#extend leaks memory on Ruby 1.9.1</title><content type='html'>&lt;p&gt;The Garbage Collector is really strange business in the Ruby land. For me, it is the major performance drain currently. If you are not aware of the limitations here's a list:&lt;br /&gt;&lt;/p&gt;&lt;ol&gt;&lt;li&gt;The GC is mark and sweep, it needs to scan the whole heap for each run. It is directly affected by heap size O(n).&lt;/li&gt;&lt;li&gt;The GC cannot be interrupted and hence all threads must wait for it to finish (shameful pause on big heaps).&lt;/li&gt;&lt;li&gt;The GC marks objects in the objects themselves destroying any value of copy on write.&lt;/li&gt;&lt;li&gt;The GC does not (edit: usually) give memory back to the system. What goes in does not (edit: usually) go out.&lt;/li&gt;&lt;li&gt;It is a bit on the conservative side. Meaning garbage can stay because it is not sure that it is so.&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;&lt;/p&gt;&lt;br /&gt;&lt;p&gt;Needless to say, some of these are being addressed, specially &lt;a href="http://www.rubyenterpriseedition.com/"&gt;3&lt;/a&gt; and &lt;a href="http://sites.google.com/site/brentsrubypatches/"&gt;5&lt;/a&gt;. But the patches are not yet accepted in the current Ruby release. I believe though that they will find their way to 1.8.x which is being maintained by &lt;a href="http://www.engineyard.com/"&gt;Engine Yard&lt;/a&gt;. The EY guys are really working hard to solve the issues of Ruby as a server platform which is the most popular use for it today thanks to Rails.&lt;/p&gt;&lt;br /&gt;&lt;p&gt;Alas, my issue today involves Ruby 1.9.1 (it does not affect 1.8.x). See I have built this toy server to experiment with multi process applications and some Unix IPC facilities. I did make the design a bit modular to make it easier to test and debug different aspects of the stack. So I have these tcp, http handler modules that extend the connection object (a socket) whenever a connection is accepted. Here's a sample:&lt;/p&gt;&lt;br /&gt;&lt;pre class="code"&gt;conn = server_socket.accept&lt;br /&gt;conn.extend HttpHandler&lt;br /&gt;..&lt;br /&gt;..&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;This worked really great and I was even able to chain handlers to get more stack functionality (a handler will simply include those that it requires). This worked great, until I looked at memory usage.&lt;/p&gt;&lt;p&gt;I discovered that after showering the server with requests it will start to grow in size. This is acceptable as it is making way for new objects. But given the way the GC works it should have allocated enough heap locations after a few of those ab runs. On the contraty, even when I am hitting the same file with ab the server keeps growing. After 10 or more ab runes (each doing 10000 requests) it is still consuming more memory. So I suspected there is a leak some where. I tested a hello world and found that the increase was very consistent. Every 10K requests the process gains 0.1 to 0.2 MB. (10 to 20 Bytes per request). So I started removing components one after another till I was left with a bare server that only requires socket and &lt;a href="http://github.com/oldmoe/reactor/tree/master"&gt;reactor&lt;/a&gt;.&lt;/p&gt; &lt;p&gt;When I tested that server the process started to gain memory then after like 3 or 4 ab runs it stabilized. It would no longer increase its allocated memory no matter how many times I run ab on it. So the next logical move was to re-insert the first level of the stack (the tcp handler module). Once I did that the issue started appearing again. So the next test was to disable the use of the tcp handler but still decorate my connections with it. The issue still appeared. Since the module is not overriding Module.extended to do any work upon it extending an object it became clear that it is the guilty party.&lt;/p&gt;&lt;p&gt;Instead of Object#extend I tried reopening the BasicSocket class and including the required module there. After doing that memory usage pattern resembled the bare bones server. It would increase for a few runs and then remain flat as long as you are hitting the same request.&lt;/p&gt;&lt;p&gt;To isolate the problem further I created this script:&lt;/p&gt;&lt;pre class="code"&gt;# This code is Ruby 1.9.x and above only&lt;br /&gt;&lt;br /&gt;@extend = ARGV[0]&lt;br /&gt;&lt;br /&gt;module BetterHash&lt;br /&gt;  def blabla&lt;br /&gt;  end&lt;br /&gt;end&lt;br /&gt;&lt;br /&gt;unless @extend&lt;br /&gt;  class Hash&lt;br /&gt;    include BetterHash&lt;br /&gt;  end&lt;br /&gt;end&lt;br /&gt;&lt;br /&gt;t = Time.now&lt;br /&gt;1_000_000.times do&lt;br /&gt;   s = {}&lt;br /&gt;   s.extend BetterHash if @extend &lt;br /&gt;end&lt;br /&gt;after = Time.now - t&lt;br /&gt;puts "done with #{GC.count} gc runs after #{after} seconds"&lt;br /&gt;sleep # so that it doesn't exit before we check the memory&lt;/pre&gt;&lt;br /&gt;using extend:&lt;pre class="code"&gt;351 GC runs, 9.108 seconds, 18.7 MB&lt;/pre&gt;&lt;br /&gt;using include:&lt;pre class="code"&gt;117 GC runs, 0.198 seconds, 2.8 MB&lt;/pre&gt;&lt;br /&gt;&lt;p&gt;Besides being much faster, the resulting process was much smaller. Around 16MB smaller. I am suspecting that the leak is around 16 bytes or a little less per extend invokation. This means that a server that uses a single extend per request will increase around 160KB in size after every 10K requests. Not that huge but it will pile up fast if left for a while and the server is under heavy load. &lt;/p&gt;&lt;br /&gt;&lt;p&gt;&lt;br /&gt;A quick grep in Rails sources showed that this pattern is being used heavily throughout the code. But it is used to extend base classes rather than objects. Hence it will not be invoked on every request and the effect will be mostly limited to the initial start size (a few bytes actually). You should avoid using it dynamically at request serving time though, till it gets fixed.&lt;/p&gt; &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/15616477-1826716301986950529?l=oldmoe.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://oldmoe.blogspot.com/feeds/1826716301986950529/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=15616477&amp;postID=1826716301986950529' title='13 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/15616477/posts/default/1826716301986950529'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/15616477/posts/default/1826716301986950529'/><link rel='alternate' type='text/html' href='http://oldmoe.blogspot.com/2009/04/objectextend-leaks-memory-on-ruby-191.html' title='Object#extend leaks memory on Ruby 1.9.1'/><author><name>oldmoe</name><uri>http://www.blogger.com/profile/14341721253106429644</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='24' height='32' src='http://3.bp.blogspot.com/_XFDHsTWrvBY/SsFeIEFTI_I/AAAAAAAAADU/NphzXmrghkw/S220/oldmoe.png'/></author><thr:total>13</thr:total></entry><entry><id>tag:blogger.com,1999:blog-15616477.post-1824940333514829492</id><published>2009-04-13T18:09:00.004+02:00</published><updated>2009-04-13T18:31:46.441+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='reactor pattern'/><category scheme='http://www.blogger.com/atom/ns#' term='eventmachine'/><category scheme='http://www.blogger.com/atom/ns#' term='event based I/O'/><category scheme='http://www.blogger.com/atom/ns#' term='ruby 1.9.1'/><title type='text'>A fast, simple, pure Ruby reactor library</title><content type='html'>&lt;p&gt;Please welcome &lt;a href="http://github.com/oldmoe/reactor/tree/master"&gt;Reactor&lt;/a&gt;, a reactor library with the very original name of "Reactor".&lt;/p&gt;&lt;h3&gt;What is a reactor any way?&lt;br /&gt;&lt;br /&gt;&lt;/h3&gt;&lt;p&gt;A reactor library is one that provides an asynchronus event handling mechanism. Ruby already has a couple of those. The most prominent are EventMachine and Rev. &lt;/p&gt;&lt;p&gt;Many high performing Ruby applications like Thin and Evented Mongrel are utilizing EventMachine for event handling. Both Rev and EventMachine build atop native reactor implementations written in C or C++. While this ensures high performance it makes some integration aspects with Ruby a bit quirky. Sometimes&lt;br /&gt;even at a noticable performance cost.&lt;/p&gt;&lt;p&gt;This is why I thought of building Reactor. A much simpler reactor library in pure Ruby that attempts to use as much of the Ruby built in classes and standard libraries as possible. It only provides a minimal API that does not attempt to be so smart. It differs from EventMachine and Rev in the following aspects.&lt;br /&gt;&lt;ol&gt;&lt;br /&gt;&lt;li&gt;Pure Ruby, no C or C++ code involved&lt;/li&gt;&lt;li&gt;Very small (~100 lines of code)&lt;/li&gt;&lt;li&gt;Uses the vanilla Ruby socket and server implementations&lt;br /&gt;&lt;/li&gt;&lt;li&gt;Decent (high) performance on Ruby 1.9.1&lt;/li&gt;&lt;li&gt;Ruby threading friendly (naturally)&lt;br /&gt;&lt;/li&gt;&lt;li&gt;You can have multiple reactors running (like Rev and unlike EventMachine)&lt;/li&gt;&lt;/ol&gt;Usage is simple, here's a simple Echo server that uses Reactor&lt;br /&gt;&lt;pre class="code"&gt;require 'reactor'&lt;br /&gt;require 'socket'&lt;br /&gt;reactor = Reactor::Base.new&lt;br /&gt;server = TCPServer.new("0.0.0.0",8080)&lt;br /&gt;reactor.attach(:read, server) do |server|&lt;br /&gt;  conn = server.accept&lt;br /&gt;  conn.write(conn.gets)&lt;br /&gt;  conn.close&lt;br /&gt;end&lt;br /&gt;reactor.run # blocking call, will run for ever&lt;/pre&gt;&lt;br /&gt;&lt;p&gt;The server is a normal Ruby TCPServer. It attaches itself to the reactor and asks to be notified if there is data to be read on the wire. A block is provided that will handle those notifications. Alternatively, the server can implement a notify_readable method that will be fired instead.&lt;/p&gt;&lt;p&gt;Any IO object can be attached to the reactor but it doesn't make much sense to attach actual files since they will block upon reading or writing anyway. Sockets and pipes will work in a non-blocking manner though.&lt;/p&gt;&lt;p&gt;Reactor is using Ruby's IO.select behind the scenes. This limits its ability to scale in comparison to something like EventMachine or Rev which are able to utilize Epoll and Kqueue which scale much better. This is not a major concern though. Most servers listen to a few fds most of the time, which is a bit faster when using select. Besides one can hope that Ruby will be able to use Epoll and Kqueue some day which will translate to direct benefit to Reactor.&lt;br /&gt;&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/15616477-1824940333514829492?l=oldmoe.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://oldmoe.blogspot.com/feeds/1824940333514829492/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=15616477&amp;postID=1824940333514829492' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/15616477/posts/default/1824940333514829492'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/15616477/posts/default/1824940333514829492'/><link rel='alternate' type='text/html' href='http://oldmoe.blogspot.com/2009/04/fast-simple-pure-ruby-reactor-library.html' title='A fast, simple, pure Ruby reactor library'/><author><name>oldmoe</name><uri>http://www.blogger.com/profile/14341721253106429644</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='24' height='32' src='http://3.bp.blogspot.com/_XFDHsTWrvBY/SsFeIEFTI_I/AAAAAAAAADU/NphzXmrghkw/S220/oldmoe.png'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-15616477.post-8086647951269351988</id><published>2009-04-05T20:46:00.003+02:00</published><updated>2009-04-05T21:20:26.210+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Ocaml'/><category scheme='http://www.blogger.com/atom/ns#' term='eventmachine'/><category scheme='http://www.blogger.com/atom/ns#' term='unicycle'/><category scheme='http://www.blogger.com/atom/ns#' term='ruby 1.9.1'/><title type='text'>Ruby Strikes Back</title><content type='html'>If you are not following Mauricio Fernandez's &lt;a href="http://eigenclass.org/"&gt;blog&lt;/a&gt; then please do yourself a favor and subscribe to it. Mauricio's writings are very interesting and informative. In one of his &lt;a href="http://eigenclass.org/R2/writings/standalone-ocaml-webapps"&gt;posts&lt;/a&gt; Mauricio gives a record of re-implementing his blog in OCaml using the OCsigen (webserver + framework) library. Mauricio did some benchmarking for the OCsigen environment against Rails and even a C fastcgi implementation. Naturally one would expect that OCaml will be orders of magnitude faster than Ruby. But the benchmark showed really abysmal performance for Rail vs. OCsigen. We are talking 260 request per second vs. 4500 requests per second for a single process test! That's north of 20X difference! I decided that Ruby can do better.&lt;br /&gt;&lt;br /&gt;Looking at what OCsigen offers revealed that Rails is an overkill in comparison. I thought that for Ruby a nice alternative can be the mystery webserver + framework called unicycle (never heard of it? you don't know what you're missing). Since OCsigen offers LWt (a light weight cooperative threading library) at its core for concurrency I added a fiber wrapper to Unicycle's request processing path so that we get a similar overhead (the testing was done using Ruby 1.9.1). &lt;br /&gt;&lt;br /&gt;Here are my results:&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Hello World - Unicycle, Single Process: 7378 requests/second&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;Please note that this is running on my Intel Mobile Core 2 Duo 2.0 GHZ processor vs. the 3GHZ desktop AMD Athlon64 that was used for the original tests (it should be roughly 50% faster than my mobile core2).&lt;br /&gt;&lt;br /&gt;I decided to take it further still. Mauricio mentioned that he was able to get performance above 2000 req/s from OCsigen when benchmarking the blog page  that we are discussing here. So I created a sqlite database (he mentioned somewhere that he is using sqlite) and inserted the same blog entry (with very little modifications) in a structured manner. I didn't bother with comments though (out of being lazy).  Sequel was used to connect and fetch the record from the database and an rhtml template that is rendered using Erubis. The result was a page very similar to the original blog post. ApacheBench was used to benchmark the page.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Unicyle + Fibers + Sequel (Sqlite) + Erubis, Single Process: 1296 requests/second&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;During the time of testing the Unicycle process was between 13MB and 21MB (that's 3x to 4x the size of the OCsigen process) &lt;br /&gt;&lt;br /&gt;Considering that the components found in any laptop are usually inferior to their desktop counter parts I believe this at least equals the figure reported for OCsigen's performance.&lt;br /&gt;&lt;br /&gt;How can Ruby achieve such performance? By careful selection of components:&lt;br /&gt;&lt;br /&gt;First off, Ruby 1.9.1, everybody should start using it for their next project. It is much faster and much easier on memory&lt;br /&gt;&lt;br /&gt;Unicycle is built atop EventMachine and the EventMachine HTTP Server. Both are C based speed demons. Unicycle itself is a minimal framework that doesn't attempt to be so smart.&lt;br /&gt;&lt;br /&gt;Erubis is a nice surprise. Pure Ruby and decently fast are not commonly found together but kudos to the authors of Erubis, they somehow did it.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Conclusion&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;Ruby is faster the OCaml. Wrong! OCaml is a lot faster than Ruby. But thanks to hard work by some prominent Rubyists you can have a Ruby setup that performs decently enough to make you proud.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/15616477-8086647951269351988?l=oldmoe.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://oldmoe.blogspot.com/feeds/8086647951269351988/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=15616477&amp;postID=8086647951269351988' title='5 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/15616477/posts/default/8086647951269351988'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/15616477/posts/default/8086647951269351988'/><link rel='alternate' type='text/html' href='http://oldmoe.blogspot.com/2009/04/ruby-strikes-back.html' title='Ruby Strikes Back'/><author><name>oldmoe</name><uri>http://www.blogger.com/profile/14341721253106429644</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='24' height='32' src='http://3.bp.blogspot.com/_XFDHsTWrvBY/SsFeIEFTI_I/AAAAAAAAADU/NphzXmrghkw/S220/oldmoe.png'/></author><thr:total>5</thr:total></entry><entry><id>tag:blogger.com,1999:blog-15616477.post-4399362237842399648</id><published>2009-01-15T18:12:00.001+02:00</published><updated>2009-01-15T18:14:34.835+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='massacre'/><category scheme='http://www.blogger.com/atom/ns#' term='palestine'/><category scheme='http://www.blogger.com/atom/ns#' term='gaza'/><category scheme='http://www.blogger.com/atom/ns#' term='freedom'/><title type='text'>We will NOT go down in the night</title><content type='html'>&lt;object width="425" height="344"&gt;&lt;param name="movie" value="http://www.youtube.com/v/dlfhoU66s4Y&amp;hl=en&amp;fs=1"&gt;&lt;/param&gt;&lt;param name="allowFullScreen" value="true"&gt;&lt;/param&gt;&lt;param name="allowscriptaccess" value="always"&gt;&lt;/param&gt;&lt;embed src="http://www.youtube.com/v/dlfhoU66s4Y&amp;hl=en&amp;fs=1" type="application/x-shockwave-flash" allowscriptaccess="always" allowfullscreen="true" width="425" height="344"&gt;&lt;/embed&gt;&lt;/object&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/15616477-4399362237842399648?l=oldmoe.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://oldmoe.blogspot.com/feeds/4399362237842399648/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=15616477&amp;postID=4399362237842399648' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/15616477/posts/default/4399362237842399648'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/15616477/posts/default/4399362237842399648'/><link rel='alternate' type='text/html' href='http://oldmoe.blogspot.com/2009/01/we-will-not-go-down-in-night.html' title='We will NOT go down in the night'/><author><name>oldmoe</name><uri>http://www.blogger.com/profile/14341721253106429644</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='24' height='32' src='http://3.bp.blogspot.com/_XFDHsTWrvBY/SsFeIEFTI_I/AAAAAAAAADU/NphzXmrghkw/S220/oldmoe.png'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-15616477.post-4865284940748823775</id><published>2008-11-14T03:13:00.008+02:00</published><updated>2008-11-14T04:34:46.068+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ruby'/><category scheme='http://www.blogger.com/atom/ns#' term='eventmachine'/><category scheme='http://www.blogger.com/atom/ns#' term='rails'/><category scheme='http://www.blogger.com/atom/ns#' term='threads'/><category scheme='http://www.blogger.com/atom/ns#' term='ruby 1.9'/><category scheme='http://www.blogger.com/atom/ns#' term='fibers'/><category scheme='http://www.blogger.com/atom/ns#' term='neverblock'/><title type='text'>Ruby Networking on Steroids</title><content type='html'>Ruby provides several socket classes for various connection protocols. Those classes are arranged in a strange and a convoluted hierarchy.&lt;br /&gt;This ASCII diagram explains this hierarchy&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;     IO&lt;br /&gt;      |&lt;br /&gt;  BasicSocket&lt;br /&gt;      |&lt;br /&gt;      |-- IPSocket &lt;br /&gt;      |     |&lt;br /&gt;      |     |-- TCPSocekt&lt;br /&gt;      |     |       |&lt;br /&gt;      |     |       |-- TCPServer&lt;br /&gt;      |     |       |&lt;br /&gt;      |     |       |-- SocksSocket&lt;br /&gt;      |     |&lt;br /&gt;      |     |-- UDPSocket &lt;br /&gt;      |&lt;br /&gt;      |-- Socket&lt;br /&gt;      |&lt;br /&gt;      |-- UNIXSocket&lt;br /&gt;            |&lt;br /&gt;          UNIXServer&lt;br /&gt;&lt;/pre&gt;           &lt;br /&gt;The BasicSocket class provides some common methods but you cannot instantiate it. You have to use one of the sub classes. We have three branches coming out from BasicSocket. One that implements the IP (and descendant) protocls the other implements the UNIX domain sockets protocol. A third branch provides a generic wrapper over FreeBSD sockets. The first problem with this branching strategy is that while the Socket class can be used as a parent class to both UNIXSocket and IPSocket classes the implementer chose to create a separate path for each of them. This results in that there exists lots of code duplication in the implementation that makes maintaining those classes a lot harder than it should be.&lt;br /&gt;&lt;br /&gt;A prime example for this is the addition of non blocking features lately to the I/O and socket classes. Only the Socket class was lucky enough to get an accept_nonblocking method. The other classes sadly didn't get it. It is very important to be able to initiate network connections in a non blocking manner if you are using an evented framework (like NeverBlock for example).&lt;br /&gt;&lt;br /&gt;What makes the problem worse is that major Ruby network libraries overlook the Socket class and use TCPSocket or UNIXSocket. Net/HTTP for example uses TCPSocket. Since NeverBlock tries to work in harmony with most Ruby libraries it attempts to make up for this inconsistency by altering the default heirarechy of socket classes. Ruby allows you to un-define constants in an object. We remove the TCPSocket and UNIXSocket classes and redefine them by inheriting from Socket and defining some methods to make up for any lost functionality.&lt;br /&gt;&lt;br /&gt;After modifying the Socket classes NeverBlock support was integrated. This was done by rewriting the connect, read and write methods so that they would detect the presence of a NeverBlock fiber and operate in an aysnchronous way accordingly. If you use the new socket classes in a non NeverBlock context or in NeverBlock's blocking mode they will resort to the old blocking implementation.&lt;br /&gt;&lt;br /&gt;So Here is an example. First we will create a server using EventMachine that takes 1 second to process each request.&lt;br /&gt;&lt;br /&gt;server.rb&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;require 'eventmachine'&lt;br /&gt;&lt;br /&gt;class Server &lt; EM::Connection&lt;br /&gt;  # handle requests here&lt;br /&gt;  def receive_data data&lt;br /&gt;    # set the respnonse to be sent after 1 second&lt;br /&gt;    EM.add_timer(1) do&lt;br /&gt;      send_data "HTTP/1.1 200 OK\r\n\r\ndone"&lt;br /&gt;      close_connection_after_writing&lt;br /&gt;    end&lt;br /&gt;  end&lt;br /&gt;end&lt;br /&gt;&lt;br /&gt;EM.run do&lt;br /&gt;  EM.start_server('0.0.0.0',8080, Server)&lt;br /&gt;end&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Second we will create a client that will issue requests to the server&lt;br /&gt;&lt;br /&gt;client.rb&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;require 'neverblock'&lt;br /&gt;require 'net/http'&lt;br /&gt;EM.run do&lt;br /&gt;  @pool = NB::FiberPool.new(20)&lt;br /&gt;  20.times do&lt;br /&gt;    @pool.spawn do&lt;br /&gt;      url = "http://localhost:8080"&lt;br /&gt;      res = Net::HTTP.start(url.host, url.port) { |http| http.get('/') }&lt;br /&gt;    end&lt;br /&gt;  end&lt;br /&gt;end &lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Issuing 20 GET requests in NeverBlock fibers causes them to run concurrently. Even while our server process a request in one complete second, they all return after approximately 1 second. &lt;br /&gt;&lt;br /&gt;Here is a blocking version&lt;br /&gt;&lt;br /&gt;blocking_client.rb&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;require 'net/http'&lt;br /&gt;20.times do&lt;br /&gt;  url = "http://localhost:8080"&lt;br /&gt;  res = Net::HTTP.start(url.host, url.port) { |http| http.get('/') }&lt;br /&gt;end&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;The blocking client finishes after around 20 seconds.&lt;br /&gt;&lt;br /&gt;Here's a teaser graph&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/_XFDHsTWrvBY/SRzcYhIxjnI/AAAAAAAAACc/9JUJh5scJbI/s1600-h/never+vs+block.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 325px; height: 265px;" src="http://2.bp.blogspot.com/_XFDHsTWrvBY/SRzcYhIxjnI/AAAAAAAAACc/9JUJh5scJbI/s400/never+vs+block.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5268327977886781042" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;The really good thing is that we used the Net/HTTP library transparently. Any Ruby library that relies on Ruby sockets will benefit from NeverBlock and gain the ability to run in a concurrent manner.&lt;br /&gt;&lt;br /&gt;&lt;h4&gt;What does that mean?&lt;/h4&gt;Originally, NeverBlock only supported concurrent database access for PostgreSQL and MySQL. While this was good and all, databases usually were the bottlenecks of most applications. Unless you have something like a database cluster which can truly absorb any load. This was a shame, since NeverBlock is meant for high levels of concurrency that are only available with massively scalable back ends. With this new development, however, we are now one step closer to tapping into this realm of high performance and scalable web applications. Read on.&lt;br /&gt;&lt;br /&gt;&lt;h4&gt;Enter AWS and the cloud&lt;/h4&gt;Amazon Web Services provide an example of a massively scalable backend that is accessible via HTTP. Services like S3, SimpleDB and SQS are all a URL away. Such services have a higher latency than your nearby database server but they more than make up for that by being able to absorb all the requests you through at them. Most of the Ruby libraries for accessing AWS rely on Net/HTTP in some way or another. This means we get NeverBlock support for those libraries. Now this is big news for those Ruby applications (including Rails ones) that rely on an AWS or a similar backend. For those types of apps, forget about a 10 or 20 fibers pool. We are talking a 1000 fibers pool here. Even higher numbers could be possible (once a nasty file descriptor bug in Ruby 1.9 is fixed).&lt;br /&gt;&lt;br /&gt;&lt;h4&gt;Why Not Threads?&lt;/h4&gt;I have been claiming that Ruby fibers are faster than Ruby threads[1]. I have seen that in my tests but those were usually limited to testing a single performance metric. So I decided to simulate a very scalable back end and see which approach offers more scalability. For testing purposes I created two client applications. One is threaded and the other is based on NeverBlock. In the NeverBlock version I did not use the fiber pool though, I was creating a new fiber per operation to mimic the threaded app behavior. The simulated scalable back end consisted of an EventMachine based server that waits for a certain time before responding with 200 OK. The delay time is to simulate back end processing and network latencies. I testing using 0, 10, 50, 100 and 500 ms as delay values. Another client application was written that worked in the normal blocking mode for comparison.&lt;br /&gt;&lt;br /&gt;The clients were tested using Ruby 1.8.6 and 1.9.1. The only exception was the NeverBlock client which was only tested with 1.9.1. This is due to the fact that the current fiber implementation for Ruby 1.8.x is based on threads so it will only reflect a threaded implementation performance. Ruby1.8 was introduced because I noticed problems with the Ruby 1.9 threading implementation regarding scalability and performance so I added Ruby1.8 to the mix which proved to have a (sometimes) faster and more scalable threading implementation.&lt;br /&gt;&lt;br /&gt;The application will attempt to issue 1000 requests to the back end server and will try to do so in a concurrent fashion (except for the blocking version of course)&lt;br /&gt;&lt;br /&gt;Here are the results&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/_XFDHsTWrvBY/SRzcYnXclHI/AAAAAAAAACk/kzQMI6rUBxY/s1600-h/neverblock+vs+threads.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 400px; height: 182px;" src="http://1.bp.blogspot.com/_XFDHsTWrvBY/SRzcYnXclHI/AAAAAAAAACk/kzQMI6rUBxY/s400/neverblock+vs+threads.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5268327979558933618" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;And the results in ASCII format (numbers in cells are requests/sec)&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;Server Delay        0ms   10ms 50ms 100ms 500ms&lt;br /&gt;&lt;br /&gt;Ruby1.8 Blocking    2000  19 16 10      2&lt;br /&gt;&lt;br /&gt;Ruby1.9 Blocking    2400  19 17 10 2&lt;br /&gt;&lt;br /&gt;Ruby1.8 Threaded    1050  800 670 536 415&lt;br /&gt;&lt;br /&gt;Ruby1.9 Threaded    618   470 451 441 395&lt;br /&gt;&lt;br /&gt;Ruby1.9 NeverBlock  2360  1997 1837 1656 1031&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Let's try to explain the results. For a server that has no delay whatsoever (a utopian assumption) we see that the blocking servers offer the greatest performance. Ruby 1.9 in blocking mode comes first mainly due to the fact that Ruby1.9 is faster than Ruby1.8 and also comes with a faster Net/HTTP library[1]. Why is blocking faster? Simply because the evented server is processing the requests serially and the latency is minimal. The request processing send a response and returns immediately so the server does not get a chance to process requests concurrently. This is the fastest that you can drive your processor.&lt;br /&gt;&lt;br /&gt;The NeverBlock implementation comes as a very close second to the fastest client which shows that the overhead of using fibers is not that much. Actually we are cheating a bit here, because we make up for the overhead by sending the requests concurrently, and while the server is still processing the serially we are able to process the fiber pause and resume while the server is working.&lt;br /&gt;&lt;br /&gt;Needless to say, NeverBlock is much ahead of the threaded clients (either 1.8 or 1.9) when working with the zero latency server. We also see that 1.8 threads are considerably faster than 1.9's.&lt;br /&gt;&lt;br /&gt;When we start adding a simulated delay to the server we see that the blocking clients fall dramatically from the first position to the last. They become too slow that they are really not suitable for use in that setting any more. Please note that the results for the 500ms delay are extrapolations. I was to annoyed by the idea of waiting 500 seconds for a test to run, twice!&lt;br /&gt;&lt;br /&gt;On the other hand, threaded and NeverBlock implementations are much less affected even though they lose ground as we increase the delay. NeverBlock maintains its lead though over threaded clients. It is generally 2.5X faster.&lt;br /&gt;&lt;br /&gt;Here is a graph of the NeverBlock advantage over the fastest threaded client&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/_XFDHsTWrvBY/SRzcY1UTmEI/AAAAAAAAACs/OF5Hj96l0sA/s1600-h/neverblock+advantages.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 400px; height: 210px;" src="http://1.bp.blogspot.com/_XFDHsTWrvBY/SRzcY1UTmEI/AAAAAAAAACs/OF5Hj96l0sA/s400/neverblock+advantages.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5268327983303858242" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;And in ASCII format&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;Server Delay          0ms 10ms   50ms     100ms     500ms&lt;br /&gt;&lt;br /&gt;NeverBlock Advantage  124.76%   149.63%   174.18%   208.96%   148.43%&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Aside from the NeverBlock advantage the numbers themselves are very impressive. A single process can achieve ~1000 operations per second given that we have half a second processing and network latency. In a mutli process setup we should be able to achieve a lot more than that. For example, forking another NeverBlock client on my dual core notebook which hosts the client and the server apps adds a 50% performance gain.&lt;br /&gt;&lt;br /&gt;&lt;h4&gt;Conclusion&lt;/h4&gt;NeverBlock really shines when the back end is highly scalable. The only problem I met was a Ruby1.9 bug that crashed the client when the file descriptors exceeded 1024. I hope this could be fixed as it will enable us to extract more performance from each process. Expect the socket support to be officially added to NeverBlock soon.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/15616477-4865284940748823775?l=oldmoe.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://oldmoe.blogspot.com/feeds/4865284940748823775/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=15616477&amp;postID=4865284940748823775' title='5 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/15616477/posts/default/4865284940748823775'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/15616477/posts/default/4865284940748823775'/><link rel='alternate' type='text/html' href='http://oldmoe.blogspot.com/2008/11/ruby-networking-on-steroids.html' title='Ruby Networking on Steroids'/><author><name>oldmoe</name><uri>http://www.blogger.com/profile/14341721253106429644</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='24' height='32' src='http://3.bp.blogspot.com/_XFDHsTWrvBY/SsFeIEFTI_I/AAAAAAAAADU/NphzXmrghkw/S220/oldmoe.png'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://2.bp.blogspot.com/_XFDHsTWrvBY/SRzcYhIxjnI/AAAAAAAAACc/9JUJh5scJbI/s72-c/never+vs+block.png' height='72' width='72'/><thr:total>5</thr:total></entry><entry><id>tag:blogger.com,1999:blog-15616477.post-7559206714549427071</id><published>2008-11-07T03:57:00.003+02:00</published><updated>2008-11-07T04:57:28.066+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Visa'/><category scheme='http://www.blogger.com/atom/ns#' term='US'/><category scheme='http://www.blogger.com/atom/ns#' term='Rubyconf 2008'/><title type='text'>My US Visa Status</title><content type='html'>So, &lt;a href="http://www.rubyconf.org/"&gt;RubyConf 2008&lt;/a&gt; has started. I was supposed to be presenting &lt;a href="http://www.espace.com.eg/neverblock"&gt;NeverBlock&lt;/a&gt; to the audience there. I didn't make it but thank God my coworker and friend &lt;a href="http://www.rubyconf.org/talks/34"&gt;Yasser&lt;/a&gt; was able to go toFlorida. I couldn't make it because I am still waiting for my visa clearance (the Americans changed presidents while I am still waiting!). The status kept saying "Under Processing" till a few days before the conference date. It changed to show the following:&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_XFDHsTWrvBY/SROt2m3r-0I/AAAAAAAAACU/eFx6TUJehIQ/s1600-h/visa_status.png"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer; width: 400px; height: 184px;" src="http://3.bp.blogspot.com/_XFDHsTWrvBY/SROt2m3r-0I/AAAAAAAAACU/eFx6TUJehIQ/s400/visa_status.png" alt="" id="BLOGGER_PHOTO_ID_5265743542984899394" border="0" /&gt;&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/15616477-7559206714549427071?l=oldmoe.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://oldmoe.blogspot.com/feeds/7559206714549427071/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=15616477&amp;postID=7559206714549427071' title='4 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/15616477/posts/default/7559206714549427071'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/15616477/posts/default/7559206714549427071'/><link rel='alternate' type='text/html' href='http://oldmoe.blogspot.com/2008/11/my-us-visa-status.html' title='My US Visa Status'/><author><name>oldmoe</name><uri>http://www.blogger.com/profile/14341721253106429644</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='24' height='32' src='http://3.bp.blogspot.com/_XFDHsTWrvBY/SsFeIEFTI_I/AAAAAAAAADU/NphzXmrghkw/S220/oldmoe.png'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://3.bp.blogspot.com/_XFDHsTWrvBY/SROt2m3r-0I/AAAAAAAAACU/eFx6TUJehIQ/s72-c/visa_status.png' height='72' width='72'/><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-15616477.post-5265970256998475002</id><published>2008-09-03T16:11:00.003+02:00</published><updated>2008-09-04T01:27:24.650+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ruby'/><category scheme='http://www.blogger.com/atom/ns#' term='rails'/><category scheme='http://www.blogger.com/atom/ns#' term='ruby 1.9'/><category scheme='http://www.blogger.com/atom/ns#' term='activerecord'/><category scheme='http://www.blogger.com/atom/ns#' term='neverblock'/><category scheme='http://www.blogger.com/atom/ns#' term='postgres'/><title type='text'>Building the Never Blocking Rails, Making Rails 12X Faster</title><content type='html'>They told you it can't be done, they told you it has no scale. They told you lies!&lt;br /&gt;&lt;br /&gt;What if you suddenly had the ability to serve mutliple concurrent requests in a single Rails instance? What if you had the ability to multiplex IO operations from a single Rails instance?&lt;br /&gt;&lt;br /&gt;No more what ifs. It has been done.&lt;br /&gt;&lt;br /&gt;I was testing &lt;a href="http://www.espace.com.eg/neverblock"&gt;NeverBlock&lt;/a&gt; support for Rails. For testing I built a normal Rails application. Nothing up normal here, you get the whole usual Rails deal, routes, controllers, ActiveRecord models and eRuby templates. I am using the Thin server for serving the application and PostgreSQL as a database server. The only difference is that I was not using the PostgreSQL adapter, rather I was using the NeverBlock::PostgreSQL adapter.&lt;br /&gt;&lt;br /&gt;All I needed to do is to call the adapter in database.yml neverblock_postgresql instead of postgresql and require 'never_block/server/thin' in my production.rb&lt;br /&gt;&lt;br /&gt;All this was working with Ruby 1.9, so I had to comment out the body of the load_rubygems method in config/boot.rb which is not needed in Ruby1.9 anyway.&lt;br /&gt;&lt;br /&gt;Now what difference does this thing make?&lt;br /&gt;&lt;br /&gt;It allows you to process multiple requests concurrently from a single Rails instance. It does this by utilizing the async features of the PG client interface  coupled with Fibers and the EventMachine to provide transparent async operations.&lt;br /&gt;&lt;br /&gt;So, when a Rails action issue any ActiveRecord operation it will be suspended and another Rails action can kick in. The first one will be resumed once PostgreSQL has provided us with the data.&lt;br /&gt;&lt;br /&gt;To make a quick test, I created a controller which would use an AR model to issue the following sql command "select sleep(1)". (sleep does not come by default with PostgreSQL, you have to implement it yourself). I ran the applications with the normal postgresql adapter and used apache bench to measure the performance of 10 concurrent requests.&lt;br /&gt;&lt;br /&gt;Here are the results:&lt;br /&gt;&lt;pre class="code"&gt;Server Software:        thin&lt;br /&gt;Server Hostname:        localhost&lt;br /&gt;Server Port:            3000&lt;br /&gt;&lt;br /&gt;Document Path:          /forums/sleep/&lt;br /&gt;Document Length:        11 bytes&lt;br /&gt;&lt;br /&gt;Concurrency Level:      10&lt;br /&gt;Time taken for tests:   10.248252 seconds&lt;br /&gt;Complete requests:      10&lt;br /&gt;Failed requests:        0&lt;br /&gt;Write errors:           0&lt;br /&gt;Total transferred:      4680 bytes&lt;br /&gt;HTML transferred:       110 bytes&lt;br /&gt;Requests per second:    0.98 [#/sec] (mean)&lt;br /&gt;Time per request:       10248.252 [ms] (mean)&lt;br /&gt;Time per request:       1024.825 [ms] (mean, across all concurrent requests)&lt;br /&gt;Transfer rate:          0.39 [Kbytes/sec] received&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Almost 1 request per second. Which is what I expected. Now I switched to the new adapter, restarted thin and redid the test. &lt;br /&gt;&lt;br /&gt;Here are the new results:&lt;br /&gt;&lt;pre class="code"&gt;&lt;br /&gt;Server Software:        thin&lt;br /&gt;Server Hostname:        localhost&lt;br /&gt;Server Port:            3000&lt;br /&gt;&lt;br /&gt;Document Path:          /forums/sleep/&lt;br /&gt;Document Length:        11 bytes&lt;br /&gt;&lt;br /&gt;Concurrency Level:      10&lt;br /&gt;Time taken for tests:   1.75797 seconds&lt;br /&gt;Complete requests:      10&lt;br /&gt;Failed requests:        0&lt;br /&gt;Write errors:           0&lt;br /&gt;Total transferred:      4680 bytes&lt;br /&gt;HTML transferred:       110 bytes&lt;br /&gt;Requests per second:    9.30 [#/sec] (mean)&lt;br /&gt;Time per request:       1075.797 [ms] (mean)&lt;br /&gt;Time per request:       107.580 [ms] (mean, across all concurrent requests)&lt;br /&gt;Transfer rate:          3.72 [Kbytes/sec] received&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Wow! a 9x speed improvement! The database requests were able to run concurrently and they all came back together.&lt;br /&gt;&lt;br /&gt;I decided to simulate various work loads and test the new implementation against the old one. I devised the workloads taking into account that the test machine did have a rather bad IO perfromance so I decided to use queries that would not tax the IO but still would require the PostgreSQL to take it's time. The work loads were categorized as follows:&lt;br /&gt;&lt;br /&gt;First a request would issue a "select 1" query, this is the fastest I can think of, then for the differen work loads&lt;br /&gt;&lt;br /&gt;&lt;pre class="code"&gt;1 - Very light  work load,  every 200 requests, one "select sleep(1)" would be issued &lt;br /&gt;2 - Light work load,        every 100 requests, one "select sleep(1)" would be issued&lt;br /&gt;3 - Moderate work load,     every 50  requests, one "select sleep(1)" would be issued&lt;br /&gt;4 - Heavy work load,        every 20  requests, one "select sleep(1)" would be issued&lt;br /&gt;5 - Very heavy work load,   every 10  requests, one "select sleep(1)" would be issued&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;I tested those workloads against the following&lt;br /&gt;&lt;br /&gt;&lt;pre class="code"&gt;1 - 1 Thin server, normal postgreSQL Adapter&lt;br /&gt;2 - 2 Thin servers (behind nginx), normal postgreSQL Adapter&lt;br /&gt;3 - 4 Thin servers (behind nginx), normal postgreSQL Adapter&lt;br /&gt;4 - 1 Thin server, neverblock postgreSQL Adapter&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;I tested with 1000 queries and a concurrency of 200 ( the mutliple thin servers were having problems above that figure, the new adapter scaled up to 1000 with no problems, usually with similar or slightly better results )&lt;br /&gt;&lt;br /&gt;Here are the graphed results:&lt;br /&gt;&lt;br /&gt;&lt;img src="http://www.espace.com.eg/assets/neverblock/images/charts/1.gif"/&gt;&lt;br /&gt;&lt;br /&gt;For the neverblock thin server I was using a pool of 12 connections. As you can see from the results, In very heavy workload I would perform on par with a 12 Thin cluster. Generally the NeverBlock Thin server easily outperforms the 4 Thin cluster. The margin increases as the work load gets heavier.&lt;br /&gt;&lt;br /&gt;And here are the results for scaling the number of concurrent connections for a NeverBlock::Thin server&lt;br /&gt;&lt;br /&gt;&lt;img src="http://www.espace.com.eg/assets/neverblock/images/charts/2.gif"/&gt;&lt;br /&gt;&lt;br /&gt;Traditionally we used to spawn as many thin servers as we can till we run out of memory. Now we don't need to do so, as a single process will maintain multiple connections and would be able to saturate a single cpu core, hence the perfect setup seems to be a single server instance for each processor core.&lt;br /&gt;&lt;br /&gt;But to really saturate a CPU one has to do all the IO requests in a non-blocking manner, not just the database. This is exactly the next step after the DB implementation is stable, to enrich NeverBlock with a set of IO libraries that operate in a seemingly blocking way while they are doing all their IO in a totally transparent non-blocking manner, thanks to Fibers.&lt;br /&gt;&lt;br /&gt;I am now wondering about the possibilities, the reduced memory footprint gains and what benefits such a solution can bring to the likes of dreamhost and all the Rails hosting companies.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/15616477-5265970256998475002?l=oldmoe.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://oldmoe.blogspot.com/feeds/5265970256998475002/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=15616477&amp;postID=5265970256998475002' title='15 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/15616477/posts/default/5265970256998475002'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/15616477/posts/default/5265970256998475002'/><link rel='alternate' type='text/html' href='http://oldmoe.blogspot.com/2008/09/building-never-blocking-rails-making.html' title='Building the Never Blocking Rails, Making Rails 12X Faster'/><author><name>oldmoe</name><uri>http://www.blogger.com/profile/14341721253106429644</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='24' height='32' src='http://3.bp.blogspot.com/_XFDHsTWrvBY/SsFeIEFTI_I/AAAAAAAAADU/NphzXmrghkw/S220/oldmoe.png'/></author><thr:total>15</thr:total></entry><entry><id>tag:blogger.com,1999:blog-15616477.post-1260927194681609176</id><published>2008-08-28T12:53:00.004+03:00</published><updated>2008-08-28T13:49:20.013+03:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ruby'/><category scheme='http://www.blogger.com/atom/ns#' term='mysql'/><category scheme='http://www.blogger.com/atom/ns#' term='threads'/><category scheme='http://www.blogger.com/atom/ns#' term='neverblock'/><title type='text'>NeverBlock, MySQL and MySQLPlus</title><content type='html'>I have great news for MySQL users. A very nice side effect has emerged from the development of the &lt;a href="http://www.espace.com.eg/neverblock/blog/2008/08/28/neverblock-mysql-support/"&gt;NeverBlock support for MySQL&lt;/a&gt;. I am glad to announce the release of a new &lt;a href="http://github/espace/mysqlplus"&gt;MySQL driver&lt;/a&gt; for Ruby applications. It builds on top of the original Ruby MySQL driver but it comes with two notable additions:&lt;br /&gt;&lt;ol&gt;&lt;li&gt;Asynchronous query processing support&lt;/li&gt;&lt;br /&gt;  &lt;li&gt;Threaded access support&lt;/li&gt;&lt;/ol&gt;&lt;br /&gt;Thanks to help from Roger Pack and Aman Gupta we were able to put the thing together that you can use and test right now (on Ruby1.8 and 1.9)&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;To install it please do:&lt;br /&gt;&lt;pre class="code"&gt;sudo gem install espace-mysqlplus&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Then you can use it in your code as follows:&lt;br /&gt;&lt;pre class="code"&gt;require 'mysqlplus'&lt;br /&gt;mysql = Mysql.real_connect(..)&lt;br /&gt;mysql.query("select sleep(1)")&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;The test folder of the gem contains examples for threaded and evented implementations.&lt;br /&gt;&lt;br /&gt;The &lt;a href="http://www.espace.com.eg/neverblock/blog/2008/08/28/neverblock-mysql-support/"&gt;announcement page&lt;/a&gt; in &lt;a href="http://www.espace.com.eg/neverblock"&gt;NeverBlock&lt;/a&gt; shows benchmark results for running the sleeping queries in normal(blocking), evented and threaded modes. The normal mode is 10X slower, which is normal due to its inability to run queries in parallel.&lt;br /&gt;&lt;br /&gt;Now that Rails is becoming so-so-thread-safe this should show tremendous gains with Rails deployments that use MySQL (PostgreSQL already has such facilities).&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/15616477-1260927194681609176?l=oldmoe.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://oldmoe.blogspot.com/feeds/1260927194681609176/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=15616477&amp;postID=1260927194681609176' title='27 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/15616477/posts/default/1260927194681609176'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/15616477/posts/default/1260927194681609176'/><link rel='alternate' type='text/html' href='http://oldmoe.blogspot.com/2008/08/neverblock-mysql-and-mysqlplus.html' title='NeverBlock, MySQL and MySQLPlus'/><author><name>oldmoe</name><uri>http://www.blogger.com/profile/14341721253106429644</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='24' height='32' src='http://3.bp.blogspot.com/_XFDHsTWrvBY/SsFeIEFTI_I/AAAAAAAAADU/NphzXmrghkw/S220/oldmoe.png'/></author><thr:total>27</thr:total></entry><entry><id>tag:blogger.com,1999:blog-15616477.post-7748178801226462869</id><published>2008-08-25T03:18:00.004+03:00</published><updated>2008-08-25T12:26:21.319+03:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ruby'/><category scheme='http://www.blogger.com/atom/ns#' term='ruby 1.9'/><category scheme='http://www.blogger.com/atom/ns#' term='activerecord'/><category scheme='http://www.blogger.com/atom/ns#' term='neverblock'/><category scheme='http://www.blogger.com/atom/ns#' term='postgres'/><title type='text'>ActiveRecord meets NeverBlock</title><content type='html'>I happily announce the release of the first &lt;a href="http://www.espace.com.eg/neverblock"&gt;NeverBlock&lt;/a&gt; enabled activerecord adapter. The &lt;a href="http://github.com/espace/activerecord-neverblock-postgresql-adapter/tree"&gt;neverblock-postgresql-adapter&lt;/a&gt;. This is a beta release but I have been testing it for a while now with great results.&lt;br /&gt;&lt;br /&gt;And while this is a big improvement it only requires you to replace the driver name in the connection to neverblock_postgresql instead of postgresql as described in &lt;a href="http://www.espace.com.eg/neverblock/blog/2008/08/24/neverblock-and-activerecord-concurrent-db-access-without-threads/"&gt;the official neverblock blog&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;To make a long story short, this enables active record to issue queries in parallel, much like in a multi-threaded application. But this has several advantages over multi-threaded operations:&lt;br /&gt;&lt;ol&gt;&lt;br /&gt;&lt;li&gt;Fibers are cheaper than threads so this solution is theoretically faster.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;NeverBlock does not require full thread safety, just avoid using globals and static variables for transient state.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;It integrates nicely in evented programs thus eliminating the performance drop which occurs with the introduction of threads in such environments&lt;/li&gt;&lt;br /&gt;&lt;/ol&gt;&lt;br /&gt;I have benchmarked this against the plain postgresql adapter using different workloads categorized as follows&lt;br /&gt;&lt;br /&gt;Very Light : A single count statement&lt;br /&gt;Light      : A single count and a create&lt;br /&gt;Moderate   : 2 counts and a create wrapped in a transaction that rolls back&lt;br /&gt;Heavy      : 3 counts, a create and an update wrapped in a transaction that commits&lt;br /&gt;Very Heavy : 3 counts, one conditional count (on a non-indexed field), a create and two updates all wrapped in a transaction that commits&lt;br /&gt;&lt;br /&gt;(if you are wondering why these queries in particular, they were extracted from some other code)&lt;br /&gt;&lt;br /&gt;All were issued 1000 times&lt;br /&gt;&lt;br /&gt;The results came as follows:&lt;br /&gt;&lt;br /&gt;&lt;img src="http://www.espace.com.eg/assets/neverblock/images/charts/5.gif"/&gt;&lt;br /&gt;&lt;br /&gt;As you can see, NeverBlock::AR is persistently faster than vanilla AR. It appears that such work loads generate linear increase for both AR and NeverBlock::AR as the NeverBlock advantage was almost the same&lt;br /&gt;&lt;br /&gt;&lt;img src="http://www.espace.com.eg/assets/neverblock/images/charts/4.gif"/&gt;&lt;br /&gt;&lt;br /&gt;Another benchmark was performed to test the effect of increasing the connection count for NeverBlock::AR. We tested with 2, 4, 8, 16 and 32 connections.&lt;br /&gt;&lt;br /&gt;The benchmark consisted of first running "select 1" 5000 times and then running &lt;strike&gt;"select sleep(10)"&lt;/strike&gt; "select sleep(1)" 20 times for each configuration.&lt;br /&gt;&lt;br /&gt;&lt;img src="http://www.espace.com.eg/assets/neverblock/images/charts/6.gif"/&gt;&lt;br /&gt;&lt;br /&gt;As you can probably guess, increasing connection count has very little effect if the queries are all very fast (you cannot beat "select 1") but if the queries are all slow, you will be able to double the performance by simply doubling the connection count.&lt;br /&gt;&lt;br /&gt;I hope this gives you a glimpse of what's coming next. Watch this space&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/15616477-7748178801226462869?l=oldmoe.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://oldmoe.blogspot.com/feeds/7748178801226462869/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=15616477&amp;postID=7748178801226462869' title='9 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/15616477/posts/default/7748178801226462869'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/15616477/posts/default/7748178801226462869'/><link rel='alternate' type='text/html' href='http://oldmoe.blogspot.com/2008/08/activerecord-meets-neverblock.html' title='ActiveRecord meets NeverBlock'/><author><name>oldmoe</name><uri>http://www.blogger.com/profile/14341721253106429644</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='24' height='32' src='http://3.bp.blogspot.com/_XFDHsTWrvBY/SsFeIEFTI_I/AAAAAAAAADU/NphzXmrghkw/S220/oldmoe.png'/></author><thr:total>9</thr:total></entry><entry><id>tag:blogger.com,1999:blog-15616477.post-3767789060463378955</id><published>2008-08-20T19:07:00.002+03:00</published><updated>2008-08-21T04:02:20.202+03:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ruby'/><category scheme='http://www.blogger.com/atom/ns#' term='mysql'/><category scheme='http://www.blogger.com/atom/ns#' term='rails'/><category scheme='http://www.blogger.com/atom/ns#' term='postgres'/><title type='text'>101 Reasons Why PostgreSQL is a better fit for Rails than MySQL</title><content type='html'>1 - Indexing Support&lt;br /&gt;&lt;br /&gt;MySQL cannot utilize more than one index per query. I believe this is worth repeating: MySQL CANNOT UTILIZE MORE THAN ONE INDEX PER QUERY. Wait till your tables get large enough and this will surely hit you. OTOH PostgreSQL can use &lt;a href="http://www.postgresql.org/docs/8.3/static/indexes-bitmap-scans.html"&gt;multiple indices per query&lt;/a&gt; which come real handy.&lt;br /&gt;&lt;br /&gt;2 - Full Text Indexing Support&lt;br /&gt;&lt;br /&gt;MySQL can do full text indexing on MyISAM tables only, those working with InnoDB tables are out if luck. PostgreSQL has very advanced &lt;a href="http://www.postgresql.org/docs/8.3/static/textsearch.html"&gt; full text indexing capabilities&lt;/a&gt; wich enable you to control the tiniest details down to the stemming strategy.&lt;br /&gt;&lt;br /&gt;3 - Asynchronous Interface&lt;br /&gt;&lt;br /&gt;MySQL drivers are very unfriendly to the Ruby interpreter. Once a command is issued they take over until they come back with results. PostgreSQL sports a completely &lt;a href="http://www.postgresql.org/docs/8.3/static/libpq-async.html"&gt;asynchronous interface&lt;/a&gt; where you can send queries to the database and then tend to other matters while the query is being processed by the server. The good news is that an Async ActiveRecord adapter for MySQL is being developed right now, as part of the rapidly growing &lt;a href="http://www.espace.com.eg/neverblock"&gt;NeverBlock&lt;/a&gt; library.&lt;br /&gt;&lt;br /&gt;4 - Ruby Threading Aware&lt;br /&gt;&lt;br /&gt;PostgreSQL dirvers enable the Ruby thread scheduler while IO requests are being processed (a nice side effect of the async interface). Which makes it much better suited for multithreaded Rails apps.  &lt;br /&gt;&lt;br /&gt;5 - Multistatements Per Query&lt;br /&gt;&lt;br /&gt;Both MySQL and PostgreSQL support sending multiple statements separated by semi colons at once. But the returning result will be that of the last statement in the group. Now did you know that by using the async interface you can send multiple queries at once and then get back the results, one by one? One of the coolest features of the coming ActiveRecord (and Sequel btw) adapter is it's support for queuing queries to be consumed by a pool of connections. A trick we are contemplating working on is to group consequent selects together and send them in a single request to PostgreSQL and then later extract the results associated with each one of them. This is still very theoretical but should be verified soon.&lt;br /&gt;&lt;br /&gt;Now that the 0b101 reasons are told I rest my case.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/15616477-3767789060463378955?l=oldmoe.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://oldmoe.blogspot.com/feeds/3767789060463378955/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=15616477&amp;postID=3767789060463378955' title='26 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/15616477/posts/default/3767789060463378955'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/15616477/posts/default/3767789060463378955'/><link rel='alternate' type='text/html' href='http://oldmoe.blogspot.com/2008/08/101-reasons-why-postgresql-is-better.html' title='101 Reasons Why PostgreSQL is a better fit for Rails than MySQL'/><author><name>oldmoe</name><uri>http://www.blogger.com/profile/14341721253106429644</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='24' height='32' src='http://3.bp.blogspot.com/_XFDHsTWrvBY/SsFeIEFTI_I/AAAAAAAAADU/NphzXmrghkw/S220/oldmoe.png'/></author><thr:total>26</thr:total></entry><entry><id>tag:blogger.com,1999:blog-15616477.post-6224774362620258664</id><published>2008-08-20T18:54:00.003+03:00</published><updated>2008-08-20T19:03:53.566+03:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ruby'/><category scheme='http://www.blogger.com/atom/ns#' term='eventmachine'/><category scheme='http://www.blogger.com/atom/ns#' term='ruby 1.9'/><category scheme='http://www.blogger.com/atom/ns#' term='postgres'/><category scheme='http://www.blogger.com/atom/ns#' term='rev'/><title type='text'>NeverBlock, much faster IO for Ruby</title><content type='html'>At &lt;a href="http://www.espace.com.eg"&gt;eSpace&lt;/a&gt; we have just released an alpha version of &lt;a href="http://www.espace.com.eg/neverblock"&gt;NeverBlock&lt;/a&gt;. A library that aims to bring evented IO to the masses. It does so by wrapping all IO in Fibers which handle all the async aspects and hides them totally from the developers.&lt;br /&gt;&lt;br /&gt;Just as a teaser, here are some benchmarks of running PostgreSQL queries with and without NeverBlock&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://www.espace.com.eg/assets/neverblock/images/charts/3.gif"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 400px;" src="http://www.espace.com.eg/assets/neverblock/images/charts/3.gif" border="0" alt="" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;10x performance boost? how about that?&lt;br /&gt;&lt;br /&gt;I am working on extending the NeverBlock library now, watch this space for great news soon&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/15616477-6224774362620258664?l=oldmoe.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://oldmoe.blogspot.com/feeds/6224774362620258664/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=15616477&amp;postID=6224774362620258664' title='10 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/15616477/posts/default/6224774362620258664'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/15616477/posts/default/6224774362620258664'/><link rel='alternate' type='text/html' href='http://oldmoe.blogspot.com/2008/08/neverblock-much-faster-io-for-ruby.html' title='NeverBlock, much faster IO for Ruby'/><author><name>oldmoe</name><uri>http://www.blogger.com/profile/14341721253106429644</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='24' height='32' src='http://3.bp.blogspot.com/_XFDHsTWrvBY/SsFeIEFTI_I/AAAAAAAAADU/NphzXmrghkw/S220/oldmoe.png'/></author><thr:total>10</thr:total></entry><entry><id>tag:blogger.com,1999:blog-15616477.post-5936650883716344321</id><published>2008-08-06T21:55:00.007+03:00</published><updated>2008-08-06T22:59:56.025+03:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ruby'/><category scheme='http://www.blogger.com/atom/ns#' term='document matching'/><category scheme='http://www.blogger.com/atom/ns#' term='tf-idf'/><category scheme='http://www.blogger.com/atom/ns#' term='information retreival'/><title type='text'>Document Matching In Ruby</title><content type='html'>With the debut of the new version of &lt;a href="http://www.meowns.com"&gt;meOwns&lt;/a&gt; we have introduced several features that are concerned with how objects are related to each other. In the user profile page, you get a list of other user that are similar to him/her. And you are faced with the chemistry meter which tells you how much you are related to this user (if you are logged in of course). If you happen to be viewing your own profile page you will get a list of recommended items that you might be interested in. Last but not least, when you view an item, you get a list of similar items.&lt;br /&gt;&lt;br /&gt;In this article I will be talking about the features from an implementation point of view. Naturally the first hurdle was to define the problem. What we needed was to find a way to match items and users. So first we needed to represent them in a way that can be matched. We started from the items first and looked at how to match two items together.&lt;br /&gt;&lt;br /&gt;What is an item? In meOwns an item is simply a name, a description, a type and some tags. We decided to ignore photos and comments from the item. Each of those fields gets a weight which affects the value of terms found in it. Here is a sample product (encoded in Yaml): &lt;pre class="code"&gt;Name : Fiat Sienna&lt;br /&gt;Type : Car&lt;br /&gt;Description : 1.6 HP, not bad for a sedan, relatively good performance for the price, best sedan i've bought&lt;br /&gt;Tags : Cars Fiat Silver&lt;/pre&gt;The above fields are then processed to extract relevant terms from them, this is done in the following manner &lt;br /&gt;&lt;ol&gt;&lt;br /&gt;&lt;li&gt;Remove punctuation and non alphanumeric characters (replace them with spaces)&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Collapse spaces and split the text on them&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Match the generated list of terms to a stop word list to remove them (words like "on", "the" should not be considered in the index)&lt;/li&gt;&lt;br /&gt;&lt;li&gt;The remaining terms are converted to lower case and then converted to their stem representation (we use a snowball stemmer for now)&lt;/li&gt;&lt;br /&gt;&lt;/ol&gt;&lt;br /&gt;The above can be represented as follows:&lt;pre class="code"&gt;Attribute : Value&lt;br /&gt;===================&lt;br /&gt;Name : fiat sienna&lt;br /&gt;Type : car&lt;br /&gt;Description : hp sedan performance price sedan buy&lt;br /&gt;Tags : car fiat silver&lt;/pre&gt;After doing so, we create a list of terms with their frequencies, each field has a frequency multiplier according to its significance. Assumming a multiplier value of 1 for all the fields&lt;pre class="code"&gt;Term : Frequency&lt;br /&gt;=======================&lt;br /&gt;fiat        : 2&lt;br /&gt;car         : 2&lt;br /&gt;sedan       : 2&lt;br /&gt;sienna      : 1&lt;br /&gt;performance : 1&lt;br /&gt;silver      : 1&lt;br /&gt;buy         : 1&lt;br /&gt;hp          : 1 &lt;/pre&gt;This Term-Frequency vector is the basis of doing item matching in meowns. Several different approaches can be implemented to reach an item representation. Even the details of a given approach can vary significantly. I have chosen to stick to the easiest approach in the initial implementation. Those willing to dig further are free to lookup more into document representation and indexing strategies. &lt;br /&gt;&lt;br /&gt;User representation is just an aggregation of their item (and wished item) representations. This way user and items share the same term vector structure and hence we can match users to items as much as we can match items to items and users to users. &lt;br /&gt;&lt;br /&gt;The matching process involves further encoding of the term frequency vector. Those in the academia refer to this as &lt;a href="http://www.blogger.com/en.wikipedia.org/wiki/Tf-idf"&gt;TF-IDF&lt;/a&gt; (Term Frequency, Inverse Document Frequency) representation. In lay man's terms this is a representation of how significant a term is to the certain document. &lt;br /&gt;&lt;br /&gt;It is composed of the product of two parts. This first which is TF (Term Frequency) is simply the frequency of the term in the document divided by the total sum of frequency of terms in the same document. &lt;br /&gt;&lt;br /&gt;The second (IDF) is the total number of documents in the corpse (the document store) divided by the number of documents which contain this term. If the term is found in all documents then the IDF will be equal to 1 and hence won't have an effect on the final product of the two parts. On the other hand, if we have a corpus of 1,000,000 documents and only one with the given term then it will multiply the TF value by 1,000,000 which is significant. &lt;br /&gt;&lt;br /&gt;A slight variation (widely used) is to multiply the TF with the Logarithmic value of the IDF. After we are done calculating TF-IDF values for all the terms in term vectors matching can be done as follows&lt;pre class="code"&gt;Term Vector A vs. Term Vector B = cos a = (A . B) / (|A|.|B|)&lt;br /&gt;A.B = dot product for the two vectors of TF-IDF values &lt;br /&gt;|A|.|B| = scalar product of the magnitudes of the two vectors&lt;/pre&gt;The returned value is called the cosine similarity between the two items. It ranges between 0 and 1 where zero means no correlation and one means exact match. We are still experimenting with threshold values but these are essentially the figures you get when you see another user's chemistry meter for example. In another installment we will discuss how are we implementing behind the scenes matching of users and items in an efficient way.&lt;br /&gt;&lt;br /&gt;Just to justify calling the post document matching in Ruby, here's a Ruby code to implement the above&lt;pre class="code"&gt;# Monkey patch string to be able to extract terms from any string&lt;br /&gt;Class String&lt;br /&gt;  def to_terms(boost = 1, terms = {})&lt;br /&gt;    # remove all non letters and reject stop words&lt;br /&gt;    terms_list = self.gsub(/(\s|\d|\W)+/u,' ').rstrip.strip.split(' ').reject{|term|$stop_words.include?(term)}&lt;br /&gt;    # transform to a hash with a frequency * boost value&lt;br /&gt;    terms_list.each do|term|&lt;br /&gt;      if terms[term]&lt;br /&gt;        terms[term] = terms[term] + boost&lt;br /&gt;      else&lt;br /&gt;        terms[term] = boost&lt;br /&gt;      end&lt;br /&gt;    end&lt;br /&gt;    terms&lt;br /&gt;  end &lt;br /&gt;end&lt;br /&gt;&lt;br /&gt;#our item class, which we match upon&lt;br /&gt;Class Item &lt;br /&gt;  def to_terms(terms = {})&lt;br /&gt;    #a hash of attributes to serialize &lt;br /&gt;    {:name =&gt; 10, &lt;br /&gt;     :description =&gt; 3, &lt;br /&gt;     :type_name =&gt; 1, &lt;br /&gt;     :tag_names =&gt; 1}.each_pair do |field, boost| &lt;br /&gt;      terms = send(field).to_terms(boost, terms)&lt;br /&gt;    end&lt;br /&gt;    terms&lt;br /&gt;  end&lt;br /&gt;end&lt;/pre&gt;The above methods enable us to extract the terms from different items. The first method refers to a global (bad me) stop word list which should be present.&lt;br /&gt;&lt;br /&gt;Now that we have the items represented as term frequencies we can generate their tf-idf vectors (we will keep them as hashes though) and we can use them to do the matching&lt;pre class="code"&gt;Class Item&lt;br /&gt;  def to_tf_idf&lt;br /&gt;    #assume we have a method that returns the df value for any term&lt;br /&gt;    terms = self.to_terms&lt;br /&gt;    total_frequency = terms.values.inject(0){|a,b|a+b}&lt;br /&gt;    terms.each do |term,freq| &lt;br /&gt;      terms[term]= (freq / total_frequency) * self.df(term)&lt;br /&gt;      magnitude = magnitude + terms[term]**2&lt;br /&gt;    end&lt;br /&gt;    terms, magnitude&lt;br /&gt;  end&lt;br /&gt;  def match(item)&lt;br /&gt;    my_tf_idf,  my_magnitude  = self.to_tf_idf&lt;br /&gt;    his_tf_idf, his_magnitude = item.to_tf_idf&lt;br /&gt;    dot_product = 0&lt;br /&gt;    my_tf_idf.each do |term,tf_idf|&lt;br /&gt;      dot_product = dot_product + tf_idf * his_tf_idf[term] if his_tf_idf[term]&lt;br /&gt;    end&lt;br /&gt;    cosine_similarity = dot_product / (my_magnitude * his_magnitude)&lt;br /&gt;  end&lt;br /&gt;end&lt;/pre&gt;Pretty easy, now you get a value between 0 and 1 that represents how similar those two items are.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/15616477-5936650883716344321?l=oldmoe.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://oldmoe.blogspot.com/feeds/5936650883716344321/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=15616477&amp;postID=5936650883716344321' title='18 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/15616477/posts/default/5936650883716344321'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/15616477/posts/default/5936650883716344321'/><link rel='alternate' type='text/html' href='http://oldmoe.blogspot.com/2008/08/document-matching-in-ruby.html' title='Document Matching In Ruby'/><author><name>oldmoe</name><uri>http://www.blogger.com/profile/14341721253106429644</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='24' height='32' src='http://3.bp.blogspot.com/_XFDHsTWrvBY/SsFeIEFTI_I/AAAAAAAAADU/NphzXmrghkw/S220/oldmoe.png'/></author><thr:total>18</thr:total></entry><entry><id>tag:blogger.com,1999:blog-15616477.post-1268846820954692730</id><published>2008-08-03T00:48:00.010+03:00</published><updated>2008-12-12T04:09:42.547+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ruby'/><category scheme='http://www.blogger.com/atom/ns#' term='performance'/><category scheme='http://www.blogger.com/atom/ns#' term='eventmachine'/><category scheme='http://www.blogger.com/atom/ns#' term='rails'/><category scheme='http://www.blogger.com/atom/ns#' term='nonblocking'/><category scheme='http://www.blogger.com/atom/ns#' term='ramaze'/><category scheme='http://www.blogger.com/atom/ns#' term='ruby 1.9'/><category scheme='http://www.blogger.com/atom/ns#' term='merb'/><category scheme='http://www.blogger.com/atom/ns#' term='sequel'/><category scheme='http://www.blogger.com/atom/ns#' term='asynchronous'/><category scheme='http://www.blogger.com/atom/ns#' term='postgres'/><title type='text'>The case for a nonblocking Ruby stack</title><content type='html'>In a previous &lt;a href="http://oldmoe.blogspot.com/2008/07/untwisting-event-loop.html"&gt;post&lt;/a&gt; I talked about the problems that plauge the web based Ruby applications regarding processor and memory use. I proposed using non-blocking IO as a solution to this problem. In a follow up &lt;a href="http://oldmoe.blogspot.com/2008/07/faster-io-for-ruby-with-postgres.html"&gt;post&lt;/a&gt; I benchmarked nonblocking vs blocking performance using the async facilities in the Ruby Postgres driver in combination with Ruby Fibers. The results were very promising (up to 40% improvement) that I decided to take the benchmarking effort one step further. I monkey patched the ruby postgres driver to be fiber aware and was able to integrate it into sequel with little to no effort. Next I used the &lt;strike&gt;unicycle&lt;/strike&gt; monorail server (the EventMachine HTTP server) in an eventmachine loop. I created a dumb controller which would query the db and render the results using the Object#to_json method.&lt;br /&gt;&lt;br /&gt;As was done with the evented db access benchmark, a long query ran every n short queries (n belongs to {5, 10, 20, 50, 100}). The running application accepted 2 urls. One ran db operations in normal mode and the other ran in nonblocking mode (every action invocation was wrapped in a fiber in the latter case)&lt;br /&gt;&lt;br /&gt;Here are the benchmark results&lt;br /&gt;&lt;br /&gt;Full results&lt;br /&gt;&lt;br /&gt;Comparing the number of requests/second fulfilled by each combination of blocking mode and conncurrency level. The first had the possible values of [blocking, nonblocking] the second had the possible values of [5, 10, 20, 50, 100]&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/_XFDHsTWrvBY/SJTYtPOmWBI/AAAAAAAAACE/HisrXjBz3dQ/s1600-h/http_serving_performance.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;" src="http://1.bp.blogspot.com/_XFDHsTWrvBY/SJTYtPOmWBI/AAAAAAAAACE/HisrXjBz3dQ/s400/http_serving_performance.png" border="0" alt="" id="BLOGGER_PHOTO_ID_5230043338977466386" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Advantage Graph&lt;br /&gt;&lt;br /&gt;Comparing the advantage gained for nonblocking over blocking mode for different long to short query ratios. Displaying the results for different levels of concurrency&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/_XFDHsTWrvBY/SJTY4JxHdZI/AAAAAAAAACM/Tpx4BmJ98cA/s1600-h/advantage_for_http_serving.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;" src="http://2.bp.blogspot.com/_XFDHsTWrvBY/SJTY4JxHdZI/AAAAAAAAACM/Tpx4BmJ98cA/s400/advantage_for_http_serving.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5230043526490191250" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;And the full results in tabular form&lt;pre class="code"&gt;&lt;br /&gt;                      Concurrent Requests&lt;br /&gt;Ratio                 10     100    1000&lt;br /&gt;&lt;br /&gt;1 To 100  Nonblocking 456.94 608.67 631.82&lt;br /&gt;1 To 100  Blocking    384.82 524.39 532.26&lt;br /&gt;          Advantage   18.74% 16.07% 18.71%&lt;br /&gt;    &lt;br /&gt;1 To 50   Nonblocking 377.38 460.74 471.89&lt;br /&gt;1 To 50   Blocking    266.63 337.49 339.01&lt;br /&gt;          Advantage   41.54% 36.52% 39.20%&lt;br /&gt;    &lt;br /&gt;1 To 20   Nonblocking 220.44 238.63 266.07&lt;br /&gt;1 To 20   Blocking    142.6  159.7  141.92&lt;br /&gt;          Advantage   54.59% 49.42% 87.48%&lt;br /&gt;    &lt;br /&gt;1 To 10   Nonblocking 130.87 139.76 195.02&lt;br /&gt;1 To 10   Blocking    78.68  84.84  81.07&lt;br /&gt;          Advantage   66.33% 64.73% 140.56%&lt;br /&gt;    &lt;br /&gt;1 To 5    Nonblocking 70.05  75.5   109.34&lt;br /&gt;1 To 5    Blocking    41.48  42.13  41.77&lt;br /&gt;          Advantage   68.88% 79.21% 161.77%&lt;/pre&gt;&lt;br /&gt;&lt;strong&gt;Conclusion&lt;br /&gt;&lt;br /&gt;&lt;/strong&gt;In accordance with my expectations. The nonblocking mode outperforms the blocking mode as long as enough long queries come into the play. If all the db queries are very small then the blocking mode will triumph mainly due to the overhead of fibers. But nevertheless, once there is a even single long query for every 100 short queries the performance is swayed into the nonblocking mode favor. There are still a few optimizations to be done, mainly complete the integrations with the EventMachine which should theoritically enhance performance. The next step is to integrate this into some framework and build a real application using this approach. Since Sequel is working now having Ramaze or Merb running in non-blocking mode should be a fairly easy task. Sadly Rails is out of the picture for now as it does not support Ruby 1.9 yet.&lt;br /&gt;&lt;br /&gt;I reckon an application that does all its IO in an evented way will need much less processes per CPU core to make full use of it. Actually I am guessing that a single core can be maxed by a single process. If this is the case then I can live much happier if I can replace the 16 Thin processes running on my server with only 4. Couple that with the 30% memory savings we get from using RubyEE and we are talking about an amazing 82.5% memory foot print reduction without sacrificing performance.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/15616477-1268846820954692730?l=oldmoe.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://oldmoe.blogspot.com/feeds/1268846820954692730/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=15616477&amp;postID=1268846820954692730' title='12 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/15616477/posts/default/1268846820954692730'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/15616477/posts/default/1268846820954692730'/><link rel='alternate' type='text/html' href='http://oldmoe.blogspot.com/2008/08/case-for-nonblocking-ruby-stack.html' title='The case for a nonblocking Ruby stack'/><author><name>oldmoe</name><uri>http://www.blogger.com/profile/14341721253106429644</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='24' height='32' src='http://3.bp.blogspot.com/_XFDHsTWrvBY/SsFeIEFTI_I/AAAAAAAAADU/NphzXmrghkw/S220/oldmoe.png'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://1.bp.blogspot.com/_XFDHsTWrvBY/SJTYtPOmWBI/AAAAAAAAACE/HisrXjBz3dQ/s72-c/http_serving_performance.png' height='72' width='72'/><thr:total>12</thr:total></entry><entry><id>tag:blogger.com,1999:blog-15616477.post-6455334409832016838</id><published>2008-08-03T00:12:00.007+03:00</published><updated>2008-12-12T04:09:42.966+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ruby'/><category scheme='http://www.blogger.com/atom/ns#' term='performance'/><category scheme='http://www.blogger.com/atom/ns#' term='threads'/><category scheme='http://www.blogger.com/atom/ns#' term='ruby 1.9'/><category scheme='http://www.blogger.com/atom/ns#' term='fibers'/><title type='text'>Ruby Fibers Vs Ruby Threads</title><content type='html'>Ruby 1.9 Fibers are touted as lightweight concurrency elements that are much lighter than threads. I have noticed a sizbale impact when I was benchmarking an application that made heavy use of fibers. So I wondered what If I switched to threads instead? After some time fighting with threads I decided I needed to write something specific for this comparison. I have written a small application that would spawn a number of fibers (or threads) and then would return the time went into this operation. I also recorded the VM size after the operation (all created fibers and threads are still reachable, hence, no garbage collection). I did not measure the cost of context switching for both approaches, may be in another time.&lt;br /&gt;&lt;br /&gt;Here are the results for creation time:&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/_XFDHsTWrvBY/SJTSh5ZI5FI/AAAAAAAAAB0/5d0xH4lJSxs/s1600-h/fiber-v-thread-creation.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;" src="http://2.bp.blogspot.com/_XFDHsTWrvBY/SJTSh5ZI5FI/AAAAAAAAAB0/5d0xH4lJSxs/s400/fiber-v-thread-creation.png" border="0" alt="" id="BLOGGER_PHOTO_ID_5230036547067765842" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;And the results for memory usage:&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_XFDHsTWrvBY/SJTS8IYc33I/AAAAAAAAAB8/QwPnXHAgpFI/s1600-h/fiber-v-thread-memory.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;" src="http://3.bp.blogspot.com/_XFDHsTWrvBY/SJTS8IYc33I/AAAAAAAAAB8/QwPnXHAgpFI/s400/fiber-v-thread-memory.png" border="0" alt="" id="BLOGGER_PHOTO_ID_5230036997768011634" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Conclusion&lt;br /&gt;&lt;br /&gt;Fibers are much faster to create than threads, they eat much less memory too. There is also a limit on the number of threads for 1.9 as I maxed on 3070 threads while fibers were not complaining when I created 100,000 of them (but they took 203 seconds and occuppied a whoping 500MB of RAM).&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/15616477-6455334409832016838?l=oldmoe.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://oldmoe.blogspot.com/feeds/6455334409832016838/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=15616477&amp;postID=6455334409832016838' title='15 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/15616477/posts/default/6455334409832016838'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/15616477/posts/default/6455334409832016838'/><link rel='alternate' type='text/html' href='http://oldmoe.blogspot.com/2008/08/ruby-fibers-vs-ruby-threads.html' title='Ruby Fibers Vs Ruby Threads'/><author><name>oldmoe</name><uri>http://www.blogger.com/profile/14341721253106429644</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='24' height='32' src='http://3.bp.blogspot.com/_XFDHsTWrvBY/SsFeIEFTI_I/AAAAAAAAADU/NphzXmrghkw/S220/oldmoe.png'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://2.bp.blogspot.com/_XFDHsTWrvBY/SJTSh5ZI5FI/AAAAAAAAAB0/5d0xH4lJSxs/s72-c/fiber-v-thread-creation.png' height='72' width='72'/><thr:total>15</thr:total></entry><entry><id>tag:blogger.com,1999:blog-15616477.post-1488678094127483011</id><published>2008-07-27T23:24:00.005+03:00</published><updated>2008-07-27T23:39:21.442+03:00</updated><title type='text'>Final Veridect, No One To Blame!</title><content type='html'>&lt;br/&gt;&lt;br/&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://inwaf2007.jeeran.com/%D9%86%D8%AF%D8%B9%D9%88-%D8%A7%D9%84%D9%84%D9%84%D8%A9-%D8%A7%D9%86-%D9%8A%D8%B1%D8%AD%D9%85%D9%87%D9%85--.jpg%20%20"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer; width: 400px;" src="http://inwaf2007.jeeran.com/%D9%86%D8%AF%D8%B9%D9%88-%D8%A7%D9%84%D9%84%D9%84%D8%A9-%D8%A7%D9%86-%D9%8A%D8%B1%D8%AD%D9%85%D9%87%D9%85--.jpg" alt="" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;More than a thousand humans drowned in the sea but apparently no one is to blame for this accident.&lt;br /&gt;&lt;br /&gt;Apparently no one cares.&lt;br /&gt;&lt;br /&gt;Are lives that cheap? Looks like some people believe so.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/15616477-1488678094127483011?l=oldmoe.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://oldmoe.blogspot.com/feeds/1488678094127483011/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=15616477&amp;postID=1488678094127483011' title='4 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/15616477/posts/default/1488678094127483011'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/15616477/posts/default/1488678094127483011'/><link rel='alternate' type='text/html' href='http://oldmoe.blogspot.com/2008/07/final-veridect-no-one-to-blame.html' title='Final Veridect, No One To Blame!'/><author><name>oldmoe</name><uri>http://www.blogger.com/profile/14341721253106429644</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='24' height='32' src='http://3.bp.blogspot.com/_XFDHsTWrvBY/SsFeIEFTI_I/AAAAAAAAADU/NphzXmrghkw/S220/oldmoe.png'/></author><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-15616477.post-1487174869975225807</id><published>2008-07-12T02:55:00.020+03:00</published><updated>2008-12-12T04:09:43.470+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ruby'/><category scheme='http://www.blogger.com/atom/ns#' term='eventmachine'/><category scheme='http://www.blogger.com/atom/ns#' term='nonblocking'/><category scheme='http://www.blogger.com/atom/ns#' term='fibers'/><category scheme='http://www.blogger.com/atom/ns#' term='postgres'/><category scheme='http://www.blogger.com/atom/ns#' term='ansynchronous'/><title type='text'>Faster IO for Ruby with Postgres</title><content type='html'>&lt;p&gt;&lt;span style="font-weight:bold;"&gt;Or 40% faster DB Access for your Ruby applications!&lt;/span&gt;&lt;/p&gt;&lt;p&gt;In a previous &lt;a href="http://oldmoe.blogspot.com/2008/07/untwisting-event-loop.html"&gt;post&lt;/a&gt; I talked a bit about event based programming for Ruby. I mentioned the EventMachine/Asymy combo as a means of doing Asynchronous database operations hence freeing up the Ruby runtime to do other things while it is waiting on database I/O operations. Even more, the devs need not worry about using a different programming model, with the help of Ruby Fibers we will continue to program in the same old ways while Fibers will be doing all the twisted work underneath. Very promising indeed, but one big elephant in the room was the immaturity of the current solution. Asymy is still very infant and it is based on the super slow pure Ruby MySQL driver, not to mention that it is fairly incomplete as well.&lt;br /&gt;&lt;/p&gt;&lt;br /&gt;So, what can we do about the elephant in the room? There is an Arabic proverb that basically says "Nothing can beat iron but iron" and this is exactly what we are going to do. Enter Postgres, the database with a realistic, unfriendly elephant mascot. Go away dolphins, a real elephant is in the room now.&lt;br /&gt;&lt;br /&gt;Surprisingly Postgres happens to have an excellent asynchronous client &lt;a href="http://www.postgresql.org/docs/8.3/static/libpq-async.html"&gt;API&lt;/a&gt;. It allows you to do almost all operations in a non blocking way. More surprisingly the &lt;a href="http://rubyforge.org/projects/ruby-pg"&gt;Postgres driver for Ruby&lt;/a&gt; covers almost all those asynchronous API calls. The driver was originally written by Matz (yes the man himself) in 1997. It was later updated by ematsu in 1999 and now we have an update fresh from the oven in March 2008 by Jdavis. If you go through the C source code you will find many hidden gems. The methods are fairly well documented and you will discover that the driver has a blocking method that wraps the asynchronous calls inside but it does so in a Ruby threading friendly way. This way a threaded application will not block on the Postgres SQL commands. Good thing but I am more interested in the asynchronous side of the fence.&lt;br /&gt;&lt;br /&gt;Let's walk through the API and see how can we use it to do non blocking database access. First you will need to install the gem ("sudo gem install pg"). Then you need to require 'pg' in your code.&lt;br /&gt;&lt;br /&gt;One problem though before we start. The current driver has this nasty little bug that prevents you from setting the connection to nonblocking. It is actually a bug in the parameter count defined in the Ruby interface. A simple switch from 0 to 1 fixes this. To save you time and sweat I have provided a replacement &lt;a href="http://fibered-pg.googlecode.com/files/pg-0.7.9.2008.03.18.gem"&gt;gem&lt;/a&gt; with the modified sources (till the bug is fixed upstream). Now let's get back to the code.&lt;br /&gt;&lt;br /&gt;Here's how to get things started&lt;pre class="code"&gt;require 'pg'&lt;br /&gt;# I have configured postgres to run in *trusted* mode&lt;br /&gt;# so I don't need to supply a password&lt;br /&gt;conn = PGconn.new({:host=&gt;'localhost',:user=&gt;'postgres',:dbname=&gt;'evented'})&lt;br /&gt;conn.setnonblocking(true)&lt;/pre&gt;This way our connection is ready for async operations. Now we need to start sending some sql commands to our connection. To do that we normally use the PGconn#exec method. But this method will block, waiting on postgres. So instead we will use the PGconn#send_query method. This method will return immediately, not waiting for Postgres to actually process the sql command. Here's how are going to use it.&lt;pre class="code"&gt;conn.send_query("select * from users where name like '%am%'")&lt;br /&gt;# the method will return immediately (or raise an exception in case of an error)&lt;/pre&gt;But wait, where are the results? Normally we expect the call to return with the data. Now where is my data? The results are being processed right now at the server side. We can continue to do other things till they come. But how do we know when they arrive? It turns out that this is easy as well. The PGconn instance provides a method that returns the connection's socket descriptor. PGconn#socket that is. We retrieve that socket descriptor and wrap it in a Ruby IO object by calling&lt;pre class="code"&gt;io = IO.new(conn.socket)&lt;/pre&gt;Now have a nice IO object that we can get notified of its activity in a select call. For the uninitiated, event based programming is done by have a tight loop that runs forever. Within this loop we check if IO events happen and if so we respond to them. One efficient way of doing so is using the Ruby Kernel#select method (which is a wrapper to the UNIX select). The select method works that way: you provide it with three lists, one for sockets that you need to read from and one for sockets that you need to write to, the third is for errors that you are interested in. The call returns an array of the sockets that can be read/write or nil if none is ready.&lt;br /&gt;&lt;br /&gt;We will use select as follows:&lt;pre class="code"&gt;# the method that will be called if input is ready&lt;br /&gt;def process_command(conn)&lt;br /&gt; # we will detail the implementation soon&lt;br /&gt;end&lt;br /&gt;&lt;br /&gt;loop do&lt;br /&gt; # we supply a list of sockets we need to read from.&lt;br /&gt; # Only our io object in this case. we nullify&lt;br /&gt; # the other lists and we set a timeout&lt;br /&gt; res = select([io],nil,nil, 0.001)&lt;br /&gt; # of course this needs to be done in a cleaner way&lt;br /&gt; process_command(conn) unless result.nil?&lt;br /&gt;end&lt;/pre&gt;This way whenever there is info to read from the socket we will not get a nil (we will get an array actually) so we can call the process command. When the process command gets called it knows that there is data in the connection to be read so it calls the PGconn#consume_input method. After which it checks to see if the conn is busy or not. If it is still busy, it does nothing (it will do in a later event). On the other hand, if the connection is not busy then we start calling the PGconn#get_result method and append what we get to the result we got so far. We keep doing that till we get a nil result which indicates the end of the command and the readiness of the connection to accept further commands. Here is how the method will look like:&lt;pre class="code"&gt;def process_command(conn)&lt;br /&gt; conn.consume_input&lt;br /&gt; unless conn.is_busy&lt;br /&gt;  res, data = 0, []&lt;br /&gt;  while res != nil&lt;br /&gt;   res = get_result&lt;br /&gt;   res.each {|d| data.push d}unless res.nil?  &lt;br /&gt;  end&lt;br /&gt;  #we are done, we need to put this data some where &lt;br /&gt; end&lt;br /&gt;end&lt;/pre&gt;Several things to be noted. First, one cannot process several commands using the same connection at once. You need several connections to achieve parallel command processing. Second, the model described above works in the twisted way, to get things working the normal way you can use Ruby Fibers (or continuations but they apparently leak memory)&lt;br /&gt;&lt;br /&gt;I have put together a couple of Ruby classes that implement a nonblocking connection pool and a fiber pool. You can find them &lt;a href="http://fibered-pg.googlecode.com/files/fibered_pg.tar.gz"&gt;here&lt;/a&gt; Using those you can write code that looks like this:&lt;pre class="code"&gt;require 'fiber_pool'&lt;br /&gt;require 'fibered_connection_pool'&lt;br /&gt;&lt;br /&gt;options = {:host=&gt;'localhost',:user=&gt;'postgres',:dbname=&gt;'evented'}&lt;br /&gt;&lt;br /&gt;cpool = FiberedC onnectionPool.new(options, 12)&lt;br /&gt;# second param is the number of connections to spawn, defaults at 8&lt;br /&gt;# note that one more connection than those will be spawned. This one&lt;br /&gt;# will be used for processing blocking requests.&lt;br /&gt;&lt;br /&gt;fpool = FiberPool.new(100)&lt;br /&gt;# the number of fibers to spawn, defaults at 50&lt;br /&gt;&lt;br /&gt;100.times do&lt;br /&gt; fpool.spawn do&lt;br /&gt;  cpool.exec(some_sql_command, true) #true means async&lt;br /&gt;  cpool.exec(some_other_sql_command, true)&lt;br /&gt;  cpool.exec(yet_another_sql_command, true)&lt;br /&gt; end&lt;br /&gt;end&lt;br /&gt;&lt;br /&gt;# our event loop&lt;br /&gt;loop do&lt;br /&gt; res = select(cpool.sockets,nil,nil,0) #check for something to read&lt;br /&gt; # IO is monkey patched to be able to hold a reference to the connection&lt;br /&gt; res.first.each{ |s|s.connection.process_command } if res&lt;br /&gt;end&lt;/pre&gt;This works as follows, once a fiber calls cpool.exec the query is sent to the pool for processing and the fiber is halted, giving way for another one to start processing. The other one will halt as well once it hits a cpool.exec. Later during the event loop you will get notifications of completion of queries (in any order) and resume the fiber associated with the finished query. Note that commands issued in the same fiber will run sequentially while those issued from different fibers will interleave. This is effectively what is achieved by threading but without its costs.&lt;br /&gt;&lt;br /&gt;Performance:&lt;br /&gt;&lt;br /&gt;I am sure that my code might use some tweaking but I am getting very good results already. During benchmarking I found out that the cost on isntantiating fibers could be high (the cost of pausing and resuming is high as well, but unavoidable) So I created a pool of fibers that can be reused (a very naive implementation that can make use of lots of improvement).&lt;br /&gt;&lt;br /&gt;I tested by issuing a group of long and short queries together. You actually provide the test program with the number of long queries and the multiplier it should use for short queries. i.e. ruby test.rb 10 20 will iterate 10 times and issue a long query then within the same iteration it will issue 20 short queries. It will do this in a blocking and then nonblocking way, reporting the time taken for each to complete and the percentage of performance increase/decrease.&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/_XFDHsTWrvBY/SHgCXig4mmI/AAAAAAAAABc/G49_0T-pxYc/s1600-h/evented_query_performance_chart.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;" src="http://1.bp.blogspot.com/_XFDHsTWrvBY/SHgCXig4mmI/AAAAAAAAABc/G49_0T-pxYc/s400/evented_query_performance_chart.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5221926371361069666" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;I tested for 10, 50 and 100 long queries with the following multipliers (1, 2, 5, 10, 50, 100). The graph shows the performance gain for each number of queries vs the multiplier. For example 50 long queries with a multiplier of 10 (i.e. 500 short queries) achieves a 39.6% reduction in query execution time. I have repeated many of the tests several time (not all of them, too lazy to do that). The repeated tests showed consistent results so I am pretty confident of the presented results.&lt;br /&gt;&lt;/p&gt;&lt;br /&gt;Here is the full list:&lt;pre class="code"&gt;&lt;br /&gt;         Queries         Mode&lt;br /&gt;Ratio  Long Short Blocking Non Blocking Advantage&lt;br /&gt;&lt;br /&gt;:1/2   10   20    0.56     0.5          10.27%&lt;br /&gt;       50   100   2.55     2.26         11.19%&lt;br /&gt;       100  200   5.15     4.46         13.53%&lt;br /&gt;&lt;br /&gt;:1/5   10   50    0.55     0.4          27.04%&lt;br /&gt;       50   250   2.72     1.83         32.82%&lt;br /&gt;       100  500   5.45     3.63         33.39%&lt;br /&gt;&lt;br /&gt;:1/10  10   100   0.6      0.4          33.76%&lt;br /&gt;       50   500   3.01     1.82         39.67%&lt;br /&gt;       100  1000  5.9      3.65         38.13%&lt;br /&gt;&lt;br /&gt;:1/20  10   200   0.72     0.45         38.12%&lt;br /&gt;       50   1000  3.43     2.1          38.73%&lt;br /&gt;       100  2000  6.83     4.33         36.53%&lt;br /&gt;&lt;br /&gt;:1/50  10   500   0.98     0.62         36.57%&lt;br /&gt;       50   2500  4.78     3.23         32.36%&lt;br /&gt;       100  5000  9.74     8.68         10.93%&lt;br /&gt;&lt;br /&gt;:1/100 10   1000  1.46     0.94         35.40%&lt;br /&gt;       50   5000  7.42     5.17         30.31%&lt;br /&gt;       100  10000 14.27    12.68        11.15%&lt;/pre&gt;The area I would like to focus on for performance tuning is the size of the fiber pool. The test is a bit sensitive to it so I believe I can gain a bit more performance with insane query counts if I optimize my fiber pool a bit. Setting the initial size too high certainly helps, but eats too much memory to make it usable.&lt;br /&gt;&lt;br /&gt;A final note. I am playing with using this along side an EventMachine based http server. It works OK but is a cpu hog. Propably due to using select in next_tick calls withing EM's event loop. I would love to be able to provide EM with a list of IO objects and a call back instead of requiring me to use it to open the connection. Nevertheless, even though in many cases the nonblocking db implementation is slower than a blocking one in the http serving arena, I managed to get ~800 req/s vs ~500 req/s for a very typical use case, A request that runs a long query followed by many short ones. Impressive to say the least. I might be even try to hack EM to support the feature I need and then see what performance this could yield.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;UPDATE&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;Apparently one can get more performance for the blocking requests if the fiber pool is initiated AFTER the blocking calls. Possibly due to the VM being impacted by the memory increase. Rerunning some of the tests showed fractional improvements for the blocking case. On the other hand, I tried some of the tests while another process was doing heavy I/O (RDoc generation). The performance gain jumped to an amazing 76% in one of the tests (it was generally between 51% and 76%).&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/15616477-1487174869975225807?l=oldmoe.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://oldmoe.blogspot.com/feeds/1487174869975225807/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=15616477&amp;postID=1487174869975225807' title='13 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/15616477/posts/default/1487174869975225807'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/15616477/posts/default/1487174869975225807'/><link rel='alternate' type='text/html' href='http://oldmoe.blogspot.com/2008/07/faster-io-for-ruby-with-postgres.html' title='Faster IO for Ruby with Postgres'/><author><name>oldmoe</name><uri>http://www.blogger.com/profile/14341721253106429644</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='24' height='32' src='http://3.bp.blogspot.com/_XFDHsTWrvBY/SsFeIEFTI_I/AAAAAAAAADU/NphzXmrghkw/S220/oldmoe.png'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://1.bp.blogspot.com/_XFDHsTWrvBY/SHgCXig4mmI/AAAAAAAAABc/G49_0T-pxYc/s72-c/evented_query_performance_chart.png' height='72' width='72'/><thr:total>13</thr:total></entry><entry><id>tag:blogger.com,1999:blog-15616477.post-1173684352627476782</id><published>2008-07-08T04:19:00.001+03:00</published><updated>2008-07-08T04:20:55.968+03:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ruby'/><category scheme='http://www.blogger.com/atom/ns#' term='eventmachine'/><category scheme='http://www.blogger.com/atom/ns#' term='rails'/><category scheme='http://www.blogger.com/atom/ns#' term='asymy'/><category scheme='http://www.blogger.com/atom/ns#' term='fibers'/><category scheme='http://www.blogger.com/atom/ns#' term='continuations'/><category scheme='http://www.blogger.com/atom/ns#' term='asynchronous'/><title type='text'>Untwisting the Event Loop</title><content type='html'>Have you ever wondered why your Rails application is so memory hungry while it is not really trying to fully utilize your CPUs? To saturate your CPUs you have to have a large number of Thin (or Mongrel or whatever) instances. Why is that? We all know that the Ruby interpreter is not able to utilize more than one CPU (or no more than one CPU at a time in the case of 1.9). But why can't Ruby (or may be it's Rails?) utilize the processors efficiently? Let's look for an answer to this question.&lt;br /&gt;&lt;br /&gt;First off, what happens in a typical Rails action? The Rails framework will be doing some request mapping and routing which is mostly CPU (if we consider memory latency negligible) Then a few requests will be sent to the database to retrieve some data after which a rendering process which is mostly CPU as well.&lt;br /&gt;&lt;pre class="code"&gt;def show&lt;br /&gt;  @user = User.find(params[id]) #db access&lt;br /&gt;  @events = Events.find(:all) #another db access&lt;br /&gt;  render :action =&gt; :show #rendering&lt;br /&gt;end&lt;/pre&gt;&lt;br /&gt;The problem here comes with the database part of the action. Calls to the database will block processing till results get back from the DBMS. During that time, Rails will be frozen and not trying to do any thing else till the call ends. Good news is that threads can help here (even Ruby's green threads). A blocked thread will give way to other threads till it is back in the ready state. Thus filling those slots with some useful processing. Sounds good enough? NO!&lt;br /&gt;&lt;br /&gt;Sadly Rails is NOT thread safe. You cannot use threads to do parallel processing in Rails. So why not something like Merb? I hear you say. Well Merb and threads will be able to interleave CPU operations and help with the time spent on IO in something like fetching data from some other service. But it won't save you when you do database IO. Simply because of the simple fact that calling C extensions blocks the whole Ruby interpreter. Yes, you read it correctly the first time. Nothing cannot be scheduled while a native call is being issued. Since database drivers are mostly C extensions they suffer from this. Your nice SELECT statement keeps the whole Ruby interpreter on hold till it is finished.&lt;br /&gt;&lt;br /&gt;But there must be a solution to this. We cannot be all left high and dry with interpreters eating our memory and not really using our CPUs.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Enter EventMachine and AsyMy&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;For those who are not in the loop of events (bun intended) there happens to be another approach to this problem. Event based (read asynchronous) IO. In this mode of operation you request an IO operation and tell the event loop what to do when the request is fulfilled (either fully or partially). An excellent library for event handling exists for Ruby which is Francis' &lt;a href="http://www.rubyeventmachine.com/"&gt;EventMachine&lt;/a&gt; (used internally by the Thin server and the evented flavour of Mongrel). But still, using EventMachine does not magically solve all our problems. The question that keeps popping up, what to do with database access? &lt;a href="http://www.github.com/tqbf/asymy"&gt;AsyMy&lt;/a&gt; to the rescue! AsyMy, written by Thomas Ptacek&lt;b&gt;,&lt;/b&gt; is an evented driver for MySQL that operates in an asynchronous fashion. A quick example will look like:&lt;br /&gt;&lt;pre class="code"&gt;connection.execute('SELECT * from events') do |headers,data|&lt;br /&gt;  # do something with headers and data&lt;br /&gt;  pp headers&lt;br /&gt;  pp data&lt;br /&gt;end&lt;/pre&gt;Asymy is still in a very early stage, the performance is horrible (as it is based on the darn slow pure Ruby MySQL driver) and it comes with many rough corners (I was not able to run INSERTs and UPDATEs without hacking it, and I am still not able to run the callbacks for those). Nevertheless, this is a formidable achievement on the road to a very fast single threaded implementation.&lt;br /&gt;&lt;br /&gt;Here's how our action would look like if there was an Asymy adapter for ActiveRecord&lt;br /&gt;&lt;pre class="code"&gt;#this is propably wrong but it can illustrate&lt;br /&gt;#the twisted nature of evented programming&lt;br /&gt;def show&lt;br /&gt;  User.find(params[:id]) do |result_set|&lt;br /&gt;      @user = result_set&lt;br /&gt;      Events.find(:all) do |result_set|&lt;br /&gt;          @events = result_set&lt;br /&gt;          @events.each do |event|&lt;br /&gt;              event.owner = @user&lt;br /&gt;              if event != @events.last&lt;br /&gt;                  event.save&lt;br /&gt;              else&lt;br /&gt;                  event.save do |ev|&lt;br /&gt;                      render :action =&gt; :show&lt;br /&gt;                  end&lt;br /&gt;              end&lt;br /&gt;          end &lt;br /&gt;      end&lt;br /&gt;  end&lt;br /&gt;end&lt;/pre&gt;We had to twist the function flow to be able to make use of the evented nature of the new driver. Instead of flow passing normally it is being scattered in the different callbacks. This is one of the areas where event based programming makes you change the way you think about program flow. A hurdle for many developers and a show stopper for some. No wonder the event library for Python is called Twisted&lt;br /&gt;&lt;br /&gt;Why not untangle this with Fibers?&lt;br /&gt;&lt;br /&gt;Fibers are lightweight concurrency primitives introduced in Ruby 1.9. How light weight? well they don't come at zero cost but in long running requests the weight they add can be negligible. Fibers provide some form of cooperative (rather than preemptive) concurrency inside a single thread (you cannot pass fibers between threads, you have been warned). Fibers enjoy the ability to pause and resume like continuations, but they don't suffer from the memory leaks the continuations have. When we use this feature wisely we can unwind the action code above to look like this:&lt;br /&gt;&lt;pre class="code"&gt;def show&lt;br /&gt;  @user = User.find(params[:id]&lt;br /&gt;  @events = Events.find(:all).each do |event|&lt;br /&gt;      event.owner = @user&lt;br /&gt;      event.save&lt;br /&gt;  end&lt;br /&gt;end&lt;/pre&gt;Huh? this is the normal action code we are used to. Well, using fibers we can do this and still do things under the hood in an evented way.&lt;br /&gt;&lt;br /&gt;To make things clear we need to illustrate Fibers with an example:&lt;br /&gt;&lt;pre class="code"&gt;require 'fiber'&lt;br /&gt;&lt;br /&gt;fiber = Fiber.new do&lt;br /&gt;  #do something&lt;br /&gt;  Fiber.yield another_thing&lt;br /&gt;  #do yet another thing&lt;br /&gt;end&lt;br /&gt;&lt;br /&gt;yielded = fiber.resume # =&gt; runs the fiber till the yield,&lt;br /&gt;                     # returns the yielded value&lt;br /&gt;                     # and pauses the fiber where it is&lt;br /&gt;fiber.resume #=&gt; re-runs the fiber from the point it was paused.&lt;br /&gt;fiber.resume #=&gt; no more statements to run, raises an exception&lt;/pre&gt;Let's see how can this be useful for dispatching controller actions (this code will preferrably be in the server itself)&lt;br /&gt;&lt;pre class="code"&gt;Fiber.new do&lt;br /&gt;  Dispatcher.dispatch(controller,action,req,res)&lt;br /&gt;  send_response res&lt;br /&gt;end.resume&lt;/pre&gt;Inside the action we call the find method repeatedly. This method could be implemented like this:&lt;br /&gt;&lt;pre class="code"&gt;class DataStore&lt;br /&gt;  def find(*args)&lt;br /&gt;      query = construct_query(*args)&lt;br /&gt;      fiber = Fiber.current # grab the current fiber&lt;br /&gt;      conn.execute(query) do |headers, data|&lt;br /&gt;          fiber.resume convert_to_objects(data)&lt;br /&gt;      end&lt;br /&gt;      yield&lt;br /&gt;  end&lt;br /&gt;end&lt;/pre&gt;This way whenever the code passes a find method it will pass the query to the db driver, return immediately and pause, giving room for other requests to be processed. Once the data comes from the db server the call back is run and it resumes the fiber (passing to it the result of the query). The result gets passed back to the caller of the function and the original action method continues till completion (or till it is paused again by another find method)&lt;br /&gt;&lt;br /&gt;Roger Pack has a nice writeup (with actual working code) on the Evented Fibered combo &lt;a href="http://betterlogic.com/roger/?p=339"&gt;here&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;Charles Jolley implemented a similar thing &lt;a href="http://www.okito.net/post/37659446/pipelined-easy-event-driven-programming-for-ruby"&gt;here&lt;/a&gt;. It is called Pipelined and while it is more obtrusive than the approach described above, it still has the advantage of being optional. Pipelined uses continuations and hence is available to Ruby 1.8 (and Rails).&lt;br /&gt;&lt;br /&gt;I am still ironing out and tying things together (and doing lots of benchmarks) and I would like to tell you that I have ditched AsyMy for now for another alternative which I will attempt to discuss in detail in another blog post.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/15616477-1173684352627476782?l=oldmoe.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://oldmoe.blogspot.com/feeds/1173684352627476782/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=15616477&amp;postID=1173684352627476782' title='9 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/15616477/posts/default/1173684352627476782'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/15616477/posts/default/1173684352627476782'/><link rel='alternate' type='text/html' href='http://oldmoe.blogspot.com/2008/07/untwisting-event-loop.html' title='Untwisting the Event Loop'/><author><name>oldmoe</name><uri>http://www.blogger.com/profile/14341721253106429644</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='24' height='32' src='http://3.bp.blogspot.com/_XFDHsTWrvBY/SsFeIEFTI_I/AAAAAAAAADU/NphzXmrghkw/S220/oldmoe.png'/></author><thr:total>9</thr:total></entry><entry><id>tag:blogger.com,1999:blog-15616477.post-4044556625566290037</id><published>2008-06-06T01:04:00.007+03:00</published><updated>2008-12-12T04:09:43.732+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Hamza'/><category scheme='http://www.blogger.com/atom/ns#' term='delivery'/><category scheme='http://www.blogger.com/atom/ns#' term='baby'/><title type='text'>Meet Hamza</title><content type='html'>&lt;div style="text-align: right;"&gt;&lt;span class="ayaah"  style="font-size:130%;"&gt;&lt;br /&gt;وَلَقَدْ خَلَقْنَا الْإِنسَانَ مِن سُلَالَةٍ مِّن طِينٍ&lt;/span&gt;&lt;span style="font-size:130%;"&gt; (12)  &lt;/span&gt;&lt;span class="ayaah"  style="font-size:130%;"&gt;ثُمَّ جَ&lt;/span&gt;&lt;span class="ayaah"  style="font-size:130%;"&gt;عَلْنَاهُ نُطْفَةً فِي قَرَارٍ مَّكِينٍ&lt;/span&gt;&lt;span style="font-size:130%;"&gt; (13)  &lt;/span&gt;&lt;span class="ayaah"  style="font-size:130%;"&gt;ثُمَّ خَلَقْنَا النُّطْفَةَ عَلَقَةً فَخَلَقْنَا الْعَلَقَةَ مُضْغَةً فَخَلَقْنَا الْمُضْغَةَ عِظَاماً فَكَسَوْنَا الْعِظَامَ لَحْماً ثُمَّ أَنشَأْنَاهُ خَلْقاً آخَرَ فَتَبَارَكَ اللَّهُ أَحْسَنُ الْخَالِقِينَ&lt;/span&gt;&lt;span style="font-size:130%;"&gt; (14) &lt;span style="font-weight: bold;"&gt;المؤمنون&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style="text-align: right;"&gt;&lt;span style="font-weight: bold;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;div style="text-align: left;"&gt;&lt;div style="text-align: left;"&gt;Verily We created man from a product of wet earth (12);  Then placed him as a drop (of seed) in a safe lodging (13);  Then fashioned We the drop a clot, then fashioned We the clot  a little lump, then fashioned We the little lump bones, then clothed the bones with flesh, and then produced it as another creation. So blessed be Allah, the Best of creators!(14) &lt;span style="font-weight: bold;"&gt;The Believers, Holy Quran&lt;/span&gt;&lt;br /&gt;&lt;/div&gt;&lt;span style="font-weight: bold;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;div style="text-align: center;"&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_XFDHsTWrvBY/SEhlNLb0n8I/AAAAAAAAAA8/DiVyVE-tGKw/s1600-h/DSC00046.JPG"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer;" src="http://3.bp.blogspot.com/_XFDHsTWrvBY/SEhlNLb0n8I/AAAAAAAAAA8/DiVyVE-tGKw/s400/DSC00046.JPG" alt="" id="BLOGGER_PHOTO_ID_5208524246136168386" border="0" /&gt;&lt;/a&gt;Hamza&lt;br /&gt;&lt;br /&gt;&lt;div style="text-align: left;"&gt;Meet Hamza, my firstborn son. God's gift to me. I am so full of emotions that I am not able to express them adequately. It has been one very strange night when we left for the hospital. We wanted to have a natural childbirth but he refused to turn upside down. We decided to go for an operation and we had the luxury of presetting the date of birth. A bit less hassle than the shock that accompanies a surprising labor pain but still manages to make you very anxious.&lt;br /&gt;&lt;br /&gt;We drove to the hospital, the doctor was there and my wife suddenly realized that there is no turning back and that she is going to actually be operated upon. She didn't panic or anything but she got her self one of the palest faces I ever saw on her. The doctor came to the room and recommended &lt;span class="me"&gt;epidural anesthesia, which meant she will be awake during the operation. I gulped, as I was not sure if she will feel pain that way or not. Then twice gulped when the doctor asked me to change into an operation suit. "I will need your help there" he said.&lt;br /&gt;&lt;br /&gt;I am not sure how did I change or how did the time pass when I was waiting in the doctor's area. Watching them washing their hands over and over (much like in the Panadol commercial). I sat there, silent, contemplating the moment. Thinking of what I might say to support her and if I am really capable of seeing her being cut. It was bizarre, ideas seemed to flash very fast in my mind and strangely it seemed totally empty at the same time. Something similar to that feeling when you try to memorize everything in your head minutes before an exam.&lt;br /&gt;&lt;br /&gt;That state was preempted when they called for me. I went inside. My first time in an operation room. They were about to start. I sat on a chair so that my head was a bit higher than hers. I leaned forward to be level with her and started whispering in her ear. She was praying, she was repeating prayers and she was trying to remember the names of every person she knew. To pray for him/her. At that time I glanced over her. Just  a glimpse as I ducked immediately after I saw that terrifying blood stream.&lt;br /&gt;&lt;br /&gt;I tried as hard as I can to hide the effect of what I saw. I kept soothing her and helping her with her prayers. At that time we were both repeating: "My God, Nothing goes smooth but what you make smooth, and You are who makes hardship smooth" and "All power and might belong to Allah, the most High, the Great". Again I took another glimpse, and my God! I saw a little foot in the doctor's hand!.&lt;br /&gt;&lt;br /&gt;It was pale, blueish white. The skin was wrinkled as wrinkled can be. The doctor was holding to it and pushing the rest of the baby out. My eyes were fixed on the scene in front of me. I was on complete awe. The miracle of birth. Exalted be Allah. I kept saying that between my words when I told my wife that I am seeing the baby. She was out of her breath. Firing questions as rapidly as she can manage. "Is he OK?", "Is he out already?". I tried to keep pace with her then resorted to "he's coming out, he's coming out"&lt;br /&gt;&lt;br /&gt;It was a few minutes till the doctor manage to get him completely out. I saw him, very very small. Very vulnerable. Very weak. I couldn't help the tear forming in my eyes for the sight of him. He was crying as the nurses wrapped him and took him away for his first shower. My wife was tired beyond belief. But she asked me to run after him to take his first photos.&lt;br /&gt;&lt;br /&gt;I ran for it and saw that they put him under some sort of a heater to keep him warm. I leaned on him, kissed him and recited the call to prayers in his both ears. They took him and showered him. And I watched the expression of disbelief on his face. It was funny to see such a thing. I wouldn't expect a different expression from an adult if was locked for nine months and then taken out like what happened to him.&lt;br /&gt;&lt;br /&gt;I showered him myself with photos. Very funny ones. I hope the collection will grow. As I watch him grow.&lt;br /&gt;&lt;br /&gt;I look at him now and see a much calmer expression on his face. Except when he is hungry of course. Getting him to that level means an inescapable scandal as he raises his voice loud enough to make sure that everyone in the neighborhood knows that we starve him.&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;br /&gt;Parenting is a new experience for me. I have always loved experiencing new things specially if I had a passion for them. In this case, I have more than a passion. I have love that I can't describe. I keep remembering how vulnerable I saw him that day. It affected me in a way that I have yet to understand.&lt;br /&gt;&lt;br /&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/15616477-4044556625566290037?l=oldmoe.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://oldmoe.blogspot.com/feeds/4044556625566290037/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=15616477&amp;postID=4044556625566290037' title='4 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/15616477/posts/default/4044556625566290037'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/15616477/posts/default/4044556625566290037'/><link rel='alternate' type='text/html' href='http://oldmoe.blogspot.com/2008/06/meet-hamza.html' title='Meet Hamza'/><author><name>oldmoe</name><uri>http://www.blogger.com/profile/14341721253106429644</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='24' height='32' src='http://3.bp.blogspot.com/_XFDHsTWrvBY/SsFeIEFTI_I/AAAAAAAAADU/NphzXmrghkw/S220/oldmoe.png'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://3.bp.blogspot.com/_XFDHsTWrvBY/SEhlNLb0n8I/AAAAAAAAAA8/DiVyVE-tGKw/s72-c/DSC00046.JPG' height='72' width='72'/><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-15616477.post-4038191382481030862</id><published>2008-04-18T00:35:00.007+02:00</published><updated>2008-12-12T04:09:44.037+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='performance'/><category scheme='http://www.blogger.com/atom/ns#' term='appengines'/><category scheme='http://www.blogger.com/atom/ns#' term='scalability'/><category scheme='http://www.blogger.com/atom/ns#' term='google'/><category scheme='http://www.blogger.com/atom/ns#' term='benchmark'/><category scheme='http://www.blogger.com/atom/ns#' term='appengine'/><title type='text'>How much horse power can your app engine deliver?</title><content type='html'>&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/_XFDHsTWrvBY/SAfRVkqm0-I/AAAAAAAAAAM/YCVxyMpMn9w/s1600-h/appengine_lowres.jpg"&gt;&lt;img style="margin: 0pt 0pt 10px 10px; float: right; cursor: pointer;" src="http://2.bp.blogspot.com/_XFDHsTWrvBY/SAfRVkqm0-I/AAAAAAAAAAM/YCVxyMpMn9w/s320/appengine_lowres.jpg" alt="" id="BLOGGER_PHOTO_ID_5190347264117625826" border="0" /&gt;&lt;/a&gt;I was wondering if the google app engine thing has the horse power to actually deliver the scalability experience that they are promising. I would expect that the app engines would be able to handle high loads and fulfill requests at decent rates.&lt;br /&gt;&lt;br /&gt;I decided to benchmark the app engines and see if it can live to the promises that google are making.&lt;br /&gt;&lt;br /&gt;For the benchmark I used a simple web page that visits the google data store, retrieves some data from it (small data set, so that the test wouldn't be bandwidth limited) then converts it to JSON (not using any library code for that, ugly hand written string manipulation code).&lt;br /&gt;&lt;br /&gt;The web page (actually the currently super useless &lt;a href="http://supergtd.appspot.com/"&gt;supergtd&lt;/a&gt; app) was written in accordance to the sample provided in the google app engine documentation. My Python skills are so immature but I can tell that everything is pretty straightforward. Route is determined, correct python file loaded and my get method is run. The method internally calls the data store and retrieves data then does some string processing and sends the result back.&lt;br /&gt;&lt;br /&gt;I tested from a server at softlayer (this one has the most bandwidth and the least latency of all the servers that I have access too, and it's a dual quad core monster for the records)&lt;br /&gt;&lt;br /&gt;Here are the results from the benchmark (click for a bigger version):&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/_XFDHsTWrvBY/SAfZPkqm0_I/AAAAAAAAAAU/pcyV2vh4oXk/s1600-h/test.gif"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer;" src="http://2.bp.blogspot.com/_XFDHsTWrvBY/SAfZPkqm0_I/AAAAAAAAAAU/pcyV2vh4oXk/s320/test.gif" alt="" id="BLOGGER_PHOTO_ID_5190355957131432946" border="0" /&gt;&lt;/a&gt;Frankly, I am very satisfied by the results. The app engine was capable of handling any load I threw at it. Even though the request I am testing is kinda simplistic it manages to pass through enough different parts of the stack to make it relevant. Many APIs will have resources that are only slightly more complex than the sample resource I created for this benchmark.&lt;br /&gt;&lt;br /&gt;Bottom line, top notch performance. And you don't even need to bother with your Elastic Computing Cloud configuration. You don't manage instances or anything (or even pay by them) you only pay per usage which is a much sweeter deal if you ask me (if your app does not require that you cross the sandbox boundaries that is). Oh and before I forget, serving static content was pretty fast too. Not breathtakingly fast but very decent to say the least (you should never skip proper client caching though, at least to save the bandwidth)&lt;br /&gt;&lt;br /&gt;If any one at Google is reading this I say "Great job, big hand for the big G". Now show me some Ruby love (and JavaScript too for what it's worth).&lt;br /&gt;&lt;br /&gt;Did I mention that I need some Ruby love? Please.&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/_XFDHsTWrvBY/SAfZPkqm0_I/AAAAAAAAAAU/pcyV2vh4oXk/s1600-h/test.gif"&gt;&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/15616477-4038191382481030862?l=oldmoe.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://oldmoe.blogspot.com/feeds/4038191382481030862/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=15616477&amp;postID=4038191382481030862' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/15616477/posts/default/4038191382481030862'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/15616477/posts/default/4038191382481030862'/><link rel='alternate' type='text/html' href='http://oldmoe.blogspot.com/2008/04/how-much-horse-power-your-app-engine.html' title='How much horse power can your app engine deliver?'/><author><name>oldmoe</name><uri>http://www.blogger.com/profile/14341721253106429644</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='24' height='32' src='http://3.bp.blogspot.com/_XFDHsTWrvBY/SsFeIEFTI_I/AAAAAAAAADU/NphzXmrghkw/S220/oldmoe.png'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://2.bp.blogspot.com/_XFDHsTWrvBY/SAfRVkqm0-I/AAAAAAAAAAM/YCVxyMpMn9w/s72-c/appengine_lowres.jpg' height='72' width='72'/><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-15616477.post-6432503650938598176</id><published>2008-04-11T15:00:00.003+02:00</published><updated>2008-04-11T15:12:04.861+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='appengines'/><category scheme='http://www.blogger.com/atom/ns#' term='python'/><category scheme='http://www.blogger.com/atom/ns#' term='google'/><title type='text'>Computing In The Cloud, Google App Engines</title><content type='html'>One day we will plug our applications in the computing cloud as much as we plug our devices into the electricity network. Surprisingly, this days is today!&lt;br /&gt;&lt;br /&gt;The trend towards offering computational infrastructure(storage, processing, bandwidth, etc) as a service is gaining momentum everyday. Services like Amazon's suite (S3, SimpleDB, EC2, SQS) and now Google AppEngines are manifestations of this trend. Infrastructure is becoming a commodity and large scale operations already invented their wheels, relieving the small to mid size player from having to invent wheels of their on. Stand on the shoulders of the giants and don't bother with performance, redundancy or data center setup overhead.&lt;br /&gt;&lt;br /&gt;I have had a look at Google App Engines. In a nut shell, App Engines is an web application environment that is capable of running a subset of Python(self). Even though there is no write support to the disk; the available features cover more than 90% of web development needs. You are able to process HTTP requests, generate response, configure your routes, persist data and query it, and connect to other web services. One of the most cunning features is the ability to authenticate google accounts, plus one to whoever lobbied for this feature. Suddenly your application will be available to millions of users with no registration overhead (defining your user count gets tricky that way though).&lt;br /&gt;&lt;br /&gt;One of the good things about Google App Engines is the choice of the programming language. Python(self) has dynamic and functional features that may be able to one day open the eyes of those who think that annotations are the next-big-thing!. I have been coding exclusively in Ruby and JavaScript (and of course &lt;a href="http://oldmoe.blogspot.com/2008/03/introducing-gammascript.html"&gt;GammaScript&lt;/a&gt;) for a while now. I have only experimented briefly with Python(self) earlier, so this was a good chance for a refresher. And Python(self) was fun except for some syntax quirks that I am not used to yet. I hope google extends their language support to cover Ruby and JavaScript (I stopped caring about Java as a web development tool long ago but others may be interested in including it as well). But kudos to Google for starting with Python(self).&lt;br /&gt;&lt;br /&gt;I was over joyed with the ease of deployment. I faced a minor bug in the supplied local webserver but had an easy work around. I am still working off Google's web framework and didn't move to something like Django or Pylons yet.&lt;br /&gt;&lt;br /&gt;Pros:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Platform comes with most of your development needs&lt;/li&gt;&lt;li&gt;Don't concern yourself with scaling, Google handles that for you, and is expected to do a very good job at it&lt;/li&gt;&lt;li&gt;Good choice for a starting language, Python(self) is a powerful daynamic language&lt;/li&gt;&lt;li&gt;Very good integration with Google accounts (brilliant!)&lt;/li&gt;&lt;li&gt;Ability to use full blown frameworks lik Django and Pylons&lt;br /&gt;&lt;/li&gt;&lt;/ul&gt; &lt;br /&gt;Cons:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Google lock in for your applications. Imagine that Google gets complaints against your application from governments (Chinese may be), will they shut it?&lt;/li&gt;&lt;li&gt;Sandboxes tend to constrain you. (that might be a pro rather than a con, it is your ticket for scaling)&lt;/li&gt;&lt;li&gt;For some reason Guido van Rossum thinks I should use "self" as the first argument in every method definition. I have read several explanations but I have yet to find something that convinces me.&lt;/li&gt;&lt;li&gt;I have been bitten with the white spaces thing a couple times. I attribute it to my acquired style though. Should be handled with time.&lt;br /&gt;&lt;/li&gt;&lt;/ul&gt; &lt;br /&gt;All in all, a very good move from Google, to them and everyone helping making cloud computing a reality. Thank you for bringing us the future!.&lt;br /&gt;&lt;br /&gt;I am tempted to test &lt;a href="http://heroku.com/"&gt;Heroku&lt;/a&gt; now. I have been claiming that I was too busy before. This is no longer an excuse.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/15616477-6432503650938598176?l=oldmoe.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://oldmoe.blogspot.com/feeds/6432503650938598176/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=15616477&amp;postID=6432503650938598176' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/15616477/posts/default/6432503650938598176'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/15616477/posts/default/6432503650938598176'/><link rel='alternate' type='text/html' href='http://oldmoe.blogspot.com/2008/04/computing-in-cloud-google-app-engines.html' title='Computing In The Cloud, Google App Engines'/><author><name>oldmoe</name><uri>http://www.blogger.com/profile/14341721253106429644</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='24' height='32' src='http://3.bp.blogspot.com/_XFDHsTWrvBY/SsFeIEFTI_I/AAAAAAAAADU/NphzXmrghkw/S220/oldmoe.png'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-15616477.post-6677938317209701417</id><published>2008-04-07T20:10:00.003+02:00</published><updated>2008-04-07T20:14:12.025+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='LBS'/><category scheme='http://www.blogger.com/atom/ns#' term='wenear'/><category scheme='http://www.blogger.com/atom/ns#' term='location based services'/><category scheme='http://www.blogger.com/atom/ns#' term='iphone'/><category scheme='http://www.blogger.com/atom/ns#' term='android'/><title type='text'>Preview of coming attractions, weNear</title><content type='html'>coming soon, to a handset near you.&lt;br /&gt;&lt;br /&gt;&lt;a href="http://www.wenear.com/"&gt;website&lt;/a&gt;, &lt;a href="http://www.wenear.com/wenear_demo20080404.wmv"&gt;perview&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;enjoy&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/15616477-6677938317209701417?l=oldmoe.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://oldmoe.blogspot.com/feeds/6677938317209701417/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=15616477&amp;postID=6677938317209701417' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/15616477/posts/default/6677938317209701417'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/15616477/posts/default/6677938317209701417'/><link rel='alternate' type='text/html' href='http://oldmoe.blogspot.com/2008/04/preview-of-coming-attractions-wenear.html' title='Preview of coming attractions, weNear'/><author><name>oldmoe</name><uri>http://www.blogger.com/profile/14341721253106429644</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='24' height='32' src='http://3.bp.blogspot.com/_XFDHsTWrvBY/SsFeIEFTI_I/AAAAAAAAADU/NphzXmrghkw/S220/oldmoe.png'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-15616477.post-4075377404464798870</id><published>2008-04-07T20:08:00.002+02:00</published><updated>2008-04-07T20:10:38.984+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='meowns'/><category scheme='http://www.blogger.com/atom/ns#' term='property'/><category scheme='http://www.blogger.com/atom/ns#' term='connection'/><category scheme='http://www.blogger.com/atom/ns#' term='interest'/><title type='text'>meOwns.com Site Tour</title><content type='html'>&lt;a href="http://www.meowns.com/site_tour"&gt;Here&lt;/a&gt; is a link to meOwns.com site tour. We are getting ready of an update in the coming weeks. Stay tuned.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/15616477-4075377404464798870?l=oldmoe.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://oldmoe.blogspot.com/feeds/4075377404464798870/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=15616477&amp;postID=4075377404464798870' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/15616477/posts/default/4075377404464798870'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/15616477/posts/default/4075377404464798870'/><link rel='alternate' type='text/html' href='http://oldmoe.blogspot.com/2008/04/meownscom-site-tour.html' title='meOwns.com Site Tour'/><author><name>oldmoe</name><uri>http://www.blogger.com/profile/14341721253106429644</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='24' height='32' src='http://3.bp.blogspot.com/_XFDHsTWrvBY/SsFeIEFTI_I/AAAAAAAAADU/NphzXmrghkw/S220/oldmoe.png'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-15616477.post-5180058717081004698</id><published>2008-04-07T18:10:00.002+02:00</published><updated>2008-04-07T18:27:40.976+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ruby'/><category scheme='http://www.blogger.com/atom/ns#' term='programming language'/><title type='text'>An introduction to Ruby</title><content type='html'>I just gave a presentation on Ruby. Uploaded to &lt;a href="http://www.scribd.com"&gt;scribd&lt;/a&gt; as usual (&lt;a href="http://www.scribd.com/doc/2462581/RubyProgrammersBestFriend"&gt;here&lt;/a&gt;)&lt;br /&gt;&lt;br /&gt;&lt;div style="display:none"&gt;&lt;script&gt;document.write('&lt;noscript&gt;');&lt;/script&gt;&lt;/div&gt; &lt;object codebase="http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=9,0,0,0" id="embedded_flash_2462581_1hw8mm_object" name="embedded_flash_2462581_1hw8mm_object" classid="clsid:d27cdb6e-ae6d-11cf-96b8-444553540000" align="middle" height="500" width="450"&gt; &lt;param name="movie" value="http://documents.scribd.com/ScribdViewer.swf?document_id=2462581&amp;access_key=key-1jd5cns5hvjzr8oyg8y0&amp;page=1&amp;version=1"&gt; &lt;param name="quality" value="high"&gt; &lt;param name="play" value="true"&gt; &lt;param name="loop" value="true"&gt; &lt;param name="scale" value="showall"&gt; &lt;param name="wmode" value="opaque"&gt; &lt;param name="devicefont" value="false"&gt; &lt;param name="bgcolor" value="#ffffff"&gt; &lt;param name="menu" value="true"&gt; &lt;param name="allowFullScreen" value="true"&gt; &lt;param name="allowScriptAccess" value="always"&gt; &lt;param name="salign" value=""&gt; &lt;embed src="http://documents.scribd.com/ScribdViewer.swf?document_id=2462581&amp;access_key=key-1jd5cns5hvjzr8oyg8y0&amp;page=1&amp;version=1" quality="high" pluginspage="http://www.macromedia.com/go/getflashplayer" play="true" loop="true" scale="showall" wmode="opaque" devicefont="false" bgcolor="#ffffff" name="embedded_flash_2462581_1hw8mm_object" menu="true" allowfullscreen="true" allowscriptaccess="always" salign="" type="application/x-shockwave-flash" align="middle" height="500" width="450"&gt;&lt;/embed&gt; &lt;/object&gt;&lt;div style="display:none"&gt; &lt;/noscript&gt; &lt;script type="text/javascript" src='http://www.scribd.com/javascripts/view.js'&gt;&lt;/script&gt;&lt;/div&gt;&lt;div id='embedded_flash_2462581_1hw8mm' style="width:100%;height:100%"&gt;&lt;span style="display:none"&gt;Read this doc on Scribd: &lt;a href="http://www.scribd.com/doc/2462581/RubyProgrammersBestFriend"&gt;Ruby-Programmers-Best-Friend&lt;/a&gt;&lt;/span&gt; &lt;/div&gt; &lt;div style="display:none"&gt;&lt;script type="text/javascript"&gt; var scribd_doc = new scribd.Document(2462581, 'key-1jd5cns5hvjzr8oyg8y0'); scribd_doc.addParam('height', 500); scribd_doc.addParam('width', 450); scribd_doc.addParam('page', 1); scribd_doc.addParam('mode', 'slideshow'); scribd_doc.write('embedded_flash_2462581_1hw8mm');&lt;/script&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/15616477-5180058717081004698?l=oldmoe.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://oldmoe.blogspot.com/feeds/5180058717081004698/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=15616477&amp;postID=5180058717081004698' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/15616477/posts/default/5180058717081004698'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/15616477/posts/default/5180058717081004698'/><link rel='alternate' type='text/html' href='http://oldmoe.blogspot.com/2008/04/introduction-to-ruby.html' title='An introduction to Ruby'/><author><name>oldmoe</name><uri>http://www.blogger.com/profile/14341721253106429644</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='24' height='32' src='http://3.bp.blogspot.com/_XFDHsTWrvBY/SsFeIEFTI_I/AAAAAAAAADU/NphzXmrghkw/S220/oldmoe.png'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-15616477.post-2403813707197639568</id><published>2008-03-23T16:47:00.004+02:00</published><updated>2008-03-25T22:53:52.477+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='javascript'/><category scheme='http://www.blogger.com/atom/ns#' term='GAMMA'/><category scheme='http://www.blogger.com/atom/ns#' term='CHAM'/><category scheme='http://www.blogger.com/atom/ns#' term='chemical programming'/><title type='text'>Introducing GammaScript</title><content type='html'>I have been experimenting with an implementation of the GAMMA formalism in Javascript. It is a VERY naive implementation that uses setTimeouts to mimick parallelism. Still it allows experimentation with the GAMMA and the chemical programming paradigm.&lt;br /&gt;&lt;br /&gt;The current implementation can be found &lt;a href="http://gammascript.googlecode.com"&gt;here&lt;/a&gt;. It provides a graphical representation of the multiset status (via the canvas element). A sample application is provided. An application that calculate the value for PI in a parallel way.&lt;br /&gt;&lt;br /&gt;For the uninitiated, GAMMA provides a chemical like reaction model. For example, imagine that you have the following set (multiset) of numbers:&lt;pre class="code"&gt;S -&gt; 1,2,1,5,4,3,7,7,3,5,9,8,3,2,2,1,5&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;A GAMMA program to compute the max of this set would look something like this:&lt;pre class="code"&gt;max(S):&lt;br /&gt;   C: x &gt;= y&lt;br /&gt;   R: x,y -&gt; x&lt;br /&gt;&lt;br /&gt;or the more concise:&lt;br /&gt;   x,y -&gt; x for every x &gt;= y&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;in GammaScript this can be written as:&lt;pre class="code"&gt;var max = {&lt;br /&gt;    condition : function(x,y){&lt;br /&gt;        return x &gt;=y;&lt;br /&gt;    },&lt;br /&gt;    reaction : function(x,y){&lt;br /&gt;        return x.consume(y);&lt;br /&gt;    }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;As you can see, none of these dictates how the set should be traversed. It is totally left to the implementation. The current Javascript implementation provides pseudo (fake) parallelism.&lt;br /&gt;&lt;br /&gt;I am considering a new implementation that utilizes Google Gears for true parallelism. And I need to refactor the view code from the core of the implementation.&lt;br /&gt;&lt;br /&gt;*UPDATE*&lt;br /&gt;&lt;br /&gt;To get the thing running you need to add data (comma separated) in the left box and click the (add) link. Then you should click the start button.&lt;br /&gt;&lt;br /&gt;For the PI calculation programs that are supplied, you need to add a single element (the tuple [0,1]) and click add then start.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/15616477-2403813707197639568?l=oldmoe.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://oldmoe.blogspot.com/feeds/2403813707197639568/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=15616477&amp;postID=2403813707197639568' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/15616477/posts/default/2403813707197639568'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/15616477/posts/default/2403813707197639568'/><link rel='alternate' type='text/html' href='http://oldmoe.blogspot.com/2008/03/introducing-gammascript.html' title='Introducing GammaScript'/><author><name>oldmoe</name><uri>http://www.blogger.com/profile/14341721253106429644</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='24' height='32' src='http://3.bp.blogspot.com/_XFDHsTWrvBY/SsFeIEFTI_I/AAAAAAAAADU/NphzXmrghkw/S220/oldmoe.png'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-15616477.post-3345582705423692698</id><published>2008-03-18T23:01:00.004+02:00</published><updated>2008-03-19T10:19:50.610+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='meowns'/><category scheme='http://www.blogger.com/atom/ns#' term='espace'/><title type='text'>meOwns, a new way to express yourself</title><content type='html'>Have you noticed the widget to the left of this blog? This is a list of the items I have in meOwns, the new way to express yourself via your belongings. This is a Ruby on Rails application that is still in early beta. Registration is invitation only currently. If you would like an invitation then please send me an email with "meowns invitation required" in the subject to oldmoe at (g)mail dot com&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/15616477-3345582705423692698?l=oldmoe.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://oldmoe.blogspot.com/feeds/3345582705423692698/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=15616477&amp;postID=3345582705423692698' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/15616477/posts/default/3345582705423692698'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/15616477/posts/default/3345582705423692698'/><link rel='alternate' type='text/html' href='http://oldmoe.blogspot.com/2008/03/meowns-new-way-to-express-yourself.html' title='meOwns, a new way to express yourself'/><author><name>oldmoe</name><uri>http://www.blogger.com/profile/14341721253106429644</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='24' height='32' src='http://3.bp.blogspot.com/_XFDHsTWrvBY/SsFeIEFTI_I/AAAAAAAAADU/NphzXmrghkw/S220/oldmoe.png'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-15616477.post-5604141267430952558</id><published>2008-03-18T17:27:00.001+02:00</published><updated>2008-03-18T17:29:37.092+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='javascript'/><category scheme='http://www.blogger.com/atom/ns#' term='dom'/><category scheme='http://www.blogger.com/atom/ns#' term='prototype'/><title type='text'>Another JavaScript session</title><content type='html'>I have uploaded my second session on JavaScript to scribd as well. Please find it &lt;a href="http://www.scribd.com/doc/2304895/javascriptinpractice"&gt;here&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/15616477-5604141267430952558?l=oldmoe.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://oldmoe.blogspot.com/feeds/5604141267430952558/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=15616477&amp;postID=5604141267430952558' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/15616477/posts/default/5604141267430952558'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/15616477/posts/default/5604141267430952558'/><link rel='alternate' type='text/html' href='http://oldmoe.blogspot.com/2008/03/another-javascript-session.html' title='Another JavaScript session'/><author><name>oldmoe</name><uri>http://www.blogger.com/profile/14341721253106429644</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='24' height='32' src='http://3.bp.blogspot.com/_XFDHsTWrvBY/SsFeIEFTI_I/AAAAAAAAADU/NphzXmrghkw/S220/oldmoe.png'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-15616477.post-4366194374974965104</id><published>2008-03-12T10:33:00.003+02:00</published><updated>2008-03-12T10:51:52.315+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='javascript'/><title type='text'>My Latest Javascript Session</title><content type='html'>I have uploaded the slides from my latest eSpace open session (this one about Javascript Internals) to scribd &lt;a href="http://www.scribd.com/doc/2263478/eSpace-Javascript-Internals-Session"&gt;here&lt;/a&gt;. Please forgive the bad formatting. I hope this is useful (too many thanks for &lt;a href="http://javascript.crockford.com/"&gt;Crockford's&lt;/a&gt; writings, I would have been lost without those)&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/15616477-4366194374974965104?l=oldmoe.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://oldmoe.blogspot.com/feeds/4366194374974965104/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=15616477&amp;postID=4366194374974965104' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/15616477/posts/default/4366194374974965104'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/15616477/posts/default/4366194374974965104'/><link rel='alternate' type='text/html' href='http://oldmoe.blogspot.com/2008/03/my-latest-javascript-sessions.html' title='My Latest Javascript Session'/><author><name>oldmoe</name><uri>http://www.blogger.com/profile/14341721253106429644</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='24' height='32' src='http://3.bp.blogspot.com/_XFDHsTWrvBY/SsFeIEFTI_I/AAAAAAAAADU/NphzXmrghkw/S220/oldmoe.png'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-15616477.post-7562981583034044613</id><published>2008-03-11T03:13:00.003+02:00</published><updated>2008-03-11T03:16:50.747+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='java'/><category scheme='http://www.blogger.com/atom/ns#' term='sun'/><category scheme='http://www.blogger.com/atom/ns#' term='iphone'/><category scheme='http://www.blogger.com/atom/ns#' term='jvm'/><category scheme='http://www.blogger.com/atom/ns#' term='apple'/><title type='text'>Sun is bringing Java to the iphone</title><content type='html'>&lt;a href="http://www.sun.com/aboutsun/media/gallery/index.xml?p=1&amp;amp;s=1"&gt;Here&lt;/a&gt; you can watch a video by a Sun engineer talking about the plans to port the JVM to the iPhone. I believe this can increase the available applications for the iPhone tenfold overnight. The challenge would be to make full use of the iPhone features like the multi touch interface and the accelerometer.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/15616477-7562981583034044613?l=oldmoe.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://oldmoe.blogspot.com/feeds/7562981583034044613/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=15616477&amp;postID=7562981583034044613' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/15616477/posts/default/7562981583034044613'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/15616477/posts/default/7562981583034044613'/><link rel='alternate' type='text/html' href='http://oldmoe.blogspot.com/2008/03/sun-is-bringing-java-to-iphone.html' title='Sun is bringing Java to the iphone'/><author><name>oldmoe</name><uri>http://www.blogger.com/profile/14341721253106429644</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='24' height='32' src='http://3.bp.blogspot.com/_XFDHsTWrvBY/SsFeIEFTI_I/AAAAAAAAADU/NphzXmrghkw/S220/oldmoe.png'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-15616477.post-1813762504197425342</id><published>2008-03-10T12:22:00.004+02:00</published><updated>2008-04-18T01:34:08.763+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ruby'/><category scheme='http://www.blogger.com/atom/ns#' term='cocoa'/><category scheme='http://www.blogger.com/atom/ns#' term='iphone'/><category scheme='http://www.blogger.com/atom/ns#' term='macruby'/><category scheme='http://www.blogger.com/atom/ns#' term='apple'/><title type='text'>The iPhone SDK, Objective C and Ruby</title><content type='html'>I bet many of you have already seen the &lt;a href="http://www.apple.com/quicktime/qtv/keynote/"&gt;iPhone SDK&lt;/a&gt; presentation already. I believe that many will be tempted by the platform. There are barriers for entry though. You have to do all development on a Mac, you have to have an iPhone (emulators are not for production testing), you have to be a partner in the Apple developer program and you have to write code in &lt;a href="http://www.google.com.eg/url?sa=t&amp;amp;ct=res&amp;amp;cd=2&amp;amp;url=http%3A%2F%2Fdeveloper.apple.com%2Fdocumentation%2FCocoa%2FConceptual%2FObjectiveC%2F&amp;amp;ei=YQzVR8enE4a2wQH5xY2TAw&amp;amp;usg=AFQjCNFR-4nUahyAELKcHUZDWKv3liXPEA&amp;amp;sig2=4ZWrKedLx8YIWP1l6LEv4w"&gt;Objective-C&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;Objective what? Objective-C is a language that sits on top of &lt;strike&gt;C++&lt;/strike&gt; C. It has some dynamic features and the syntax will instantly remind you of&lt;br /&gt;SmallTalk. Actually I shrug to the idea of writing code that looks like SmallTalk with C++ like constructs. Sounds like sweet and sour Chinese food&lt;br /&gt;to me (it reminds me of the ugly "new" operator that is off place in Javascript). But Apple has done a great job with Cocoa (the MacOS X interface toolkit) and CocoaTouch (the one designed for touch interfaces, iPhone, iPodTouch and soon iTablet). The API is very elegant and clean (I still yearn for the BeOS API though, will always do).&lt;br /&gt;&lt;br /&gt;OK, what does this have to do with Ruby? Well, a very interesting project popped up in the Ruby core list recently. Apparently, Apple is integrating the whole of Ruby1.9 into the Objective-C runtime and it is calling the package &lt;a href="http://trac.macosforge.org/projects/ruby/wiki/MacRuby"&gt;MacRuby&lt;/a&gt;. Ruby code will have access to all Cocoa interfaces and vice-versa. This project is an open source one but is being spearheaded by Apple. All those actively contributing right now are Apple engineers. They are trying to expand the interfaces to their APIs and thus cater for more developers.&lt;br /&gt;&lt;br /&gt;Will we ever see an iPhone shipping with MacRuby? Will we be able to write Cocoa Touch interfaces in Ruby? Imagine that, I will no longer be ashamed that &lt;a href="http://hackety.org/press/nks.html"&gt;I don't know shoes!&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/15616477-1813762504197425342?l=oldmoe.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://oldmoe.blogspot.com/feeds/1813762504197425342/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=15616477&amp;postID=1813762504197425342' title='8 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/15616477/posts/default/1813762504197425342'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/15616477/posts/default/1813762504197425342'/><link rel='alternate' type='text/html' href='http://oldmoe.blogspot.com/2008/03/iphone-sdk-objective-c-and-ruby.html' title='The iPhone SDK, Objective C and Ruby'/><author><name>oldmoe</name><uri>http://www.blogger.com/profile/14341721253106429644</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='24' height='32' src='http://3.bp.blogspot.com/_XFDHsTWrvBY/SsFeIEFTI_I/AAAAAAAAADU/NphzXmrghkw/S220/oldmoe.png'/></author><thr:total>8</thr:total></entry><entry><id>tag:blogger.com,1999:blog-15616477.post-2188528016149109796</id><published>2008-02-29T18:27:00.018+02:00</published><updated>2008-03-18T17:41:44.537+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='javascript'/><category scheme='http://www.blogger.com/atom/ns#' term='generators'/><category scheme='http://www.blogger.com/atom/ns#' term='generator expressions'/><title type='text'>Generators &amp; Generator Expressions in Javascript 1.8</title><content type='html'>Javascript has been going through a step by step evolution for a while now. Many people are unaware that new Javascript features are being added to almost every new firefox major release. And while most references are still quoting the 1.5 release, 1.6, 1.7 and even 1.8 are out now and can be used today.&lt;br /&gt;&lt;br /&gt;One of those new features (an exciting one) is the introduction of generators. In layman's terms generators are: pause and resume for your methods. How is that? A generator is simply a normal method, but one that has the ability to yield back control to the caller while maintaining its state for future runs. This is not a very accurate description as it will not yield back to the method caller but actually to those who call next() on it. Confused already? let's use an example to make things clear.&lt;br /&gt;&lt;pre class="code"&gt;//fibnacci example, stolen right from the mozilla docs&lt;br /&gt;function fib() {&lt;br /&gt; var i = 0, j = 1;&lt;br /&gt; while (true) {&lt;br /&gt;   yield i;&lt;br /&gt;   var t = i;&lt;br /&gt;   i = j;&lt;br /&gt;   j += t;&lt;br /&gt; }&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;var g = fib();&lt;br /&gt;for (var i = 0; i &amp;lt; 10; i++) {&lt;br /&gt; document.write(g.next() + " ");&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;which results in:&lt;br /&gt;1 1 2 3 5 8 13 21 34 55&lt;/pre&gt;&lt;br /&gt;Before you get lost in the above code, here is a quick description of what happens:&lt;ol&gt;&lt;br /&gt;&lt;li&gt;Javascript knows the above fib function is a generator (becuase it encloses the keyword yield)&lt;/li&gt;&lt;br /&gt;&lt;li&gt;When you call a generator function, any parameters you send in the call are bound&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Rather than executing the method body it returns a generator iterator, one which you can call some of the iterator methods on (like next, send and close)&lt;/li&gt;&lt;br /&gt;&lt;li&gt;The loop outside the function is run and g.next() gets called&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Whenever g.next() is called the fib function body gets executed, until it reaches the yield keword, at this point it returns control back to the caller of next() while its state remains intact.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;The result of the expression following the yield is returned to the caller of next (this is what is being generated by the generator)&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Subsequent calls to next() will cause the function to continue right after the yield keyword and yields control back again when it re-encounters it.&lt;/li&gt;&lt;/ol&gt;&lt;br /&gt;You can think of generators as a interruptable transformations. They are usually used to generate a transformation of some iteratable data while giving the callers control of when (or if) they are allowed to move forward with this generation.&lt;br /&gt;&lt;br /&gt;Building on this, a new feature was introduced to make your life even easier. Generator expressions; Instead of having to write a generator functions it is possible to describe your transformation as a short hand in-place expression.&lt;br /&gt;&lt;br /&gt;Consider the following generator function (also stolen from Mozilla but modified this time)&lt;pre class="code"&gt;function square(obj) {&lt;br /&gt;for each ( var i in obj )&lt;br /&gt;yield i*i;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;var someNumbers = {a:1,b:2,c:3,d:4,e:5,f:6};&lt;br /&gt;&lt;br /&gt;var iterator = square(someNumbers);&lt;br /&gt;try {&lt;br /&gt;while (true) {&lt;br /&gt;document.write(iterator.next() + " ");&lt;br /&gt;}&lt;br /&gt;} catch (error if error instanceof StopIteration) {&lt;br /&gt;//we are done&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;this results in:&lt;br /&gt;1 4 9 16 25 36&lt;/pre&gt;&lt;br /&gt;This square function will iterate over the hash values (using the for each..in statement) and will generate the square of the current hash value and yield control back to the caller.&lt;br /&gt;&lt;br /&gt;In this case the generator function is merely doing a very simple transformation. Thus we can easily replace it by a generator expression.&lt;br /&gt;&lt;br /&gt;Like this example (for the third time, stolen and modified from mozilla.org):&lt;br /&gt;&lt;pre class="code"&gt;var someNumbers = {a:1,b:2,c:3,d:4,e:5,f:6};&lt;br /&gt;&lt;br /&gt;var iterator = (i * i for each (i in someNumbers));&lt;br /&gt;try {&lt;br /&gt;while (true) {&lt;br /&gt;document.write(iterator.next() + " ");&lt;br /&gt;}&lt;br /&gt;} catch (error if error instanceof StopIteration) {&lt;br /&gt;//we are done&lt;br /&gt;}&lt;/pre&gt;&lt;br /&gt;This line :&lt;br /&gt;&lt;pre class="code"&gt;var iterator = (i * i for each (i in someNumbers));&lt;/pre&gt;Is what we call generator expressions. This is exactly like the above generator function. It returns an iterator (the assignment) that when its next method is called it does a transformation (the expression i * i) in some sort of a loop (the for each..in statement) and returns control back to the caller after each iteration (implicitly yielding the expression result).&lt;br /&gt;&lt;br /&gt;And there is more to generator expressions. They actually have a neat way of yielding only under some condition and the Javascript 1.8 developers (thanks Brendan et. al) came up with a cool Ruby like conditioning.&lt;br /&gt;&lt;br /&gt;Say you only wanted to get the squares of the even numbers in the list, the above generator expression will be rewritten as:&lt;br /&gt;&lt;pre class="code"&gt;var iterator =&lt;br /&gt;(i * i for each (i in someNumbers) if (i%2==0));&lt;/pre&gt;sweet!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/15616477-2188528016149109796?l=oldmoe.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://oldmoe.blogspot.com/feeds/2188528016149109796/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=15616477&amp;postID=2188528016149109796' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/15616477/posts/default/2188528016149109796'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/15616477/posts/default/2188528016149109796'/><link rel='alternate' type='text/html' href='http://oldmoe.blogspot.com/2008/02/generators-generator-expressions-in.html' title='Generators &amp; Generator Expressions in Javascript 1.8'/><author><name>oldmoe</name><uri>http://www.blogger.com/profile/14341721253106429644</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='24' height='32' src='http://3.bp.blogspot.com/_XFDHsTWrvBY/SsFeIEFTI_I/AAAAAAAAADU/NphzXmrghkw/S220/oldmoe.png'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-15616477.post-3145947311841254793</id><published>2008-02-17T19:04:00.006+02:00</published><updated>2008-03-01T15:12:20.154+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='javascript. Aspect oriented programming'/><category scheme='http://www.blogger.com/atom/ns#' term='AOP'/><title type='text'>Aspect Oriented Javascript, revisited</title><content type='html'>Tonight I was experimenting a bit with a minimalist implementation of advising in Aspect Oriented Javascript. I Implemented the three basic advices (before, around and after). For this naive implementation I slammed the functions in the Object's prototype. This way I had access to them in all objects in the system.&lt;br /&gt;&lt;br /&gt;Here's how to use them:&lt;br /&gt;&lt;pre class="code"&gt;Dog.before('bark',function(){alert('going to bark')});&lt;br /&gt;Dog.after('bark',function(){alert('done barking')});&lt;br /&gt;User.prototype.before('login',function(){alert('logging in!')}&lt;/pre&gt;&lt;br /&gt;The actual code written is very small (20 lines, less if you discount lines taken by braces)&lt;br /&gt;&lt;pre class="code"&gt;Object.prototype.before = function(func, advice){&lt;br /&gt;var oldfunc = this[func];&lt;br /&gt;this[func] = function(){&lt;br /&gt;   advice.apply(this,arguments);&lt;br /&gt;   oldfunc.apply(this,arguments);&lt;br /&gt;}&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;Object.prototype.around = function(func, advice){&lt;br /&gt;   var oldfunc = this[func];&lt;br /&gt;   this[func] = function(){&lt;br /&gt;      var myargs = [oldfunc];&lt;br /&gt;      for(var i=0; i &lt; arguments.length;i++){&lt;br /&gt;         myargs.push(arguments[i])&lt;br /&gt;      }&lt;br /&gt;      advice.apply(this,myargs);&lt;br /&gt;   }&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;Object.prototype.after = function(func, advice){&lt;br /&gt;var oldfunc = this[func];&lt;br /&gt;this[func] = function(){&lt;br /&gt;   oldfunc.apply(this,arguments);&lt;br /&gt;   advice.apply(this,arguments);&lt;br /&gt;}&lt;br /&gt;}&lt;/pre&gt;This way you can add any sorts of filters to your JavaScript methods. Enhancing on this is to be able to remove those filters once added and to add a filter to all functions of an object (recursively) at once.&lt;br /&gt;&lt;br /&gt;We also need some fail safety against users trying to advice non functions or even undefined properties.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/15616477-3145947311841254793?l=oldmoe.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://oldmoe.blogspot.com/feeds/3145947311841254793/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=15616477&amp;postID=3145947311841254793' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/15616477/posts/default/3145947311841254793'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/15616477/posts/default/3145947311841254793'/><link rel='alternate' type='text/html' href='http://oldmoe.blogspot.com/2008/02/aspect-oriented-javascript-revisited.html' title='Aspect Oriented Javascript, revisited'/><author><name>oldmoe</name><uri>http://www.blogger.com/profile/14341721253106429644</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='24' height='32' src='http://3.bp.blogspot.com/_XFDHsTWrvBY/SsFeIEFTI_I/AAAAAAAAADU/NphzXmrghkw/S220/oldmoe.png'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-15616477.post-380230690484281467</id><published>2008-02-09T20:03:00.000+02:00</published><updated>2008-02-09T20:10:23.773+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='multicore'/><category scheme='http://www.blogger.com/atom/ns#' term='java'/><category scheme='http://www.blogger.com/atom/ns#' term='threads'/><category scheme='http://www.blogger.com/atom/ns#' term='tomcat'/><title type='text'>Tom, the cat we all love</title><content type='html'>&lt;p&gt;You have your Java web app hot from the oven. Looking around you see this fat cat (we call it Tom, Tomcat) sitting around the corner. You hand it the app hoping that it will do a good job of serving it. &lt;/p&gt;&lt;p&gt;But how does Tomcat manage to serve pages from our Java web app? Simple, Tomcat listens on a certain port (default is 8080) and accepts requests, spawning a thread for each request to be handled when it reaches the maximum number of allowed threads (the maxthreadcount parameter and it defaults to 150) it queues the incoming requests until a thread is free so it can hand over a request to it. When threads get idle, Tomcat will start killing them till it reaches the max spare threads count (maxsparethreads, defaults to 75) &lt;/p&gt;&lt;p&gt;Sounds good. That means that a default Tomcat instance can handle up to 150 requests in parallel. Meaning it will spawn up to 150 threads. Which is a good thing. The more threads, the more parallel processing we can do. &lt;/p&gt;&lt;p&gt;WRONG! Because of limits imposed by your combination of hardware and software the above naive statements are not true. Manly due to: &lt;/p&gt;&lt;p&gt;Hardware: You can have only have n running threads, where n is the number of your cpu cores. Other threads are waiting until the scheduler preempts the current ones and permits them to run. &lt;/p&gt;&lt;p&gt;VM: JVM uses native threads (it used green threads in the past) which means that creating threads is not a very small process. In reality the cost associated with it is a bit high. &lt;/p&gt;&lt;p&gt;OS: Context switching is usually a heavy operation as well. When you have many thread more than your cores you will be dealing with much of those operations. &lt;/p&gt;&lt;p&gt;Here is a scenario: You use Tomcat with its default settings on a Quad core machine to serve your web applications. Your website is attacked and get sustained 150+ concurrent requests. Thus Tomcat spawns his max thread limit of threads (150) and attempts to serve all the coming requests. &lt;/p&gt;&lt;p&gt;Since you only have 4 cores. Only 4 threads can be active at a time, neglecting the Tomcat process and any other system processes then we have our 4 cores being fought for by 150 threads. Many threads will be waiting for I/O (hopefully hardware accelerated) most of the time. Thus a single core will be able to handle more than 1 thread depending on the speed of that core and the amount of time the threads are waiting. &lt;/p&gt;&lt;p&gt;I would say that a single core can cope with 5 to 10 threads (processing web requests) with negligible context switching penalty. Having more than that will result in too many context switches for threads congested on the core. With the default Tomcat settings, a cpu core will be handling 37 threads on average. This will lead to poor performance under heavy load and will slow down the application rather than help it run faster &lt;/p&gt;&lt;p&gt;So, what should we do with the maxthreads setting? &lt;/p&gt;&lt;ol&gt;&lt;li&gt;Start from an informed position, knowing how much cores in your system you can just throw a suitable amount by following my (rather simplistic) approximation above of 5 to 10 (it is a guesstimate and it may turn out very bad for your specific case so don't say I didn't warn you) or ..&lt;br /&gt;&lt;br /&gt;&lt;/li&gt;&lt;li&gt;Use a benchmarking tool, like Apache bench (from www.apache.org) and start testing your typical workload on your production machine with 1 thread per core setting. Record you requests/second and then redo the tests with more threads added. Stop adding threads when you can't get better performance. At this point, if you are not satisfied with your performance you can either:&lt;br /&gt;&lt;br /&gt;&lt;/li&gt;&lt;ol&gt;&lt;li&gt;Get faster hardware&lt;/li&gt;&lt;li&gt;Optimize your application and redo the benchmarking again&lt;/li&gt;&lt;li&gt;Both of the above&lt;br /&gt;&lt;/li&gt;&lt;/ol&gt;&lt;/ol&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/15616477-380230690484281467?l=oldmoe.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://oldmoe.blogspot.com/feeds/380230690484281467/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=15616477&amp;postID=380230690484281467' title='10 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/15616477/posts/default/380230690484281467'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/15616477/posts/default/380230690484281467'/><link rel='alternate' type='text/html' href='http://oldmoe.blogspot.com/2008/02/tom-cat-we-all-love.html' title='Tom, the cat we all love'/><author><name>oldmoe</name><uri>http://www.blogger.com/profile/14341721253106429644</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='24' height='32' src='http://3.bp.blogspot.com/_XFDHsTWrvBY/SsFeIEFTI_I/AAAAAAAAADU/NphzXmrghkw/S220/oldmoe.png'/></author><thr:total>10</thr:total></entry><entry><id>tag:blogger.com,1999:blog-15616477.post-5171993701954591503</id><published>2008-02-09T18:59:00.000+02:00</published><updated>2008-02-09T19:37:56.804+02:00</updated><title type='text'>Mixing Asset and Page Caching In A Multiserver Setup</title><content type='html'>Generally you would set an expires header for your assets so that your clients would retrieve them from their local caches and not bother your servers for a good amount of time.&lt;br /&gt;&lt;br /&gt;Setting this for JavaScript and Image files causes your site to feel much faster on pages with many images and JavaScript files. But what do we do when any of those static resource change? For Images I usually change the filename with the new version and change the reference. For JavaScript files a common practice is to append some version information to the file name, usually a time stamp of the last modification date. This way when a file changes the reference to it changes as well and clients no longer use the old the cached resource and they will request the new one.&lt;br /&gt;&lt;br /&gt;The simplistic time stamping approach works fine on a single server setup. When you add more servers you will find that you will need a more distributed safe way other than time stamping. One such way is to use your repository's revision file number. As long as you consistently deploy to all the machines you will have the same revision number on all the servers. In that case your files can look like this, application_235.js and common_42.js&lt;br /&gt;&lt;br /&gt;Another issue arises with caching. If you are caching your entire responses (in memcached for example) and it references a Javascript file which happens to change its version then the response will keep asking for the older version rather than the new one. This can easily be solved by appending the application revision number to the cache key, i.e. "/users/1235/profile.html_1269". This way whenever the revision is upped your application will look for the latest ones in the caches and the older ones will auto expire (if you are using a cache store with auto expire capability like memcached)&lt;br /&gt;&lt;br /&gt;Now, just relax and watch your web server serving static files blazingly fast while you are assured that everything is in sync.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/15616477-5171993701954591503?l=oldmoe.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://oldmoe.blogspot.com/feeds/5171993701954591503/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=15616477&amp;postID=5171993701954591503' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/15616477/posts/default/5171993701954591503'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/15616477/posts/default/5171993701954591503'/><link rel='alternate' type='text/html' href='http://oldmoe.blogspot.com/2008/02/mixing-asset-and-page-caching-in.html' title='Mixing Asset and Page Caching In A Multiserver Setup'/><author><name>oldmoe</name><uri>http://www.blogger.com/profile/14341721253106429644</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='24' height='32' src='http://3.bp.blogspot.com/_XFDHsTWrvBY/SsFeIEFTI_I/AAAAAAAAADU/NphzXmrghkw/S220/oldmoe.png'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-15616477.post-6891879259168268730</id><published>2008-02-08T02:19:00.001+02:00</published><updated>2008-02-18T13:19:05.551+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ruby'/><category scheme='http://www.blogger.com/atom/ns#' term='rails'/><category scheme='http://www.blogger.com/atom/ns#' term='mongrel'/><category scheme='http://www.blogger.com/atom/ns#' term='groovy'/><title type='text'>Ponder This</title><content type='html'>I happened to stumble on this (kinda old) &lt;a href="http://docs.codehaus.org/display/GRAILS/Grails+vs+Rails+Benchmark"&gt;page&lt;/a&gt; where the author is comparing Rails to Groovy performance. I was reading his comments, he was generally concluding that Groovy is faster than Rails. I didn't look at the graphs until I saw his comment that 10 Mongrels behind pound was much slower than a single Mongrel!!. I double checked the graph and this was the case! The 10 Mongrel setup is much slower in all the cases than a 1 Mongrel instance.&lt;br /&gt;&lt;br /&gt;I was wondering where is the catch. Rereading the article I spotted it with little effort. Here's a ponder-this for those who read this blog (both of you), what did the guy do to screw up the performance figures that bad? If you get it, please add a comment with your answer.&lt;br /&gt;&lt;br /&gt;Given his numbers, an appropriate Rails setup will make Rails suddenly faster (I don't mean any magic tricks, just fixing his fatal mistake). But the difference will not be that big anyway.&lt;br /&gt;&lt;br /&gt;Update: seemingly no one discovered (or cared to discover?) the mistake, so here it goes. The lad used 10 Mongrels on a 1GB Ram machine that also ran mysql, OS X and whatever else he got running, he simply ran out of memory and started swapping. The numbers for the 10 Mongrel setup were including the disk swapping penalty. Couldn't he just listen to his drive or see a blinking led?&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/15616477-6891879259168268730?l=oldmoe.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://oldmoe.blogspot.com/feeds/6891879259168268730/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=15616477&amp;postID=6891879259168268730' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/15616477/posts/default/6891879259168268730'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/15616477/posts/default/6891879259168268730'/><link rel='alternate' type='text/html' href='http://oldmoe.blogspot.com/2008/02/can-you-tell.html' title='Ponder This'/><author><name>oldmoe</name><uri>http://www.blogger.com/profile/14341721253106429644</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='24' height='32' src='http://3.bp.blogspot.com/_XFDHsTWrvBY/SsFeIEFTI_I/AAAAAAAAADU/NphzXmrghkw/S220/oldmoe.png'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-15616477.post-7083388278904334677</id><published>2008-01-26T23:43:00.000+02:00</published><updated>2008-01-26T23:56:35.158+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ruby'/><category scheme='http://www.blogger.com/atom/ns#' term='performance'/><category scheme='http://www.blogger.com/atom/ns#' term='rails'/><category scheme='http://www.blogger.com/atom/ns#' term='nginx'/><category scheme='http://www.blogger.com/atom/ns#' term='mongrel'/><category scheme='http://www.blogger.com/atom/ns#' term='thin'/><title type='text'>Thin is getting thinner by the day</title><content type='html'>It seems that &lt;a href="http://macournoyer.wordpress.com/2008/01/26/get-intimate-with-your-load-balancer-tonight/"&gt;&lt;span class="commentauthor"&gt;&lt;/span&gt;macournoyer&lt;/a&gt; is in a frenzy of releases for Thin!. He just released the 0.6.1 sweet cheesecake release (what's up with the release names? I'm trying to control my diet here!). The new release uses less memory than Mongrel which is a very good thing IMHO. It also uses the same config files used for Mongrel Cluster. The big bang is the ability to use UNIX sockets to connect to the load balancer instead of TCP connections. This decreases the overhead of nginx sending requests and getting responses back from the upstream servers.&lt;br /&gt;&lt;br /&gt;These are good news, though I will have to test if this will translate to noticeable performance increase in a full fledged Ruby on Rails application&lt;br /&gt;&lt;br /&gt;I will redo my tests with this new setup and come back with more numbers&lt;br /&gt;&lt;br /&gt;Stay tuned&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/15616477-7083388278904334677?l=oldmoe.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://oldmoe.blogspot.com/feeds/7083388278904334677/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=15616477&amp;postID=7083388278904334677' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/15616477/posts/default/7083388278904334677'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/15616477/posts/default/7083388278904334677'/><link rel='alternate' type='text/html' href='http://oldmoe.blogspot.com/2008/01/thin-is-getting-thinner-by-day.html' title='Thin is getting thinner by the day'/><author><name>oldmoe</name><uri>http://www.blogger.com/profile/14341721253106429644</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='24' height='32' src='http://3.bp.blogspot.com/_XFDHsTWrvBY/SsFeIEFTI_I/AAAAAAAAADU/NphzXmrghkw/S220/oldmoe.png'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-15616477.post-7818442764471290281</id><published>2008-01-26T02:00:00.000+02:00</published><updated>2008-01-26T02:26:23.854+02:00</updated><title type='text'>Coding around the globe</title><content type='html'>Yesterday I was doing lots of testing and I had to install and reinstall many components between tests. &lt;span style="font-style: italic;"&gt;apt-get install&lt;/span&gt; and &lt;span style="font-style: italic;"&gt;gem install&lt;/span&gt; were next only to &lt;span style="font-style: italic;"&gt;ab&lt;/span&gt; and &lt;span style="font-style: italic;"&gt;httperf&lt;/span&gt; in command usage frequency yesterday. I was setting up the stack several times and when I realized the following:&lt;br /&gt;&lt;br /&gt;I was using the nginx web server (authored by a &lt;a href="http://sysoev.ru/en/"&gt;Russian&lt;/a&gt;)&lt;br /&gt;I tested against Mongrel application server (authored by an &lt;a href="http://www.zedshaw.com/"&gt;American&lt;/a&gt;) and Thin application server (authored by a &lt;a href="http://www.macournoyer.com/"&gt;Canadian&lt;/a&gt;)&lt;br /&gt;I built the test application using the Rails application framework (authored by a &lt;a href="http://www.loudthinking.com"&gt;Danish&lt;/a&gt;)&lt;br /&gt;I was programming this app in the Ruby language (authored by a &lt;a href="http://www.rubyist.net/%7Ematz/"&gt;Japanese&lt;/a&gt;)&lt;br /&gt;All work was done on Linux OS (originally authored by a &lt;a href="http://www.cs.helsinki.fi/u/torvalds/"&gt;Finnish&lt;/a&gt;)&lt;br /&gt;I was doing all this work in &lt;a href="http://www.espace.com.eg"&gt;Egypt&lt;/a&gt; for a service partner located in UAE&lt;br /&gt;&lt;br /&gt;Not to mention the authors of the numerous tools I worked with in that particular day. I suddenly felt connected with all of them. I was thankful to live in a time were people from every where could contribute to a particular problem. It is amazing to see the culmination of efforts of many who are seemingly separated but in the end they seem all to be working in an unintended harmony.  I felt that our little service is composed of bits and pieces from all over the globe. I only knitted them together with amazing results.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/15616477-7818442764471290281?l=oldmoe.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://oldmoe.blogspot.com/feeds/7818442764471290281/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=15616477&amp;postID=7818442764471290281' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/15616477/posts/default/7818442764471290281'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/15616477/posts/default/7818442764471290281'/><link rel='alternate' type='text/html' href='http://oldmoe.blogspot.com/2008/01/coding-around-globe.html' title='Coding around the globe'/><author><name>oldmoe</name><uri>http://www.blogger.com/profile/14341721253106429644</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='24' height='32' src='http://3.bp.blogspot.com/_XFDHsTWrvBY/SsFeIEFTI_I/AAAAAAAAADU/NphzXmrghkw/S220/oldmoe.png'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-15616477.post-4391060880442221259</id><published>2008-01-26T00:43:00.003+02:00</published><updated>2008-03-02T01:09:56.361+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ruby'/><category scheme='http://www.blogger.com/atom/ns#' term='rails'/><category scheme='http://www.blogger.com/atom/ns#' term='nginx'/><category scheme='http://www.blogger.com/atom/ns#' term='mongrel'/><category scheme='http://www.blogger.com/atom/ns#' term='thin'/><title type='text'>The numbers are in, Thin is (sort of) faster than Mongrel for production apps.</title><content type='html'>I have been playing with the Thin + nginx combo for a while now. I did a lot of stress testing using an actual application that is ready to go for production soon. I played with both Thin and Mongrel to see if there is a big difference.&lt;br /&gt;&lt;br /&gt;I need not mention that I didn't bother testing Apache as a proxy balancer instead of nginx, I am getting an earth shaking 7900 req/s for static requests using nginx on an application serving unit.&lt;br /&gt;&lt;br /&gt;Our application serving units are xen virtual servers with a quad core processor each.  It runs The nginx web server and the web application cluster (Mongrel currently, but may be Thin too). The cluster runs 10 Mongrels and 4 nginx workers. Any application can scale its front end by adding more of those application serving units.&lt;br /&gt;&lt;br /&gt;While testing I was surprised that after a certain test Thin was giving me results that were slower than Mongrel. Repeating the tests or letting the system load cool down didn't help. What I found was that I hit the memory limit and the system started swapping. I shut down one Thin server and suddenly they started to outperform (albeit by a small margin) the Mongrel cluster again.&lt;br /&gt;&lt;br /&gt;I tested against three of the very heavy pages. The results were the average of three runs for each page in the specified concurrency/connections pair&lt;br /&gt;&lt;br /&gt;So  here are the numbers (using Apache Bench):      &lt;br /&gt;&lt;span style="font-family:courier new;"&gt;&lt;span style="font-size:100%;"&gt;&lt;span style="font-weight: bold; color: rgb(0, 0, 102);"&gt;&lt;pre class="code"&gt;            (Concurrency/Connections) &lt;br /&gt;            100/1000    200/1000    200/10000 &lt;br /&gt;Thin        104.3req/s  115.7req/s  123.1req/s&lt;br /&gt;Mongrel     100.6req/s  113.2req/s  121.6req/s&lt;br /&gt;&lt;/pre&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;The figures speak for themselves; while Thin is constantly faster than Mongrel, the difference is negligible. I am assuming that this is due to the fact that most of the time is spent in processing the Rails stack and doing IO with memcached then sending the actual repsonse back. The raw differences between Thin and Mongrel are dwarfed by the time spent in Rails. You will see a good advantage for Thin when you are doing very small requests that do little processing and send small responses. While this is not the case for this particular test, it is typical in many Ajax intensive applications. And since this application is full of Ajax requests, I believe that we might opt for Thin at the end.&lt;br /&gt;&lt;br /&gt;I have to say that I was very happy with the results so far. Mongrel and Thin are both robust and nginx is a true gem ;). The application is expected to generate lots of traffic and I am confident that scaling would only be a matter of adding more boxes. My next challenge is to get more performance out of those boxes. Which is a possibility since &lt;a href="http://blog.evanweaver.com/articles/2008/01/21/b-the-fastest-u-can-b-memcached"&gt;Evan&lt;/a&gt; is working on a much better memcached client for Ruby. Knowing that Evan is working on Mongrel too is reassuring me regarding its future.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/15616477-4391060880442221259?l=oldmoe.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://oldmoe.blogspot.com/feeds/4391060880442221259/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=15616477&amp;postID=4391060880442221259' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/15616477/posts/default/4391060880442221259'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/15616477/posts/default/4391060880442221259'/><link rel='alternate' type='text/html' href='http://oldmoe.blogspot.com/2008/01/numbers-are-in-thin-is-sort-of-faster.html' title='The numbers are in, Thin is (sort of) faster than Mongrel for production apps.'/><author><name>oldmoe</name><uri>http://www.blogger.com/profile/14341721253106429644</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='24' height='32' src='http://3.bp.blogspot.com/_XFDHsTWrvBY/SsFeIEFTI_I/AAAAAAAAADU/NphzXmrghkw/S220/oldmoe.png'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-15616477.post-157382649559325102</id><published>2008-01-21T12:23:00.000+02:00</published><updated>2008-01-26T14:40:01.722+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ruby'/><category scheme='http://www.blogger.com/atom/ns#' term='rails'/><category scheme='http://www.blogger.com/atom/ns#' term='nginx'/><category scheme='http://www.blogger.com/atom/ns#' term='mongrel'/><category scheme='http://www.blogger.com/atom/ns#' term='thin'/><title type='text'>Thin, the thin server that can!</title><content type='html'>I did some testing for the new kid on the block. The &lt;a href="http://code.macournoyer.com/thin/"&gt;Thin&lt;/a&gt; Ruby web server. I used 4 instances and &lt;a href="http://nginx.net/"&gt;Nginx&lt;/a&gt; as my proxy server.&lt;br /&gt;&lt;br /&gt;Thin is based on tried and true components (best in their class if you ask me). It's got its parser from Mongrel, IO management by Eventmachine and finally it connects to your favorite Ruby framework via Rack. It's amazing how one can achieve much just by blending the right components together.&lt;br /&gt;&lt;br /&gt;For static page serving, I got a whopping ~2500 req/s on my 2GHZ Core 2 Duo machine. (vs ~900 req/s for the same machine running Mongrels). That's for 1000 concurrent users using Apache Bench&lt;br /&gt;&lt;br /&gt;I also managed to achieve ~1200 req/s for a dynamic request in Rails that prints out 'Hello world!'. For the same 1000 concurrent users.&lt;br /&gt;&lt;br /&gt;I will put it to real test in the coming days in more real world scenarios. I hope to be able to post the results here soon.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/15616477-157382649559325102?l=oldmoe.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://oldmoe.blogspot.com/feeds/157382649559325102/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=15616477&amp;postID=157382649559325102' title='7 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/15616477/posts/default/157382649559325102'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/15616477/posts/default/157382649559325102'/><link rel='alternate' type='text/html' href='http://oldmoe.blogspot.com/2008/01/thin-thin-server-that-can.html' title='Thin, the thin server that can!'/><author><name>oldmoe</name><uri>http://www.blogger.com/profile/14341721253106429644</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='24' height='32' src='http://3.bp.blogspot.com/_XFDHsTWrvBY/SsFeIEFTI_I/AAAAAAAAADU/NphzXmrghkw/S220/oldmoe.png'/></author><thr:total>7</thr:total></entry><entry><id>tag:blogger.com,1999:blog-15616477.post-6781938842718491574</id><published>2008-01-07T00:40:00.001+02:00</published><updated>2008-03-02T01:06:57.190+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='rest'/><category scheme='http://www.blogger.com/atom/ns#' term='rails'/><category scheme='http://www.blogger.com/atom/ns#' term='pagination'/><title type='text'>Restful Pagination in Rails</title><content type='html'>Have you ever tried to cache your paginated lists? Sadly, vanilla Rails wont help much as they ignore the url query parameters when caching and hence the page=x value is not honored and Rails caching (action or page caching) will simply stick to the first page rendered for all requests.&lt;br /&gt;&lt;br /&gt;One might come with a solution that overrides the cache key generation to incorporate the query string, which will work, but will result in very long and ugly hash keys.&lt;br /&gt;&lt;br /&gt;Luckily there is a better approach, if you simply defined routs for pages (for the paginated resources) and name them page parameter with the same name you give it in the paginator then Rails will pick up the route when creating paginated links. &lt;br /&gt;&lt;br /&gt;In your routes.rb&lt;pre class="code"&gt;map.resources    :users&lt;br /&gt;map.paged_users   '/users/pages/:page'&lt;br /&gt;map.formatted_paged_users  '/users/pages/:page.:format'&lt;/pre&gt;&lt;br /&gt;once the above routes are in place, all you need is to make sure your paginators are using 'page' as the page parameter name and you will see the pagination links created like this:&lt;pre class="code"&gt;/users/pages/1&lt;br /&gt;/users/pages/2&lt;/pre&gt;&lt;br /&gt;Don't forget the formatted route to support pagination with various formats so you can use routes like:&lt;pre class="code"&gt;/users/pages/1.xml&lt;/pre&gt;&lt;br /&gt;These urls are very cache friendly and adhere to REST much more than the default parameters based ones.&lt;br /&gt;&lt;br /&gt;Happy caching (with pagination)&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/15616477-6781938842718491574?l=oldmoe.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://oldmoe.blogspot.com/feeds/6781938842718491574/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=15616477&amp;postID=6781938842718491574' title='5 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/15616477/posts/default/6781938842718491574'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/15616477/posts/default/6781938842718491574'/><link rel='alternate' type='text/html' href='http://oldmoe.blogspot.com/2008/01/restful-pagination-in-rails.html' title='Restful Pagination in Rails'/><author><name>oldmoe</name><uri>http://www.blogger.com/profile/14341721253106429644</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='24' height='32' src='http://3.bp.blogspot.com/_XFDHsTWrvBY/SsFeIEFTI_I/AAAAAAAAADU/NphzXmrghkw/S220/oldmoe.png'/></author><thr:total>5</thr:total></entry><entry><id>tag:blogger.com,1999:blog-15616477.post-548272453583206535</id><published>2007-09-27T16:08:00.001+02:00</published><updated>2007-09-27T16:19:50.487+02:00</updated><title type='text'>To AJAX or Not to AJAX? That is the question!</title><content type='html'>&lt;p&gt;When faced with a new web project these days you typically hear the clients listing AJAX as one of the must haves in their brand new web application. Pretty cool as you might be accustomed yourself to AJAX to the extent that you can hardly imagine returning back to the page reload per click days. But, if you are more sensible (or better yet, your clients are so) you would think twice before entirely abandoning the normal site browsing model for an AJAX based one.&lt;br /&gt;&lt;br /&gt;Why? I hear you say. Many reasons, including the fact that we live in the early 21st century, where – get ready for this – not all Internet access devices are equipped with state of the art browsers that can consume your AJAX interfaces or whatever Javascript or CSS magic you throw at them. Many mobile phones (millions to say the least) can hardly parse plain old HTML, some can do CSS but not Javascript&lt;br /&gt;&lt;br /&gt;Ok, you tell me. “I will have to do two versions, one that is full of AJAX effects and one old boring HTML only version.” STOP IT, I say, you can't be more wrong. Thank God there could be more elegant solutions to the problem than just writing another application around the same database. I present to you my humble take on the problem. Using the Ruby on Rails Framework (you can apply similar thoughts in other frameworks if you like, and many ideas can be copied easily as they only involve Javascript)&lt;br /&gt;&lt;/p&gt;&lt;p&gt; First off, the controllers. The controllers are responsible for receiving requests and sending responses. What we need to do is make them intelligent enough to understand different types of requests and respond accordingly. This is done using Rails magical method “respond_to”&lt;br /&gt;&lt;/p&gt;&lt;pre&gt;class IssuesController &amp;lt; ApplicationController&lt;br /&gt;def index&lt;br /&gt;...&lt;br /&gt;respond_to do |format|&lt;br /&gt;format.html { # do something }&lt;br /&gt;format.js { # do another thing }&lt;br /&gt;format.json { # and another thing }&lt;br /&gt;format.xml { # ok, enough }&lt;br /&gt;end&lt;br /&gt;end&lt;br /&gt;...&lt;br /&gt;end&lt;br /&gt;&lt;/pre&gt;&lt;p&gt; In the above example we see that each format will have a different response. This is great for a start, that way we can implement slightly varying responses for the AJAX and the none AJAX calls. To make things easier on us we will implement a very simple case of AJAX. Each rhtml view is rendered in a DIV tag within an rhtml layout. In the none AJAX model, pages are rendered by rendering both the layout and the inner view. In the AJAX model, only the inner view is rendered and is sent back to the browser to replace whatever resides in the content DIV.&lt;br /&gt;&lt;/p&gt;&lt;p&gt;  So, our controllers will work as follows:&lt;br /&gt;&lt;/p&gt;  &lt;pre&gt;class IssuesController &amp;lt; ApplicationController&lt;br /&gt;def index&lt;br /&gt;...&lt;br /&gt;respond_to do |format|&lt;br /&gt;format.html   # will render index.rhtml&lt;br /&gt;format.js { render :layout =&gt; false }&lt;br /&gt;# the above line will render index.rhtml but without the layout&lt;br /&gt;end&lt;br /&gt;end&lt;br /&gt;...&lt;br /&gt;end&lt;br /&gt;&lt;/pre&gt;  &lt;p&gt; The above lines made our controller ready to respond to normal or AJAX requests (given that AJAX requests will have the .js format). In the former case it will return back the whole page but in the latter it will omit rendering the layout and only send the content.&lt;br /&gt;&lt;/p&gt;&lt;p&gt; Ok, but what we still need two views. I hear you, and fear not, you will have to change nothing. Actually it's only a trivia to adapt your views to this model. Let's see how this can be done.&lt;br /&gt;&lt;/p&gt;Here's a normal view code sample, and pardon me, I won't use the link_to helper method for clarity purposes:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;...&lt;br /&gt;&amp;lt;div id=”content”&amp;gt;&lt;br /&gt;...&lt;br /&gt;&amp;lt;ul&amp;gt;&lt;br /&gt;&amp;lt;li&amp;gt;&amp;lt;a href=”url1”&amp;gt;Link1&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;&lt;br /&gt;&amp;lt;li&amp;gt;&amp;lt;a href=”url2”&amp;gt;Link2&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;&lt;br /&gt;&amp;lt;li&amp;gt;&amp;lt;a href=”url3”&amp;gt;Link3&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;&lt;br /&gt;&amp;lt;/ul&amp;gt;&lt;br /&gt;...&lt;br /&gt;&amp;lt;form target=”url4”&amp;gt;&lt;br /&gt;...&lt;br /&gt;&amp;lt;input type=”submit”&amp;gt;&lt;br /&gt;&amp;lt;/form&amp;gt;&lt;br /&gt;...&lt;br /&gt;&amp;lt;/div&amp;gt;&lt;br /&gt;...&lt;br /&gt;&lt;/pre&gt;  &lt;p&gt; The above fragment shows a list of links and a form. All should behave in the normal way and reload the page when clicked. Now let's imagine that the user is using a Javascript capable browser. What effect could this coming fragment have on his experience?&lt;br /&gt;&lt;/p&gt;  &lt;pre&gt;&lt;br /&gt;&amp;lt;!-- Warning, this fragment requires prototype.js --&amp;gt;&lt;br /&gt;&amp;lt;script&amp;gt;&lt;br /&gt;function ajaxifyLinks(){&lt;br /&gt;// check if there is AJAX support&lt;br /&gt;if(!Ajax.getTransport())return false;&lt;br /&gt;// loop on all links&lt;br /&gt;$$('a').each(function(link){&lt;br /&gt;// attach an event observer to each link's 'onclick' event&lt;br /&gt;Event.observe(link, 'click', function(event){&lt;br /&gt;// call the original url (with .js added) with AJAX&lt;br /&gt;new Ajax.Updater('content',link.href+”.js”);&lt;br /&gt;// stop the browser from following the link&lt;br /&gt;return false;&lt;br /&gt;});&lt;br /&gt;});&lt;br /&gt;// loop on all forms&lt;br /&gt;$$('form').each(function(form){&lt;br /&gt;// attach an event observer to each form's 'onsubmit' event&lt;br /&gt;Event.observe(form, 'submit', function(event){&lt;br /&gt;// send the form contents via AJAX&lt;br /&gt;new Ajax.Updater('content',form.action+”.js”,&lt;br /&gt;   {params:Form.serialize(form),&lt;br /&gt;   method:'post'});&lt;br /&gt;// stop the browser from submitting the form&lt;br /&gt;return false;&lt;br /&gt;});&lt;br /&gt;});&lt;br /&gt;}&lt;br /&gt;&amp;lt;/script&amp;gt;&lt;br /&gt;&lt;/pre&gt;&lt;p&gt; The above code will transform EVERY link and form in the page to AJAX, that is, in case that the browser supports both Javascript and AJAX. Otherwise links and forms will remain untouched and they will behave as usual.&lt;br /&gt;&lt;/p&gt;  &lt;p&gt;Of course this is a minimalistic example. We knowingly avoided touching on any special case but, in another installment of this article we will get more intimate with the subject and may be we can handle more aggressive ... techniques!&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/15616477-548272453583206535?l=oldmoe.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://oldmoe.blogspot.com/feeds/548272453583206535/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=15616477&amp;postID=548272453583206535' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/15616477/posts/default/548272453583206535'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/15616477/posts/default/548272453583206535'/><link rel='alternate' type='text/html' href='http://oldmoe.blogspot.com/2007/09/when-faced-with-new-web-project-these.html' title='To AJAX or Not to AJAX? That is the question!'/><author><name>oldmoe</name><uri>http://www.blogger.com/profile/14341721253106429644</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='24' height='32' src='http://3.bp.blogspot.com/_XFDHsTWrvBY/SsFeIEFTI_I/AAAAAAAAADU/NphzXmrghkw/S220/oldmoe.png'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-15616477.post-282661150357470233</id><published>2007-05-25T17:46:00.001+03:00</published><updated>2008-01-26T14:42:05.600+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ruby'/><category scheme='http://www.blogger.com/atom/ns#' term='caching'/><category scheme='http://www.blogger.com/atom/ns#' term='rails'/><title type='text'>Using action+client caching to speed up your Rails application</title><content type='html'>Too many visitors are visiting your website and loads of dynamic data are being delivered to your clients?. Of those visitors, you have more people reading your site's content than people modifying it? meaning,  you get lots more GET requests than POST, PUT or DELETE?&lt;br /&gt;&lt;br /&gt;If the above questions are all answered with a YES, then, my friend, you are desperately in need of caching. Caching will help you lessen the load on your servers by doing two main things:&lt;br /&gt;&lt;ol&gt;&lt;li&gt;It eliminates lengthy trips to the (slow by nature) database to fetch the dynamic data&lt;br /&gt;&lt;/li&gt;&lt;li&gt;It frees precious CPU cycles needed in processing this data and preparing it for presentation.&lt;/li&gt;&lt;/ol&gt;I have faced the same situation with a project we are planning, we are bound to have much more GETS than any other HTTP command, and since we are building a Restful application we will have a one to one mapping between our web resources (&lt;span class="blsp-spelling-error" id="SPELLING_ERROR_0"&gt;urls&lt;/span&gt;) and our application models. The needs of our caching mechanism are the following:&lt;br /&gt;&lt;ol&gt;&lt;li&gt;It needs to be fast&lt;br /&gt;&lt;/li&gt;&lt;li&gt;It needs to be shared across multiple servers&lt;br /&gt;&lt;/li&gt;&lt;li&gt;Authentication is required for some actions&lt;/li&gt;&lt;li&gt;Page presentation changes (slightly) based on logged in user&lt;/li&gt;&lt;li&gt;Most pages are shared and only a few are private for each user&lt;br /&gt;&lt;/li&gt;&lt;/ol&gt;We have two answer the following now, what caching technique and what cache store we will use?&lt;br /&gt;&lt;br /&gt;The cache store part is easy, &lt;span class="blsp-spelling-error" id="SPELLING_ERROR_1"&gt;memcached&lt;/span&gt; seems like the most sensible choice as it achieves points 1 &amp;amp; 2 and is orthogonal to the other 3 requirements. So it is &lt;span class="blsp-spelling-error" id="SPELLING_ERROR_2"&gt;memcached&lt;/span&gt; for now.&lt;br /&gt;&lt;br /&gt;Now, which caching technique?. Rails has several caching methods, the most famous of those is Page, Action and Fragment Caching. Greg Pollack has a great writeup on these &lt;a href="http://www.railsenvy.com/2007/2/28/rails-caching-tutorial"&gt;here&lt;/a&gt; and &lt;a href="http://www.railsenvy.com/2007/3/20/ruby-on-rails-caching-tutorial-part-2"&gt;here&lt;/a&gt;. Model caching is also an option, but it can get a bit too complicated, so I'm leaving it out for now, it can be implemented later though (layering your caches is usually a good idea)&lt;br /&gt;&lt;br /&gt;Page caching is the fastest, but we will use the ability to authenticate (unless we do so via HTTP authentication, which I would love to, but sadly is not the case). This leaves us with action and fragment caching. Since the page contains slightly different presentation based on the logged in user (like a hello message and may be a localized &lt;span class="blsp-spelling-error" id="SPELLING_ERROR_3"&gt;datetime&lt;/span&gt; string) fragment caching would sound to be the better choice, no?  Well, I would love to be able to use action caching after all, this way I can server whole pages without invoking the &lt;span class="blsp-spelling-error" id="SPELLING_ERROR_4"&gt;renderer&lt;/span&gt; at all and really avoid doing lots of string processing by Ruby.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;There is a solution, if you'd just wake up and smell the coffee, we are in Web 2.0 and we should think in Web 2.0 age solutions for Web 2.0 problems. What if add little JavaScript to the page that dynamically displays the desired content based on user role. And if the content is really little, why not store it in a session cookie? Max Dunn implements a similar solution for his wiki &lt;a href="http://blog.maxdunn.com/articles/2006/09/16/ruby-on-rails-advanced-page-caching"&gt;here&lt;/a&gt; and thus the page is served the same with &lt;span class="blsp-spelling-error" id="SPELLING_ERROR_5"&gt;dom&lt;/span&gt; manipulation kicking in to do the simple mods for this specific user. Rendering of those is done on the client so no load on the server, and since the mods are really small, the client is not hurt either, and it gets to get the page much faster, it's a win win situation. Life can't be better!&lt;br /&gt;&lt;br /&gt;No, It can!. In a content driven website, many people check a hot topic frequently, and many reread the same data they read before. In those cases, the server is sending those a cached page yes, but it is resending the same bits which the browser has in it's cache. This is a waste of bandwidth, and your mongrel will be waiting for the page transfer to finish before it can consume another request.&lt;br /&gt;&lt;br /&gt;A better solution is to utilize client caching. Tell the browser to use the version in its cache if it is not invalidated. Just send the new data in a cookie and and let the page dynamically modify itself to adapt to the logged in user. Relying on session cookies for dynamic parts will prevent the browser from displaying stale data between two different session. But the page itself will not be fetched over the wire more than once, even for different users on the same computer.&lt;br /&gt;&lt;br /&gt;I am using the &lt;a href="http://blog.craz8.com/action-cache-plugin/"&gt;Action Cache &lt;span class="blsp-spelling-error" id="SPELLING_ERROR_6"&gt;Plugin&lt;/span&gt;&lt;/a&gt; by Tom Fakes to add client caching capabilities to my Action Caches. Basically things go in the following manner:&lt;br /&gt;&lt;ol&gt;&lt;li&gt;A GET request is encountered and is intercepted&lt;br /&gt;&lt;/li&gt;&lt;li&gt;Caching headers are checked, if none exists then proceed&lt;br /&gt;else send (304 NOT MODIFIED)&lt;br /&gt;&lt;/li&gt;&lt;li&gt;Action Cache is checked if it is not there then proceed&lt;br /&gt;else send the cached page (200 OK)&lt;br /&gt;&lt;/li&gt;&lt;li&gt;Action processed and page content is rendered&lt;/li&gt;&lt;li&gt;Page added to cache, with last-modified header information&lt;/li&gt;&lt;li&gt;Response sent back to browser (200 OK + all headers)&lt;/li&gt;&lt;/ol&gt;So how to determine the impact of applying these to the application&lt;br /&gt;&lt;ol&gt;&lt;li&gt;We need to know the percentage of GET requests, which can be cached as opposed to POST, PUT and DELETE ones&lt;br /&gt;&lt;/li&gt;&lt;li&gt;Of those GET requests, how many are repeated?&lt;/li&gt;&lt;li&gt;Of those repeated GET requests, how many originate from the same client?&lt;/li&gt;&lt;/ol&gt;Those numbers can tell us if our caching model works fine or not, this should be the topic of the next installment of this article&lt;br /&gt;&lt;br /&gt;Happy caching&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/15616477-282661150357470233?l=oldmoe.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://oldmoe.blogspot.com/feeds/282661150357470233/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=15616477&amp;postID=282661150357470233' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/15616477/posts/default/282661150357470233'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/15616477/posts/default/282661150357470233'/><link rel='alternate' type='text/html' href='http://oldmoe.blogspot.com/2007/05/using-actionclient-caching-to-speed-up.html' title='Using action+client caching to speed up your Rails application'/><author><name>oldmoe</name><uri>http://www.blogger.com/profile/14341721253106429644</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='24' height='32' src='http://3.bp.blogspot.com/_XFDHsTWrvBY/SsFeIEFTI_I/AAAAAAAAADU/NphzXmrghkw/S220/oldmoe.png'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-15616477.post-115715350156633489</id><published>2006-09-02T00:08:00.000+03:00</published><updated>2006-09-15T17:38:28.750+03:00</updated><title type='text'>Aiming at technology trends</title><content type='html'>How many times you were left high and dry after a very promising technology or product that you so much believed in just vanished in front of your eyes?&lt;br /&gt;&lt;br /&gt;inversely, how many times you felt that joy when you made sure that this emerging technology you embraced or evangalized is actually gaining real momentum?&lt;br /&gt;&lt;br /&gt;For me, my path in the technology sector has been a mix of both. Does that mean anything? does it say anything that every technology you adopt booms/busts?&lt;br /&gt;&lt;br /&gt;In my silly 10 years of following the computer industry in general I had the following  adoption failures:&lt;br /&gt;&lt;br /&gt;&lt;ol&gt;&lt;li&gt;&lt;a href="http://en.wikipedia.org/wiki/Cyrix"&gt;Cyrix&lt;/a&gt;, the little company that could! I was amazed by the capacity of their small team of engineers. But they just couldn't stand the tough fight. Bye Bye Cyrix.&lt;br /&gt;&lt;br /&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://en.wikipedia.org/wiki/Beos"&gt;BeOS&lt;/a&gt;, a piece of engineering beauty. At least in the usability dept. I learnt my C++ by carefully studying the BeOS AP. I did almost all my low level coding attempts on BeOS. I even learnt bash on BeOS. I wasn't grasping why not every body on earth is using it! Silly people I thought (and still think :P). BeOS is no more, RIP BeOS (would be happy to see it ressurected one day though)&lt;br /&gt;&lt;/li&gt;&lt;/ol&gt;Not a long list, what about successes?&lt;br /&gt;&lt;br /&gt;&lt;ol&gt;&lt;li&gt;&lt;a href="http://en.wikipedia.org/wiki/Hibernate_%28Java%29"&gt;Hibernate&lt;/a&gt; the ORM man, I knew it was a hit the day I saw their documentation, those guys new their stuff! I joined the ranks in the early version 2.0 days (Gavin's rewrite of the thing). You can still see me grin each time I see a developer using Hibernate at the place where I work.&lt;br /&gt;&lt;br /&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://en.wikipedia.org/wiki/Javascript"&gt;Javascript&lt;/a&gt; for semi-fat clients, it was the year 2000 and the use of Javascript for more than form validation was a taboo for many (browser compatability hell). Not for me, at the place where I work we fully embraced Javascript, and it (almost) never failed us!&lt;br /&gt;&lt;br /&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://en.wikipedia.org/wiki/Ajax_%28programming%29"&gt;Ajax&lt;/a&gt;, we've already had such functionality, but since reading adaptive path's article, I really saw what I was missing by avoiding the XMLHTTPRequest object. In a week or so I had an ajaxified wroking protoytpe of our flagship application.&lt;br /&gt;&lt;br /&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://en.wikipedia.org/wiki/Rubyonrails"&gt;Ruby on Rails&lt;/a&gt;, not really an early adopter (managed to use it for production in the pre 1.0 days).  I still get this feeling of joy whenever I hear about another success (many of those these days). Rails has come out of age, that's for sure.&lt;/li&gt;&lt;/ol&gt;Wow, that's 100% more than the failures list, I am glad that this is case though I dont think it proves anything&lt;br /&gt;&lt;br /&gt;Now what about the products/technologies I'm looking at now?&lt;br /&gt;&lt;ol&gt;&lt;li&gt;&lt;a href="http://en.wikipedia.org/wiki/Ubuntu_%28Linux_distribution%29"&gt;Ubuntu&lt;/a&gt;? &lt;a href="http://en.wikipedia.org/wiki/Debian_GNU/Linux"&gt;Debian&lt;/a&gt; was already great. Ubuntu is the icing on the top of the cake. This one might boom.&lt;br /&gt;&lt;br /&gt;&lt;/li&gt;&lt;li&gt;Offline web apps (sometimes connected apps, discussed &lt;a href="http://trimpath.com/blog/?p=52"&gt;here&lt;/a&gt;). These are just around the corner. If they manage to break the chasm before wireless technology covers the whole planet they will enjoy great success (for a while at least).&lt;br /&gt;&lt;br /&gt;&lt;/li&gt;&lt;li&gt;The new wave of falling back to the forgotten &lt;a href="http://en.wikipedia.org/wiki/REST#REST_versus_RPC"&gt;REST&lt;/a&gt; API. I beleive we have a winner here. Specially when you see something like &lt;a href="http://www.loudthinking.com/arc/000593.html"&gt;this&lt;/a&gt; coming out of it&lt;br /&gt;&lt;/li&gt;&lt;/ol&gt;Things that I hate/think will fail/would like to see fail&lt;br /&gt;&lt;br /&gt;&lt;ol&gt;&lt;li&gt;&lt;a href="http://en.wikipedia.org/wiki/JavaServer_Faces"&gt;JSF&lt;/a&gt;, I believe one day people will realize that building interfaces is not like building a brick wall. That's the day JSF and the likes will be burnt for witchcraft!&lt;br /&gt;&lt;br /&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://en.wikipedia.org/wiki/Php"&gt;PHP&lt;/a&gt;, combine an ugly inconsistent sytanx with a terrible extension API and you've got yourself a PHP clone. Even though I managed to write decent apps in PHP but wouldn't like to live this experience again.&lt;br /&gt;&lt;/li&gt;&lt;/ol&gt;That's enough for a wish-to-fail list, 2 items and I already feel the high blood pressure, I just don't want to get started on the giants now.&lt;br /&gt;&lt;br /&gt;Every body will have his own pattern of failures/successes in following trends. Would be interesting to see what others think.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/15616477-115715350156633489?l=oldmoe.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://oldmoe.blogspot.com/feeds/115715350156633489/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=15616477&amp;postID=115715350156633489' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/15616477/posts/default/115715350156633489'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/15616477/posts/default/115715350156633489'/><link rel='alternate' type='text/html' href='http://oldmoe.blogspot.com/2006/09/aiming-at-technology-trends.html' title='Aiming at technology trends'/><author><name>oldmoe</name><uri>http://www.blogger.com/profile/14341721253106429644</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='24' height='32' src='http://3.bp.blogspot.com/_XFDHsTWrvBY/SsFeIEFTI_I/AAAAAAAAADU/NphzXmrghkw/S220/oldmoe.png'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-15616477.post-115222994373603822</id><published>2006-07-07T02:52:00.000+03:00</published><updated>2006-08-16T01:10:16.093+03:00</updated><title type='text'>Rob Williams on Ruby, Jibberish and English</title><content type='html'>I was amused to read Rob William's &lt;a href="http://www.jroller.com/page/robwilliams?entry=idea_for_sd_times_ruby"&gt;take&lt;/a&gt; on a &lt;a href="http://www.sdtimes.com/article/column-20060701-01.html"&gt;Ruby article in SD Times&lt;/a&gt;. Aside from his sarcasm, he scores home with most of the arguments though I beg to differ with some of them. I will highlight some of my opinions and respond for Ruby :)&lt;br /&gt;&lt;ol&gt;&lt;li&gt;Our tools do that.!! The article was pointing to how Ruby follows the Unified Access Principle where you only have one interface to the class data members, whether it is a simple storage operation or a complex one. But, as Rob points  out, having such support in the language is useless because current tools do that. I bet Bertrand Meyer wouldn't roll on his grave because Ruby is trying to offer UAP even when there are such tools around.&lt;br /&gt;&lt;pre&gt;# initial class&lt;br /&gt;class Plan&lt;br /&gt;   attr_accessor: owner&lt;br /&gt;end&lt;br /&gt;&lt;br /&gt;plan = Plan.new&lt;br /&gt;plan.owner = rob&lt;br /&gt;plan.owner                      #  =&amp;gt; rob&lt;br /&gt;&lt;br /&gt;# we now need to upgrate to a full fledged setter&lt;br /&gt;# rather than the one dynamically generated for us above&lt;br /&gt;class Plan&lt;br /&gt;   attr_accessor: owner, assigned&lt;br /&gt;   def owner=(owner)&lt;br /&gt;       @owner = owner&lt;br /&gt;      @assigned = true     &lt;br /&gt;   end&lt;br /&gt;end&lt;br /&gt;&lt;br /&gt;plan = Plan.new&lt;br /&gt;plan.owner = rob&lt;br /&gt;plan.owner                        # =&amp;gt; rob&lt;br /&gt;plan.assinged                 # =&amp;gt; true&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;/li&gt;&lt;li&gt;Long code is like short code is like medium code! Rob is picking at the author for mentioning that Ruby produces less lines of code. He's arguing that the tool is producing those verbose Java lines for us too!. As if we should accept garbage only and only if it is spit at us by our favourite tool! Why don't we all ditch the use of annotations for Hibernate mapping when our eclipse XML editor does autocompletion for the .hbm files? it is not about too many lines of code, it is about clutter and organization. I long for the day when i used VisualAge for Java, It was really anti clutter! (written in SmallTalk, no less!)&lt;br /&gt;&lt;pre&gt;# neat example on short code&lt;br /&gt;session.time_out = 48.hours.from_now&lt;/small&gt;&lt;/span&gt;&lt;/big&gt;&lt;br /&gt;&lt;/pre&gt;&lt;/li&gt;&lt;li&gt;Rob rightfully accused the author to have poorly written the testing section and dynamism. The author was seemingly speaking about mock objects and how using them in a dynamically typed system is easier than a statically typed one. In a dynamic setting, identifying a mock object and using it is seamless. Also writing the mock object itself is seamless, no interface or contract of some sort, you only code the methods that you intend to handle and the others are handled by a common method. It's rather interesting to see that almost all the Java guys would praise AOP and tell stories about the wonders that they achieved using AOP (many are actually finding their way around the static nature of Java through AOP)&lt;br /&gt;&lt;pre&gt;class Person&lt;br /&gt;   def can_run?&lt;br /&gt;       # some tedious operation&lt;br /&gt;   end&lt;br /&gt;   def can_jump?&lt;br /&gt;       # some other operation&lt;br /&gt;   end&lt;br /&gt;end&lt;br /&gt;&lt;br /&gt;# person mockup&lt;br /&gt;# returns true whenever the method called has the character ?&lt;br /&gt;class PersonMockup&lt;br /&gt;   def method_missing(method_id)&lt;br /&gt;       method_id.to_s["?"]&lt;br /&gt;   end&lt;br /&gt;end&lt;br /&gt;&lt;br /&gt;# or for some dynamic magic&lt;br /&gt;class Roman&lt;br /&gt;   def roman_to_int(str)&lt;br /&gt;       # do conversion here&lt;br /&gt;   end&lt;br /&gt;   def method_missing(method_id)&lt;br /&gt;       roman_to_int(method_id.to_s)&lt;br /&gt;   end&lt;br /&gt;end&lt;br /&gt;&lt;br /&gt;roman = Roman.new&lt;br /&gt;roman.V            # =&amp;gt; 5&lt;br /&gt;roman.IV            #  =&amp;gt; 4&lt;br /&gt;roman.VII          #  =&amp;gt; 7&lt;br /&gt;&lt;/pre&gt;&lt;/li&gt;&lt;li&gt;&lt;span style="color: rgb(204, 0, 0);"&gt;&lt;small&gt;&lt;big&gt;&lt;span style="color: rgb(0, 0, 0);"&gt;"ActiveRecord is simplistic". Here Rob doesn't justify why he thinks AR is rather simplistic? may be he is commenting on the simple example given by the article author? AR is rather a simple interface for a stunningly powerfull engine that provides you with a wide variety of DB constructs, the has_many and sisters, are actually defined in the AR module and when invoked they add methods and functionality to the invoking class at the class definition time. And due to Ruby's syntax flexibility they fit naturally in the class definition you don't even notice that they are function calls but rather some seamless annotation of some sort (which actually affects the class being declared and adds methods and attributes dynamically to it). Ruby provides you with the ability to add even more of those to AR. By excercising this feature you can build very complex relations between your domain models and still keep your code clean and clutter free.&lt;br /&gt;&lt;/span&gt;&lt;/big&gt;&lt;/small&gt;&lt;/span&gt;&lt;br /&gt;&lt;pre&gt;class Person &amp;lt; ActiveRecord::Base&lt;br /&gt;   has_many       :plans&lt;br /&gt;   has_many        :tasks,    :through    =&amp;gt; :plans&lt;br /&gt;end&lt;br /&gt;&lt;br /&gt;class Plan &amp;lt; ActiveRecord::Base&lt;br /&gt;   has_many        :tasks&lt;br /&gt;   belongs_to        :owner, :class    =&amp;gt; "Person"&lt;br /&gt;end&lt;br /&gt;&lt;br /&gt;class Task &amp;lt; ActiveRecord::Base&lt;br /&gt;   belongs_to        :plan&lt;br /&gt;   acts_as_tree     #defines parent/child relation ship among tasks&lt;br /&gt;   acts_as_taggable #for folksonomy aware objects (AR plugin)&lt;br /&gt;end&lt;br /&gt;&lt;br /&gt;#get all completed tasks for rob (involves one hit to the database)&lt;br /&gt;completed_tasks = rob.tasks.select{|task| task.completed?}&lt;br /&gt;&lt;br /&gt;#get all completed tasks that are parents for other tasks&lt;br /&gt;completed_prent_tasks = completed_tasks.select{|taks|!task.children.empty?}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;/li&gt;&lt;li&gt;On DSLs. There are many ways one can solve a problem, but if you can shape your language around the domain you're attempting at that makes for much clearer code (wich is evident by looking at the above example) DSLs are abundant in Ruby code, mainly because the language has enough metaprogramming constructs and syntax flexibility that promotes such an approach to problems. Here are a few examples on how you can shape your code around your specific problem domain in a way that a domain expert will naturally understand the code.&lt;big&gt;&lt;/big&gt;&lt;br /&gt;&lt;pre&gt;# using a dsl suited for representing a workflow&lt;br /&gt;workflow "default" do&lt;br /&gt; step "scan"&lt;br /&gt; step "ocr" do&lt;br /&gt;   when_error "manual"&lt;br /&gt; end&lt;br /&gt; step "cleanup"&lt;br /&gt;end&lt;br /&gt;&lt;br /&gt;workflow "manual" do&lt;br /&gt; step "correction"&lt;br /&gt; step "distribute"&lt;br /&gt;end&lt;br /&gt;&lt;br /&gt;#or a dsl for meal recipes&lt;br /&gt;recipe "PBJ Sandwich"&lt;br /&gt;ingredients "two slices of bread",&lt;br /&gt;           "one heaping tablespoon of peanut butter",&lt;br /&gt;           "one teaspoon of jam"&lt;br /&gt;instructions "spread peanut butter...",&lt;br /&gt;            "spread jam...",&lt;br /&gt;            "place other slice..."&lt;br /&gt;servings 1&lt;br /&gt;prep_time "2 minutes"&lt;br /&gt;&lt;br /&gt;#another dsl from rspec&lt;br /&gt;target.should.equal 7&lt;br /&gt;target.should.not.equal 5&lt;br /&gt;target.should.be Fixnum&lt;br /&gt;target.should.contain 'a'&lt;br /&gt;target.should.be.empty&lt;br /&gt;target.should_respond_to :quak #for the love of the duck!&lt;br /&gt;&lt;br /&gt;#or from the poignant guide :D&lt;br /&gt;class Dragon &amp;lt; Creature&lt;br /&gt;  life 1340     # tough scales&lt;br /&gt;  strength 451  # bristling veins&lt;br /&gt;  charisma 1020 # toothy smile&lt;br /&gt;  weapon 939    # fire breath&lt;br /&gt;end&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;/li&gt;&lt;li&gt;camelCaseVariablesLookPrettyNeatAndAreSoSweet , on the other hand underbar_variables_look_terribly_ugly_and_dull . That's what Rob thinks I suppose. I wont comment on Rob's taste, to each his own. I used to think like him, not any more.&lt;br /&gt;&lt;pre&gt;testHasThreeClientsAndOneSupplierAndTwoStores()  //pretty java&lt;br /&gt;&lt;br /&gt;test_has_three_clients_and_one_supplier_and_two_stores #ugly ruby :)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;/li&gt;&lt;/ol&gt;I have to stop before this turns into a Ruby vs. Java thing (a typical my daddy is bigger than your daddy duel) or did it happen already? Anyways, Rob was complaining from the poor quality of the article, which is largely true (I bet he'd complain from the poor quality of my writeup too, but would that stop me?). My issue is that harm was done to Ruby the language in the exchange, that's why I tried to shed some more light on the issues mentioned. I dont hate Java, I just think I had too much coffee ;)&lt;br /&gt;&lt;br /&gt;To each his tools, to each his rules&lt;br /&gt;&lt;br /&gt;oldmoe&lt;small&gt;&lt;b&gt;&lt;i&gt;&lt;br /&gt;&lt;br /&gt;"My opinion is right, though it could possibly be wrong. Your opinion is wrong, though it could possibly be right", Imam Shafey&lt;/i&gt;&lt;/b&gt;&lt;/small&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/15616477-115222994373603822?l=oldmoe.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://oldmoe.blogspot.com/feeds/115222994373603822/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=15616477&amp;postID=115222994373603822' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/15616477/posts/default/115222994373603822'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/15616477/posts/default/115222994373603822'/><link rel='alternate' type='text/html' href='http://oldmoe.blogspot.com/2006/07/rob-williams-on-ruby-jibberish-and.html' title='Rob Williams on Ruby, Jibberish and English'/><author><name>oldmoe</name><uri>http://www.blogger.com/profile/14341721253106429644</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='24' height='32' src='http://3.bp.blogspot.com/_XFDHsTWrvBY/SsFeIEFTI_I/AAAAAAAAADU/NphzXmrghkw/S220/oldmoe.png'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-15616477.post-114832968488416463</id><published>2006-05-22T23:28:00.000+03:00</published><updated>2006-05-22T23:28:04.973+03:00</updated><title type='text'>Guide: Environments in Rails 1.1</title><content type='html'>This article covers what environments in Ruby on Rails are, how they are configured, and how you can create custom environments outside of the stock development, test and production.&lt;br/&gt;&lt;br/&gt;&lt;a href="http://glu.ttono.us/articles/2006/05/22/guide-environments-in-rails-1-1"&gt;read more&lt;/a&gt;&amp;nbsp;|&amp;nbsp;&lt;a href="http://digg.com/programming/Guide:_Environments_in_Rails_1.1"&gt;digg story&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/15616477-114832968488416463?l=oldmoe.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://oldmoe.blogspot.com/feeds/114832968488416463/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=15616477&amp;postID=114832968488416463' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/15616477/posts/default/114832968488416463'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/15616477/posts/default/114832968488416463'/><link rel='alternate' type='text/html' href='http://oldmoe.blogspot.com/2006/05/guide-environments-in-rails-11.html' title='Guide: Environments in Rails 1.1'/><author><name>oldmoe</name><uri>http://www.blogger.com/profile/14341721253106429644</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='24' height='32' src='http://3.bp.blogspot.com/_XFDHsTWrvBY/SsFeIEFTI_I/AAAAAAAAADU/NphzXmrghkw/S220/oldmoe.png'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-15616477.post-114815964403928325</id><published>2006-05-21T00:14:00.000+03:00</published><updated>2006-05-21T00:14:04.046+03:00</updated><title type='text'>acts_as_taggable_tag (take two)</title><content type='html'>It's been a while since I wrote any update on that topic, but I'm glad that I will be reporting good progress this time.&amp;nbsp; The acts_as_taggable_tag (AATT from now on) plugin is shaping up nicely (along with my Ruby and Rails knowledge).&lt;br /&gt;&lt;br /&gt;Currently the AATT is a real plugin that lives in /vendor/plugins in your rails app. The plugin enables you to do that to any of your models&lt;br /&gt;&lt;code&gt;&lt;br /&gt;class Person &amp;lt; ActiveRecord::Base&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; acts_as_taggable_tag&lt;br /&gt;end&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;This simple invocation adds the following to your model class&lt;br /&gt;&lt;code&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; #These methods are called to define the relations&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; has_many :tag_joins, :class_name =&amp;gt; "Tagging", :as =&amp;gt; :tagged_one&lt;br /&gt;            &amp;nbsp;&amp;nbsp;&amp;nbsp; has_many :tagged_one_joins, :class_name =&amp;gt; "Tagging", :as =&amp;gt; :tag&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; #These instance methods are defined for your model&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; tags&amp;nbsp;&amp;nbsp;  &amp;nbsp;&amp;nbsp;  &amp;nbsp;&amp;nbsp;  &amp;nbsp;&amp;nbsp;  &amp;nbsp;&amp;nbsp;  &amp;nbsp;&amp;nbsp;&amp;nbsp; #returns a list of objects that tag yours&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; tagged_ones&amp;nbsp;&amp;nbsp;  &amp;nbsp;&amp;nbsp;  &amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; #&lt;/code&gt;&lt;code&gt;returns a list of objects that are tagged by you&lt;/code&gt;&lt;code&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; tag(tagged_one)&amp;nbsp;&amp;nbsp;&amp;nbsp;  &amp;nbsp;&amp;nbsp;&amp;nbsp; #tag this object by yourself&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; remove_tag(tag)&amp;nbsp;&amp;nbsp;&amp;nbsp;  &amp;nbsp;&amp;nbsp;&amp;nbsp; #remove this tag from you&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; clear_all_tags&amp;nbsp;&amp;nbsp;  &amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; #delete the relations between you and your tags&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; clear_all_tagged_ones&amp;nbsp; #delete the relations between you and objects tagged by you&lt;br /&gt;&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;If you look at the implementation of the above methods you'll notice how inefficient they are (a select call for each tag on a certain object for example). Performance is not my primary concern at this point in time, I am just trying to get the concept right.&lt;br /&gt;&lt;br /&gt;A class is created for the&amp;nbsp; dual polymorphic join model (name Tagging). Currently the name and the table mappings are not configurable (you have to use what I give you, period). The table structure is available in a migration format and can be invoked by:&lt;br /&gt;&lt;code&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; rake import_aatt_schema&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;and it can be dropped from the database using:&lt;br /&gt;&lt;code&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; rake drop_aatt_schema&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;I will be preparing a .zip file containing the plugin. To install it you only need to unzip it in the vendor/plugins directory. A great guide to using plugins can be found &lt;a href="http://wiki.rubyonrails.org/rails/pages/Plugins"&gt;here&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/15616477-114815964403928325?l=oldmoe.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://oldmoe.blogspot.com/feeds/114815964403928325/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=15616477&amp;postID=114815964403928325' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/15616477/posts/default/114815964403928325'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/15616477/posts/default/114815964403928325'/><link rel='alternate' type='text/html' href='http://oldmoe.blogspot.com/2006/05/actsastaggabletag-take-two.html' title='acts_as_taggable_tag (take two)'/><author><name>oldmoe</name><uri>http://www.blogger.com/profile/14341721253106429644</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='24' height='32' src='http://3.bp.blogspot.com/_XFDHsTWrvBY/SsFeIEFTI_I/AAAAAAAAADU/NphzXmrghkw/S220/oldmoe.png'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-15616477.post-114686659821778901</id><published>2006-05-06T01:00:00.000+03:00</published><updated>2006-05-06T02:42:32.106+03:00</updated><title type='text'>acts_as_taggable_tag</title><content type='html'>&lt;p&gt;&lt;a href="http://rubyforge.org/projects/taggable/" title="acts_as_taggable" mce_href="http://rubyforge.org/projects/taggable/"&gt;acts_as_taggable&lt;/a&gt; provides a very easy means for tagging various objects in your Rails application. By using this plugin you can now add a tag to every object and even look at those objects from the tag's point of view; thanks to :polymorphic =&gt; true.&lt;/p&gt; &lt;p&gt;I was entertaining the idea of using tagging in a system that I am intending to build. There would be a very generic framework that consists of a certain basic element that can be tagged by different types of tags. I then realized that this was actually the opposite of what acts_as_taggable does!. acts_as_taggable defines a single tag type that can be applied to any object. This lead me to thinking, why not join both ideas? And hence the acts_as_taggable_tag.&lt;/p&gt; &lt;p&gt;acts_as_taggable_tag is not yet a module, but I couldn't resist the name ;). What it does is that it simply enables any object to act as a tag for another object even if it was of the same class or even if it was tagging itself! Thus implementing dynamic many to many associations across all your persistent domain objects through tags&lt;/p&gt; &lt;p&gt;The caveat though is that has_many :through does not play nicely with polymorphic associations as explained &lt;a href="http://blog.hasmanythrough.com/articles/2006/04/03/polymorphic-through" title="polymorphic_through" mce_href="http://blog.hasmanythrough.com/articles/2006/04/03/polymorphic-through"&gt;here&lt;/a&gt; . I ended up using only the join table (taggings in my case) and adding methods for retrieving both the objects that act as tags for the current object and the objects that are tagged by the current object&lt;/p&gt; &lt;p&gt;This implementation  is a bit lacking when it comes to performance. What would make it sweet though is to enable :polymorphic associations with a has_many :through, looks like the next thing to dig into :)&lt;br /&gt;&lt;br /&gt;Now for the code:&lt;br /&gt;&lt;/p&gt;&lt;br /&gt;Person Class&lt;br /&gt;&lt;pre style="border: 1px dotted black; padding: 6px; background-color: rgb(255, 255, 204); font-size: 12px; color: black;"&gt;class Person &amp;lt; ActiveRecord::Base&lt;br /&gt;has_many :tag_joins,&lt;br /&gt;:class_name =&gt;"Tagging",&lt;br /&gt;:as =&gt; :tagged_one&lt;br /&gt;has_many :tagged_one_joins,&lt;br /&gt;:class_name =&gt; "Tagging",&lt;br /&gt;:as =&gt; :tag&lt;br /&gt;&lt;br /&gt;def tags&lt;br /&gt;self.tag_joins.collect { |tj| tj.tag }&lt;br /&gt;end&lt;br /&gt;&lt;br /&gt;def tagged_ones&lt;br /&gt;self.tagged_one_joins.collect { |tj| tj.tagged_one }&lt;br /&gt;end&lt;br /&gt;end&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Message Class&lt;br /&gt;&lt;pre style="border: 1px dotted black; padding: 6px; background-color: rgb(255, 255, 204); font-size: 12px; color: black;"&gt;class Message &amp;lt; ActiveRecord::Base&lt;br /&gt;has_many :tag_joins,&lt;br /&gt;:class_name =&gt;"Tagging",&lt;br /&gt;:as =&gt; :tagged_one&lt;br /&gt;has_many :tagged_one_joins,&lt;br /&gt;:class_name =&gt; "Tagging",&lt;br /&gt;:as =&gt; :tag&lt;br /&gt;&lt;br /&gt;def tags&lt;br /&gt;self.tag_joins.collect { |tj| tj.tag }&lt;br /&gt;end&lt;br /&gt;&lt;br /&gt;def tagged_ones&lt;br /&gt;self.tagged_one_joins.collect { |tj| tj.tagged_one }&lt;br /&gt;end&lt;br /&gt;end&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Tagging Class&lt;br /&gt;&lt;pre style="border: 1px dotted black; padding: 6px; background-color: rgb(255, 255, 204); font-size: 12px; color: black;"&gt;class Tagging &amp;lt; ActiveRecord::Base&lt;br /&gt;belobgs_to :tag, :polymorphic =&gt; true&lt;br /&gt;belongs_to :tagged_one, :polymorphic =&gt; true&lt;br /&gt;end&lt;br /&gt;&lt;/pre&gt;&lt;p&gt;The code in the Person and Message classes is identical, now each of them has a list of tags and a list of tagged_ones each containing whatever objects of whatever classes that happen to tag or be tagged by the current instance of Person or Message&lt;br /&gt;&lt;br /&gt;The Tagging class represents the double polymorphic association between the tagging object and the tagged one regardless of the Class. This approach can be used to implement all sorts of hierarchies among your persistent objects through tagging. I am using it to build a multi process project management tool, which through tags can create different views of the same data like an Iteration/Story one for something like XPlanner or a TodoList one for the BaseCamp style.&lt;br /&gt;&lt;/p&gt;What's next is to look at how to modify AR so it will accept polymorphic associations with a  has_many :through. But that can wait, I'm already glad that I can tag with such a flexible structure.&lt;br /&gt;&lt;br /&gt;Happy tagging :)&lt;br /&gt;&lt;p&gt;&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/15616477-114686659821778901?l=oldmoe.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://oldmoe.blogspot.com/feeds/114686659821778901/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=15616477&amp;postID=114686659821778901' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/15616477/posts/default/114686659821778901'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/15616477/posts/default/114686659821778901'/><link rel='alternate' type='text/html' href='http://oldmoe.blogspot.com/2006/05/actsastaggabletag.html' title='acts_as_taggable_tag'/><author><name>oldmoe</name><uri>http://www.blogger.com/profile/14341721253106429644</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='24' height='32' src='http://3.bp.blogspot.com/_XFDHsTWrvBY/SsFeIEFTI_I/AAAAAAAAADU/NphzXmrghkw/S220/oldmoe.png'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-15616477.post-114502402851242330</id><published>2006-04-14T15:00:00.000+02:00</published><updated>2006-04-14T16:13:48.576+02:00</updated><title type='text'>White collar cultures (A.K.A multinationals)</title><content type='html'>This is not a whining post (at least I dont intend it to be so). I'm just trying to think out of the box for a change.&lt;br /&gt;&lt;br /&gt;For the past few months I was surrounded by more white collars than ever in my life. Mr. IT Manager this and Mr. IT Manager that, even Mr. Director of Technical and Non Techncial Economic Hyper Relations at PSGCD (their names happen to puzzle you somtimes, dont they?).&lt;br /&gt;&lt;br /&gt;And guess what? meetings to these people are like water to a fish, they can hardly survive out of the meeting room. We need to change a label? let's throw an ultra high level management meeting for three companies and let everyone and his brother join in. Now after 3 hours of brain trashing everyone agrees that the label really needs changing, and a follow up meeting is set to decide on the actual label to be used (ofcourse that's an exaggeration so take it with a grain of salt,...or two!)&lt;br /&gt;&lt;br /&gt;I just heard a comment  saying the I should be doing more meetings and less programming! I beg your pardon, programming is MY WATER! I dont imagine myself laying back and moving things by pointing a stick at them. I'm not an ivory tower type of a manager either. And I dont like the constraints that conventional management and white collar cultures are putting around me.&lt;br /&gt;&lt;br /&gt;The joke is, after all that, those white collars are &lt;span style="font-weight: bold;"&gt;&lt;span style="font-style: italic;"&gt;COSTING US MONEY!&lt;/span&gt;&lt;/span&gt; you'd imagine that working with multinationals is like having a cash cow, rather it's like having a fake cow for display (and no milk at all).   You only have to deal with complicated requirements (that dont even make sense sometimes), very eager expectations, very slow payment and very limited technical assistance (up to the level of requiring us to travel between cities to install files).&lt;br /&gt;&lt;br /&gt;I bet you're asking now: "why are you putting up with all of this?", I just asked myself the same question, and hence that (seemingly whining!) post :)&lt;br /&gt;&lt;br /&gt;I'm still pondering it all in my mind, where should we (or I?) be directed, how can we be happy? and I mean HAPPY!&lt;br /&gt;&lt;br /&gt;I'd quote DHH here, "so be happy"&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/15616477-114502402851242330?l=oldmoe.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://oldmoe.blogspot.com/feeds/114502402851242330/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=15616477&amp;postID=114502402851242330' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/15616477/posts/default/114502402851242330'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/15616477/posts/default/114502402851242330'/><link rel='alternate' type='text/html' href='http://oldmoe.blogspot.com/2006/04/white-collar-cultures-aka.html' title='White collar cultures (A.K.A multinationals)'/><author><name>oldmoe</name><uri>http://www.blogger.com/profile/14341721253106429644</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='24' height='32' src='http://3.bp.blogspot.com/_XFDHsTWrvBY/SsFeIEFTI_I/AAAAAAAAADU/NphzXmrghkw/S220/oldmoe.png'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-15616477.post-113166067031051725</id><published>2005-11-10T23:58:00.001+02:00</published><updated>2005-11-11T00:11:10.310+02:00</updated><title type='text'>Changing Jobs</title><content type='html'>I'm back at this again! Changing jobs for the.... well... for the second time in my career! I'm even moving back to my old place!.  I hope this move will prove fruitfull. By all means, I will try to make it so inshaAllah.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/15616477-113166067031051725?l=oldmoe.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://oldmoe.blogspot.com/feeds/113166067031051725/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=15616477&amp;postID=113166067031051725' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/15616477/posts/default/113166067031051725'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/15616477/posts/default/113166067031051725'/><link rel='alternate' type='text/html' href='http://oldmoe.blogspot.com/2005/11/changing-jobs.html' title='Changing Jobs'/><author><name>oldmoe</name><uri>http://www.blogger.com/profile/14341721253106429644</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='24' height='32' src='http://3.bp.blogspot.com/_XFDHsTWrvBY/SsFeIEFTI_I/AAAAAAAAADU/NphzXmrghkw/S220/oldmoe.png'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-15616477.post-113166027907862705</id><published>2005-11-10T23:58:00.000+02:00</published><updated>2006-09-24T16:38:23.976+02:00</updated><title type='text'>Prayers in Ramadan!</title><content type='html'>Here's a &lt;a href="http://www.rubydo.net/memories/ramadan2005/index.html"&gt;link&lt;/a&gt; for some photos of the prayers in the last nights of Ramadan. I can be found somewhere in the crowds :). I was very impressed by the numbers!. I only hope this trend continues.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/15616477-113166027907862705?l=oldmoe.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://oldmoe.blogspot.com/feeds/113166027907862705/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=15616477&amp;postID=113166027907862705' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/15616477/posts/default/113166027907862705'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/15616477/posts/default/113166027907862705'/><link rel='alternate' type='text/html' href='http://oldmoe.blogspot.com/2005/11/prayers-in-ramadan.html' title='Prayers in Ramadan!'/><author><name>oldmoe</name><uri>http://www.blogger.com/profile/14341721253106429644</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='24' height='32' src='http://3.bp.blogspot.com/_XFDHsTWrvBY/SsFeIEFTI_I/AAAAAAAAADU/NphzXmrghkw/S220/oldmoe.png'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-15616477.post-112949931378610456</id><published>2005-10-16T23:17:00.000+02:00</published><updated>2005-10-16T23:48:52.070+02:00</updated><title type='text'>Ramadan..</title><content type='html'>It is time for Ramadan again (the month of fasting for us Muslims). I  hardly can find time to blog because of the many things to do. I hope I will be back in shape soon. Happy Ramadan to Muslims every where. Happy Ramadan to humanity.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/15616477-112949931378610456?l=oldmoe.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://oldmoe.blogspot.com/feeds/112949931378610456/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=15616477&amp;postID=112949931378610456' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/15616477/posts/default/112949931378610456'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/15616477/posts/default/112949931378610456'/><link rel='alternate' type='text/html' href='http://oldmoe.blogspot.com/2005/10/ramadan.html' title='Ramadan..'/><author><name>oldmoe</name><uri>http://www.blogger.com/profile/14341721253106429644</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='24' height='32' src='http://3.bp.blogspot.com/_XFDHsTWrvBY/SsFeIEFTI_I/AAAAAAAAADU/NphzXmrghkw/S220/oldmoe.png'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-15616477.post-112812342889884853</id><published>2005-10-01T00:52:00.000+02:00</published><updated>2005-10-01T01:37:08.946+02:00</updated><title type='text'>JSTemplates</title><content type='html'>Our code base became largely infested with lots of JSTemplates (Javascript templates from trimpath.com) . We are using them for almost all view rendering now. JSTemplates are great but there are certain areas where they keep you wanting more. I'll try to put those issues into prespective here:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;No remote includes!&lt;br /&gt;It would be great if one could just include a url in a template. But how could this be done? and the latency? these are questions that arise when speaking about such a feature&lt;/li&gt;&lt;li&gt;InnerHTML based!&lt;br /&gt;since they always return a string representing the rendred template the only reasonable way to use it is to set it as the innerhtml of some html node in your document. While this is a normal practice it has some drawbacks!&lt;/li&gt;&lt;ul&gt;&lt;li&gt;once you set the inner html of an object the control is immediately returned before the content is actually parsed and added to the dom tree&lt;/li&gt;&lt;li&gt;so if you want to access an element just after the template was rendered you might find it not yet available as a dom node!&lt;/li&gt;&lt;li&gt;While every benchmark on that issue says that setting innerhtml is the fastest thing on earth I believe that dom manipulation is much faster (the benchmarks count only the time needed to create the string and assign it to the node, not the parsing and rendering time!)&lt;/li&gt;&lt;/ul&gt;&lt;/ul&gt;So here we have them! only two issues :)&lt;br /&gt;&lt;br /&gt;The first is really not that important. It is the second one that bugs me! I think one solution would be to parse the rendered template yourself and create the nodes as you go and after you finish you append them to the container. But this would be much slower than the browser's implementation! An alternative would be an event that fires when the browser finishes the rendering! would that be possible? I think it could be done one way or another (like every render function returns a unique id of an object it appends to the end of the template. This is looked for on a timeout or interval method which would fire some callback if it is found).&lt;br /&gt;&lt;br /&gt;Just some thoughts. Aside from that JSTemplates are ultra cool!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/15616477-112812342889884853?l=oldmoe.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://oldmoe.blogspot.com/feeds/112812342889884853/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=15616477&amp;postID=112812342889884853' title='8 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/15616477/posts/default/112812342889884853'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/15616477/posts/default/112812342889884853'/><link rel='alternate' type='text/html' href='http://oldmoe.blogspot.com/2005/10/jstemplates.html' title='JSTemplates'/><author><name>oldmoe</name><uri>http://www.blogger.com/profile/14341721253106429644</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='24' height='32' src='http://3.bp.blogspot.com/_XFDHsTWrvBY/SsFeIEFTI_I/AAAAAAAAADU/NphzXmrghkw/S220/oldmoe.png'/></author><thr:total>8</thr:total></entry><entry><id>tag:blogger.com,1999:blog-15616477.post-112803187532646287</id><published>2005-09-30T00:04:00.001+02:00</published><updated>2008-02-18T13:13:16.727+02:00</updated><title type='text'>(AOJP) Aspect Oriented Javascript Programming</title><content type='html'>Update: I did a newer implementation &lt;a href="http://oldmoe.blogspot.com/2008/02/aspect-oriented-javascript-revisited.html"&gt;here&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;In an attempt to trace the program flow of our Javascript client application we needed a stack trace of function calls. We needed to eliminate unneeded redraw and keep the code flow intact.&lt;br /&gt;Over at &lt;a href="http://www.jroller.com/page/deep/20030701"&gt;deep&lt;/a&gt; some amazing AOP stuff in Javascript can be found. So what we did to print the stack trace? only an advice is added before all the interrested functions (Javascript has enough reflective abilities for us to be able to inject these advices along our object graph dynamically). And now we have a nice and accurate call stack! Ofcourse many cases of redraw are gone now. And we introduced another feature into our Javascript beast, AOP capabilities!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/15616477-112803187532646287?l=oldmoe.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://oldmoe.blogspot.com/feeds/112803187532646287/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=15616477&amp;postID=112803187532646287' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/15616477/posts/default/112803187532646287'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/15616477/posts/default/112803187532646287'/><link rel='alternate' type='text/html' href='http://oldmoe.blogspot.com/2005/09/aojp-aspect-oriented-javascript.html' title='(AOJP) Aspect Oriented Javascript Programming'/><author><name>oldmoe</name><uri>http://www.blogger.com/profile/14341721253106429644</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='24' height='32' src='http://3.bp.blogspot.com/_XFDHsTWrvBY/SsFeIEFTI_I/AAAAAAAAADU/NphzXmrghkw/S220/oldmoe.png'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-15616477.post-112769136411908738</id><published>2005-09-26T02:08:00.000+03:00</published><updated>2005-09-27T01:08:55.276+03:00</updated><title type='text'>Move the view to the browser</title><content type='html'>Any self respecting MVC framework would have some sort of view manipulation. Almost all those sever centric frameworks have some sort of view manipulation. You create your views in your templating engine of choice (Velocity, Smarty, ERB, ASP, JSP or whatever). The views are processed by the server and the rendered result is sent to the browser. Some templating and view technologies go an extra step further an enable you to use cached copies of the compiled view templates.&lt;br /&gt;&lt;br /&gt;Of particular interest where the SpringMVC and the RubyOnRails frameworks. Both provide means for view handling. And both provide hooks for custom view handlers. Which would lead to the interesting question.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;What's wrong with the current view handlers?&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;All these handlers operate in the classical web application linked pages model (Web 1.0). With the eager move to Single Page Applications (Web 2.0) we need to add these abilities to current frameworks (or create new frameworks?)&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;How can we acheive this?&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;&lt;/span&gt;&lt;/span&gt;Instead of sending complete pages. The server should send to the client data only. And this data should only be view data with no control data. The view control data should reside completely on the client. So instead of responding to a list_items request with a rendered html page with the items required we will only send the data required. To be parsed and rendered by the client browser.&lt;span style="font-weight: bold;"&gt;&lt;span style="font-weight: bold;"&gt;&lt;/span&gt;&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;And data format?&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Although it seems natural that we use XML for data sending. In a web browser environmnet it is more fit to use a more Javascript friendly approach. JSON (which is very XML like but in native Javascript) can be used as a data interchange medium. JSON will easily be parsed into live Javascript objects. Passed easily to templates (Javascript Templates from trimpath). And used to store model cache if needed (keeping the cache in the native Javascript format rather than XML is crucial for performance).&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Template Engine?&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;As mentioned above. The Javascript Template from www.trimpath.com act as a very good alternative to its server side cousins. It has Smarty, Velocity like syntax. And it is very extinsible. The only draw back is that it is lacking an include mechanism (though this is partially solved by using its Macros)&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;And the result?&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Adding web 2.0 application capabilities to the current tried and true web application frameworks. Making it easy to move even current apps to the user friendly era of web 2.0. I will be working soon in the Spring to JStemplates interfaces and I might delve into a RoR implementation as well (this would be very easy I believe, might envolve overriding a method or two in the action controller). The only thing that is missing is standardizing the client side controller code.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/15616477-112769136411908738?l=oldmoe.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://oldmoe.blogspot.com/feeds/112769136411908738/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=15616477&amp;postID=112769136411908738' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/15616477/posts/default/112769136411908738'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/15616477/posts/default/112769136411908738'/><link rel='alternate' type='text/html' href='http://oldmoe.blogspot.com/2005/09/move-view-to-browser.html' title='Move the view to the browser'/><author><name>oldmoe</name><uri>http://www.blogger.com/profile/14341721253106429644</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='24' height='32' src='http://3.bp.blogspot.com/_XFDHsTWrvBY/SsFeIEFTI_I/AAAAAAAAADU/NphzXmrghkw/S220/oldmoe.png'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-15616477.post-112750568024175982</id><published>2005-09-23T22:41:00.000+03:00</published><updated>2005-09-23T23:01:20.303+03:00</updated><title type='text'>Amr Khaled</title><content type='html'>I just saw &lt;a href="http://www.amrkhaled.net/"&gt;Amr Khaled&lt;/a&gt; on TV a moment ago. It's been a while since the last episode of his program (life makers) was aired. I really missed the man! He has a way in speaking that manages to capture me every time. Specially at this time before Ramadan. The program was being aired from Egypt which means that Amr actually manages to visit his own country from time to time. My participation in his life makers program was far beyond what I would like to do. I will try to&lt;br /&gt;be more active in the coming months. And I will try to make this Ramadan the best so far. Amr is the kind of man that is able to rally people for a good cause. He is our Gandhi! And more!. I just wish we all give him all the support we can. May Allah reward us all. Amen&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/15616477-112750568024175982?l=oldmoe.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://oldmoe.blogspot.com/feeds/112750568024175982/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=15616477&amp;postID=112750568024175982' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/15616477/posts/default/112750568024175982'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/15616477/posts/default/112750568024175982'/><link rel='alternate' type='text/html' href='http://oldmoe.blogspot.com/2005/09/amr-khaled.html' title='Amr Khaled'/><author><name>oldmoe</name><uri>http://www.blogger.com/profile/14341721253106429644</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='24' height='32' src='http://3.bp.blogspot.com/_XFDHsTWrvBY/SsFeIEFTI_I/AAAAAAAAADU/NphzXmrghkw/S220/oldmoe.png'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-15616477.post-112691064458275767</id><published>2005-09-17T00:58:00.000+03:00</published><updated>2005-09-23T00:58:56.710+03:00</updated><title type='text'>Statefull, stateless, statefull....</title><content type='html'>Over at &lt;a href="http://www.theserverside.com/"&gt;theserverside.com&lt;/a&gt; &lt;span style="font-size:100%;"&gt;Alexander Jerusalem wrote: &lt;a href="http://www.theserverside.com/news/thread.tss?thread_id=36601"&gt;Does JSF + AJAX really make sense?&lt;/a&gt; He raised some serious questions about how should the client and server interact and how could we maintain the server state when the client updates the form elements dynamically.&lt;br /&gt;&lt;br /&gt;His worries are very well founded and stem from the fact that current web application frameworks are mostly server centric. The server is playing a monoply on the data model, action control and even view state.&lt;br /&gt;&lt;br /&gt;Here's a look from the other side of the fence. I was not doing much Java development as of late and instead of using bell-and-whistle based frameworks I was participating in a big project that had the following properties:&lt;br /&gt;&lt;/span&gt;&lt;ul&gt;&lt;li&gt;Huge client base that consumed the processing power and the bandwitdh of the server(s)&lt;/li&gt;&lt;li&gt;Distributed implementation which should be done as simple as possible due to time constraints&lt;/li&gt;&lt;li&gt;Distributed data set (per user data are private and thus caching has no real system wide effect)&lt;/li&gt;&lt;li&gt;To stress the first point. Processing and bandwidth were at premium! we had to save on both.&lt;br /&gt;&lt;/li&gt;&lt;/ul&gt;Now the server centric frameworks that are all around us are far from suitable for such a beast. In fact it does not have to be a beast at all! We followed the KISS model and made ourselves a winner!&lt;br /&gt;&lt;br /&gt;Recipe for success:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Build a stateless server application (only loggedIn? should be tracked)&lt;/li&gt;&lt;li&gt;The server only responds to fine grained actions (for some actions the server responded with JSON data, for most of the action the server simply said: "ok")&lt;br /&gt;&lt;/li&gt;&lt;li&gt;Put this app on as many servers as you wish as long as they share the data and can share the loggedIn? peice of info.&lt;/li&gt;&lt;li&gt;Build a statefull SPA client:&lt;/li&gt;&lt;ul&gt;&lt;li&gt;Full model and model cache (use the cache to avoid visiting the server often)&lt;br /&gt;&lt;/li&gt;&lt;li&gt;Full fledged controller (the client decides what happens next)&lt;br /&gt;&lt;/li&gt;&lt;li&gt;Complete View rendering and management (Javascript templates any one?)&lt;/li&gt;&lt;/ul&gt;&lt;li&gt;Connect everything using Ajax and JSON (how thinner can we get?)&lt;/li&gt;&lt;li&gt;Forget about HTMLUnit (use your own Javascript infrastructure to test and direct your DOM)&lt;/li&gt;&lt;/ul&gt;What was good about such an application?&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Fast prototyping&lt;br /&gt;We managed to get a prototype (no server side thing) up and running in no time. Even during development the evolving prototype would be one step ahead by relying on static data rather than server responses&lt;/li&gt;&lt;li&gt;Prototype reuse&lt;br /&gt;The prototype actually became the application! It was continously under refactoring but the team managed to always keep it under control&lt;/li&gt;&lt;li&gt;Real Distributed Application&lt;br /&gt;The clients now do the controlling part and the parsing and rendering of view templates. They keep the view state and the model state (through the cache) only updates are propagated to the server. Much less processing on the server(s) now&lt;/li&gt;&lt;li&gt;Bandwidth Savings&lt;br /&gt;Down to 25% of the original bandwidth consumption (more savings expected) and due to decreased server load we are now able to use mod_deflate for even better savings&lt;br /&gt;&lt;/li&gt;&lt;/ul&gt;So did we find ourselves a killer framework for next generation web apps? I doubt! but I believe that we have now the optimum solution for our type of problem. Some other problems might need different solutions and might not benefit from such an approach. But to see Alexander's words in another view: Yes sometimes Ajax and JSF dont co-exist. Sometimes you'll need to drop JSF for Ajax to work the way you want!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/15616477-112691064458275767?l=oldmoe.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://oldmoe.blogspot.com/feeds/112691064458275767/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=15616477&amp;postID=112691064458275767' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/15616477/posts/default/112691064458275767'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/15616477/posts/default/112691064458275767'/><link rel='alternate' type='text/html' href='http://oldmoe.blogspot.com/2005/09/statefull-stateless-statefull.html' title='Statefull, stateless, statefull....'/><author><name>oldmoe</name><uri>http://www.blogger.com/profile/14341721253106429644</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='24' height='32' src='http://3.bp.blogspot.com/_XFDHsTWrvBY/SsFeIEFTI_I/AAAAAAAAADU/NphzXmrghkw/S220/oldmoe.png'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-15616477.post-112673199515762925</id><published>2005-09-14T23:32:00.000+03:00</published><updated>2005-09-15T00:15:51.836+03:00</updated><title type='text'>What's wrong with AJAX guides?</title><content type='html'>Most Ajax guides available online would tell you how Ajax is our way to the asynchronus client programming. Follow that with an example of how to build Ajax requests with details upto callback management. The poor reader will grab the code and paste it in his/her application and will find it working. After his/her application grows things will start to miss behave. What went wrong? The poor fellow who had little to no knowledge of Javascript (and may be shunned it off earlier as a languange for those who can't do real programming) didnt give the script a good look trying to figure out what's going on first.&lt;br /&gt;&lt;br /&gt;Here's an example from developer.apple.com :&lt;br /&gt;&lt;br /&gt;&lt;pre  style="border: 1px solid ; padding: 3px; background-color: rgb(238, 238, 255);font-family:trebuchet ms;"&gt;&lt;span style="font-size:100%;"&gt;var req;&lt;br /&gt;&lt;br /&gt;function loadXMLDoc(url) {&lt;br /&gt; req = false;&lt;br /&gt;// branch for native XMLHttpRequest object&lt;br /&gt;if(window.XMLHttpRequest) {&lt;br /&gt; try {&lt;br /&gt;   req = new XMLHttpRequest();&lt;br /&gt;    } catch(e) {&lt;br /&gt;   req = false;&lt;br /&gt;    }&lt;br /&gt;// branch for IE/Windows ActiveX version&lt;br /&gt;} else if(window.ActiveXObject) {&lt;br /&gt;    try {&lt;br /&gt;     req = new ActiveXObject("Msxml2.XMLHTTP");&lt;br /&gt;   } catch(e) {&lt;br /&gt;     try {&lt;br /&gt;        req = new ActiveXObject("Microsoft.XMLHTTP");&lt;br /&gt;     } catch(e) {&lt;br /&gt;        req = false;&lt;br /&gt;     }&lt;br /&gt;  }&lt;br /&gt;}&lt;br /&gt; if(req) {&lt;br /&gt;  req.onreadystatechange = processReqChange;&lt;br /&gt;  req.open("GET", url, true);&lt;br /&gt;  req.send("");&lt;br /&gt; }&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;function processReqChange() {&lt;br /&gt; // only if req shows "loaded"&lt;br /&gt; if (req.readyState == 4) {&lt;br /&gt;     // only if "OK"&lt;br /&gt;     if (req.status == 200) {&lt;br /&gt;         // ...processing statements go here...&lt;br /&gt;     } else {&lt;br /&gt;         alert("There was a problem retrieving the XML data:\n" +&lt;br /&gt;             req.statusText);&lt;br /&gt;     }&lt;br /&gt; }&lt;br /&gt;}&lt;/span&gt;&lt;/pre&gt;What's the problem with the above code? specially in an asynchronus environment?&lt;br /&gt;&lt;br /&gt;The variable req (which is used as the XMLHTTPRequest object) is defined globally!! So when our friend tries to instantiate a new request before the current one finishes it will overwrite it and you will lose any reference to the old request.&lt;br /&gt;&lt;br /&gt;But why did we have a global variable in the first place? If it's so bad, why didnt we make it local tothe loadXMLDoc funciton?&lt;br /&gt;&lt;br /&gt;Simple, because the call back function needs to access this variable. And if it cannot be found in its scope it will look at it in the global scope.&lt;br /&gt;&lt;br /&gt;Closures any one?&lt;br /&gt;&lt;br /&gt;this can easily be solved by defining the call back function as an inner function to the loadXMLDoc() function and declaring the variable req as local to this function&lt;br /&gt;&lt;pre  style="border: 1px solid ; padding: 3px; background-color: rgb(238, 238, 255);font-family:trebuchet ms;"&gt;&lt;span style="font-size:100%;"&gt;function loadXMLDoc(url) {&lt;br /&gt;&lt;span style="font-style: italic; font-weight: bold;"&gt;var&lt;/span&gt; req = false;&lt;br /&gt;// branch for native XMLHttpRequest object&lt;br /&gt;if(window.XMLHttpRequest) {&lt;br /&gt; try {&lt;br /&gt;   req = new XMLHttpRequest();&lt;br /&gt;    } catch(e) {&lt;br /&gt;   req = false;&lt;br /&gt;    }&lt;br /&gt;// branch for IE/Windows ActiveX version&lt;br /&gt;} else if(window.ActiveXObject) {&lt;br /&gt;    try {&lt;br /&gt;     req = new ActiveXObject("Msxml2.XMLHTTP");&lt;br /&gt;   } catch(e) {&lt;br /&gt;     try {&lt;br /&gt;        req = new ActiveXObject("Microsoft.XMLHTTP");&lt;br /&gt;     } catch(e) {&lt;br /&gt;        req = false;&lt;br /&gt;     }&lt;br /&gt;  }&lt;br /&gt;}&lt;br /&gt; if(req) {&lt;br /&gt;  req.onreadystatechange = processReqChange;&lt;br /&gt;  req.open("GET", url, true);&lt;br /&gt;  req.send("");&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; &lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-size:100%;"&gt;&lt;span style="font-style: italic; font-weight: bold;"&gt;var processReqChange = function() {&lt;/span&gt;&lt;br /&gt;&lt;span style="font-style: italic; font-weight: bold;"&gt;    // only if req shows "loaded"&lt;/span&gt;&lt;br /&gt;&lt;span style="font-style: italic; font-weight: bold;"&gt;  if (req.readyState == 4) {&lt;/span&gt;&lt;br /&gt;&lt;span style="font-style: italic; font-weight: bold;"&gt;        // only if "OK"&lt;/span&gt;&lt;br /&gt;&lt;span style="font-style: italic; font-weight: bold;"&gt;         if (req.status == 200) {&lt;/span&gt;&lt;br /&gt;&lt;span style="font-style: italic; font-weight: bold;"&gt;          // ...processing statements go here...&lt;/span&gt;&lt;br /&gt;&lt;span style="font-style: italic; font-weight: bold;"&gt;         } else {&lt;/span&gt;&lt;br /&gt;&lt;span style="font-style: italic; font-weight: bold;"&gt;            alert("There was a problem...");&lt;/span&gt;&lt;br /&gt;&lt;span style="font-style: italic; font-weight: bold;"&gt;   }&lt;/span&gt;&lt;br /&gt;&lt;span style="font-style: italic; font-weight: bold;"&gt;    }&lt;/span&gt;&lt;br /&gt;&lt;span style="font-style: italic; font-weight: bold;"&gt; }&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt; &lt;span style="font-size:100%;"&gt;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;&lt;/pre&gt;All we needed to do is declare req as a local variable by adding the var keyword and removing the global declaration.&lt;br /&gt;&lt;br /&gt;This way each request will spawn a new req object which will be accessed by the callback function and will be garbage collected after the callback function returns (some browsers - namely older versions of internet explorer - might leak memory here as they deal very badly with closures)&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/15616477-112673199515762925?l=oldmoe.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://oldmoe.blogspot.com/feeds/112673199515762925/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=15616477&amp;postID=112673199515762925' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/15616477/posts/default/112673199515762925'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/15616477/posts/default/112673199515762925'/><link rel='alternate' type='text/html' href='http://oldmoe.blogspot.com/2005/09/whats-wrong-with-ajax-guides.html' title='What&apos;s wrong with AJAX guides?'/><author><name>oldmoe</name><uri>http://www.blogger.com/profile/14341721253106429644</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='24' height='32' src='http://3.bp.blogspot.com/_XFDHsTWrvBY/SsFeIEFTI_I/AAAAAAAAADU/NphzXmrghkw/S220/oldmoe.png'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-15616477.post-112652314404173833</id><published>2005-09-12T14:00:00.000+03:00</published><updated>2005-09-12T15:34:41.253+03:00</updated><title type='text'>Mubarak won!</title><content type='html'>Great news for all the national party members. Mubarak won the elections with a staggering 88.5%!.&lt;br /&gt;The Egyptians voiced their opinion! and what I conclude for it is :&lt;br /&gt;&lt;br /&gt;&lt;ol&gt;   &lt;li&gt;They dont care for the 19000 wrongfully imprisoned fellow Egyptians&lt;/li&gt;   &lt;li&gt;They dont care for being poisoned with illegal pesticides&lt;/li&gt;   &lt;li&gt;They dont care if their childern recieve no education at all&lt;br /&gt;  &lt;/li&gt; &lt;/ol&gt;&lt;br /&gt;which calls for an interesting question, what do the Egyptians care for?&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/15616477-112652314404173833?l=oldmoe.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://oldmoe.blogspot.com/feeds/112652314404173833/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=15616477&amp;postID=112652314404173833' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/15616477/posts/default/112652314404173833'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/15616477/posts/default/112652314404173833'/><link rel='alternate' type='text/html' href='http://oldmoe.blogspot.com/2005/09/mubarak-won.html' title='Mubarak won!'/><author><name>oldmoe</name><uri>http://www.blogger.com/profile/14341721253106429644</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='24' height='32' src='http://3.bp.blogspot.com/_XFDHsTWrvBY/SsFeIEFTI_I/AAAAAAAAADU/NphzXmrghkw/S220/oldmoe.png'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-15616477.post-112501138812440775</id><published>2005-08-26T00:52:00.000+03:00</published><updated>2005-08-26T02:10:29.733+03:00</updated><title type='text'>Going Home</title><content type='html'>Another day (and night?) at work. It is 2 A.M. now and still too much left to do. If not for Sheikh Abdel Basset's sweet recitation of the Holy Qur'an I doubt we could handle the stress!. Any way, I must head home now. Tomorrow is (yet) another day insha'Allah.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/15616477-112501138812440775?l=oldmoe.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://oldmoe.blogspot.com/feeds/112501138812440775/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=15616477&amp;postID=112501138812440775' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/15616477/posts/default/112501138812440775'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/15616477/posts/default/112501138812440775'/><link rel='alternate' type='text/html' href='http://oldmoe.blogspot.com/2005/08/going-home.html' title='Going Home'/><author><name>oldmoe</name><uri>http://www.blogger.com/profile/14341721253106429644</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='24' height='32' src='http://3.bp.blogspot.com/_XFDHsTWrvBY/SsFeIEFTI_I/AAAAAAAAADU/NphzXmrghkw/S220/oldmoe.png'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-15616477.post-112500667955095553</id><published>2005-08-26T00:41:00.000+03:00</published><updated>2005-08-26T00:51:19.553+03:00</updated><title type='text'>And more deadlines!</title><content type='html'>Seems the Ajax fever is spreading around. Today I had a meeting with a high profile client that was negotiating building an Ajax prototype to be demonstrated in Gitex!. They were enthusiastic and were talking about the possibilities and I was thinking to myself: "O Allah! no more deadlines!!". Best thing is that they were talking about a 3 weeks deadline which made my refusal much easier!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/15616477-112500667955095553?l=oldmoe.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://oldmoe.blogspot.com/feeds/112500667955095553/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=15616477&amp;postID=112500667955095553' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/15616477/posts/default/112500667955095553'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/15616477/posts/default/112500667955095553'/><link rel='alternate' type='text/html' href='http://oldmoe.blogspot.com/2005/08/and-more-deadlines.html' title='And more deadlines!'/><author><name>oldmoe</name><uri>http://www.blogger.com/profile/14341721253106429644</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='24' height='32' src='http://3.bp.blogspot.com/_XFDHsTWrvBY/SsFeIEFTI_I/AAAAAAAAADU/NphzXmrghkw/S220/oldmoe.png'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-15616477.post-112491273240857455</id><published>2005-08-24T22:30:00.000+03:00</published><updated>2005-08-24T22:45:32.413+03:00</updated><title type='text'>Deadlines.. oh deadlines!</title><content type='html'>Can we live without deadlines? or a better question, how many did lines a man can handle concurrently? I have like 6 or 7 deadlines to achieve these days. 4 or so at work plus my masters and other family commitments. I wonder how some people manage to do so many things in so little time!. I wish i will find a clue one day. The sad thing is that i hardly have time to follow the drama in the Egyptian political scene. The national party guys  and their leader (Mubarak) are trying to convince us that he can undo in 6 years the damage he made the previous 24 years. I wonder if the guy even considers honoring his deadlines!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/15616477-112491273240857455?l=oldmoe.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://oldmoe.blogspot.com/feeds/112491273240857455/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=15616477&amp;postID=112491273240857455' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/15616477/posts/default/112491273240857455'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/15616477/posts/default/112491273240857455'/><link rel='alternate' type='text/html' href='http://oldmoe.blogspot.com/2005/08/deadlines-oh-deadlines.html' title='Deadlines.. oh deadlines!'/><author><name>oldmoe</name><uri>http://www.blogger.com/profile/14341721253106429644</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='24' height='32' src='http://3.bp.blogspot.com/_XFDHsTWrvBY/SsFeIEFTI_I/AAAAAAAAADU/NphzXmrghkw/S220/oldmoe.png'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-15616477.post-112466537260036263</id><published>2005-08-22T01:48:00.000+03:00</published><updated>2005-08-22T02:06:54.500+03:00</updated><title type='text'>Javascript, Ajax  and asynchronus stuff</title><content type='html'>I've been doing lots of ajax as of late. At first my thought was: "been there, seen that!" as i already did lots of asynchronus stuff (using hidden iframes). But i'm totally sold now. Here's why:&lt;br /&gt;&lt;br /&gt;&lt;ol&gt;   &lt;li&gt;While using iframes I had to manage my call backs from within the returning page (i.e. server output needed to know about the client side control structure, and needed to resend some post operations data other than a mere success or failure message). Now it's totally client side! Using callback functions (with their closures! you dont get to lose the request initiating environment!!) is so very sweet to work with!&lt;/li&gt;   &lt;li&gt;Handling server misbehaviour!. You dont rely on the iframe to always load any more, you can easily detect when a request times out (without writing your special timing routines for that!)&lt;/li&gt;   &lt;li&gt;All you communicate is data now. And I even dont use XML, &lt;a href="http://www.json.org/"&gt;JSON&lt;/a&gt; is my winning card here and it integrates oh so easy with the front end logic (our back end code is written in C, thanks for whoever wrote libjson, a life saver for us).&lt;/li&gt; &lt;/ol&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/15616477-112466537260036263?l=oldmoe.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://oldmoe.blogspot.com/feeds/112466537260036263/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=15616477&amp;postID=112466537260036263' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/15616477/posts/default/112466537260036263'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/15616477/posts/default/112466537260036263'/><link rel='alternate' type='text/html' href='http://oldmoe.blogspot.com/2005/08/javascript-ajax-and-asynchronus-stuff.html' title='Javascript, Ajax  and asynchronus stuff'/><author><name>oldmoe</name><uri>http://www.blogger.com/profile/14341721253106429644</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='24' height='32' src='http://3.bp.blogspot.com/_XFDHsTWrvBY/SsFeIEFTI_I/AAAAAAAAADU/NphzXmrghkw/S220/oldmoe.png'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-15616477.post-112466428138835480</id><published>2005-08-22T01:35:00.000+03:00</published><updated>2005-08-22T03:13:11.413+03:00</updated><title type='text'>Presedential elections &amp; national press</title><content type='html'>Our national (operated by the governoment) press went hectic these days. Every body and his mother is waving flags for Mubarak and his party. What makes the joke so ironic is that while they praise the president (shame on them) the cut the other competitors down to size (shame on them again). The funniest thing was the so called "Resala" which is released by "Al-Ahram" newspaper (the largest in Egypt). It is posting daily that the competitors' wives are against their running for presedency.! Why dont we hear the opinion of Suzan Mubarak then? or the journalists dare not speak to the first lady?&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/15616477-112466428138835480?l=oldmoe.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://oldmoe.blogspot.com/feeds/112466428138835480/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=15616477&amp;postID=112466428138835480' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/15616477/posts/default/112466428138835480'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/15616477/posts/default/112466428138835480'/><link rel='alternate' type='text/html' href='http://oldmoe.blogspot.com/2005/08/presedential-elections-national-press.html' title='Presedential elections &amp; national press'/><author><name>oldmoe</name><uri>http://www.blogger.com/profile/14341721253106429644</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='24' height='32' src='http://3.bp.blogspot.com/_XFDHsTWrvBY/SsFeIEFTI_I/AAAAAAAAADU/NphzXmrghkw/S220/oldmoe.png'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-15616477.post-112455688588508611</id><published>2005-08-20T19:53:00.000+03:00</published><updated>2005-08-22T01:47:11.256+03:00</updated><title type='text'>Hello World!</title><content type='html'>Hello World. This is my first encounter with blogging. Let's see if it will prove useful .&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/15616477-112455688588508611?l=oldmoe.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://oldmoe.blogspot.com/feeds/112455688588508611/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=15616477&amp;postID=112455688588508611' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/15616477/posts/default/112455688588508611'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/15616477/posts/default/112455688588508611'/><link rel='alternate' type='text/html' href='http://oldmoe.blogspot.com/2005/08/hello-world.html' title='Hello World!'/><author><name>oldmoe</name><uri>http://www.blogger.com/profile/14341721253106429644</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='24' height='32' src='http://3.bp.blogspot.com/_XFDHsTWrvBY/SsFeIEFTI_I/AAAAAAAAADU/NphzXmrghkw/S220/oldmoe.png'/></author><thr:total>0</thr:total></entry></feed>
