<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" media="screen" href="/~d/styles/rss2full.xsl"?><?xml-stylesheet type="text/css" media="screen" href="http://feeds.feedburner.com/~d/styles/itemcontent.css"?><rss xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:sy="http://purl.org/rss/1.0/modules/syndication/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/" xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0" version="2.0">

<channel>
	<title>Engine Yard Blog</title>
	
	<link>http://www.engineyard.com/blog</link>
	<description />
	<lastBuildDate>Fri, 06 Nov 2009 22:00:30 +0000</lastBuildDate>
	<generator>http://wordpress.org/?v=2.8.4</generator>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
			<atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="self" href="http://www.engineyard.com/feed/" type="application/rss+xml" /><feedburner:feedFlare href="http://add.my.yahoo.com/rss?url=http%3A%2F%2Fwww.engineyard.com%2Ffeed%2F" src="http://us.i1.yimg.com/us.yimg.com/i/us/my/addtomyyahoo4.gif">Subscribe with My Yahoo!</feedburner:feedFlare><feedburner:feedFlare href="http://www.newsgator.com/ngs/subscriber/subext.aspx?url=http%3A%2F%2Fwww.engineyard.com%2Ffeed%2F" src="http://www.newsgator.com/images/ngsub1.gif">Subscribe with NewsGator</feedburner:feedFlare><feedburner:feedFlare href="http://feeds.my.aol.com/add.jsp?url=http%3A%2F%2Fwww.engineyard.com%2Ffeed%2F" src="http://o.aolcdn.com/favorites.my.aol.com/webmaster/ffclient/webroot/locale/en-US/images/myAOLButtonSmall.gif">Subscribe with My AOL</feedburner:feedFlare><feedburner:feedFlare href="http://www.bloglines.com/sub/http://www.engineyard.com/feed/" src="http://www.bloglines.com/images/sub_modern11.gif">Subscribe with Bloglines</feedburner:feedFlare><feedburner:feedFlare href="http://www.netvibes.com/subscribe.php?url=http%3A%2F%2Fwww.engineyard.com%2Ffeed%2F" src="http://www.netvibes.com/img/add2netvibes.gif">Subscribe with Netvibes</feedburner:feedFlare><feedburner:feedFlare href="http://fusion.google.com/add?feedurl=http%3A%2F%2Fwww.engineyard.com%2Ffeed%2F" src="http://buttons.googlesyndication.com/fusion/add.gif">Subscribe with Google</feedburner:feedFlare><feedburner:feedFlare href="http://www.pageflakes.com/subscribe.aspx?url=http%3A%2F%2Fwww.engineyard.com%2Ffeed%2F" src="http://www.pageflakes.com/ImageFile.ashx?instanceId=Static_4&amp;fileName=ATP_blu_91x17.gif">Subscribe with Pageflakes</feedburner:feedFlare><feedburner:feedFlare href="http://www.plusmo.com/add?url=http%3A%2F%2Fwww.engineyard.com%2Ffeed%2F" src="http://plusmo.com/res/graphics/fbplusmo.gif">Subscribe with Plusmo</feedburner:feedFlare><feedburner:feedFlare href="http://www.thefreedictionary.com/_/hp/AddRSS.aspx?http%3A%2F%2Fwww.engineyard.com%2Ffeed%2F" src="http://img.tfd.com/hp/addToTheFreeDictionary.gif">Subscribe with The Free Dictionary</feedburner:feedFlare><feedburner:feedFlare href="http://www.bitty.com/manual/?contenttype=rssfeed&amp;contentvalue=http%3A%2F%2Fwww.engineyard.com%2Ffeed%2F" src="http://www.bitty.com/img/bittychicklet_91x17.gif">Subscribe with Bitty Browser</feedburner:feedFlare><feedburner:feedFlare href="http://www.newsalloy.com/?rss=http%3A%2F%2Fwww.engineyard.com%2Ffeed%2F" src="http://www.newsalloy.com/subrss3.gif">Subscribe with NewsAlloy</feedburner:feedFlare><feedburner:feedFlare href="http://www.live.com/?add=http%3A%2F%2Fwww.engineyard.com%2Ffeed%2F" src="http://tkfiles.storage.msn.com/x1piYkpqHC_35nIp1gLE68-wvzLZO8iXl_JMledmJQXP-XTBOLfmQv4zhj4MhcWEJh_GtoBIiAl1Mjh-ndp9k47If7hTaFno0mxW9_i3p_5qQw">Subscribe with Live.com</feedburner:feedFlare><feedburner:feedFlare href="http://mix.excite.eu/add?feedurl=http%3A%2F%2Fwww.engineyard.com%2Ffeed%2F" src="http://image.excite.co.uk/mix/addtomix.gif">Subscribe with Excite MIX</feedburner:feedFlare><feedburner:feedFlare href="http://download.attensa.com/app/get_attensa.html?feedurl=http%3A%2F%2Fwww.engineyard.com%2Ffeed%2F" src="http://www.attensa.com/blogs/attensa/WindowsLiveWriter/BadgeredintoBadges_10C02/attensa_feed_button5.gif">Subscribe with Attensa for Outlook</feedburner:feedFlare><feedburner:feedFlare href="http://www.webwag.com/wwgthis.php?url=http%3A%2F%2Fwww.engineyard.com%2Ffeed%2F" src="http://www.webwag.com/images/wwgthis.gif">Subscribe with Webwag</feedburner:feedFlare><feedburner:feedFlare href="http://www.podcastready.com/oneclick_bookmark.php?url=http%3A%2F%2Fwww.engineyard.com%2Ffeed%2F" src="http://www.podcastready.com/images/podcastready_button.gif">Subscribe with Podcast Ready</feedburner:feedFlare><feedburner:feedFlare href="http://www.flurry.com/pushRssFeed.do?r=fb&amp;url=http%3A%2F%2Fwww.engineyard.com%2Ffeed%2F" src="http://www.flurry.com/images/flurry_rss_logo2.gif">Subscribe with Flurry</feedburner:feedFlare><feedburner:feedFlare href="http://www.wikio.com/subscribe?url=http%3A%2F%2Fwww.engineyard.com%2Ffeed%2F" src="http://www.wikio.com/shared/img/add2wikio.gif">Subscribe with Wikio</feedburner:feedFlare><feedburner:feedFlare href="http://www.dailyrotation.com/index.php?feed=http%3A%2F%2Fwww.engineyard.com%2Ffeed%2F" src="http://www.dailyrotation.com/rss-dr2.gif">Subscribe with Daily Rotation</feedburner:feedFlare><atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="hub" href="http://pubsubhubbub.appspot.com" /><item>
		<title>Rails Roadshow Recap</title>
		<link>http://feedproxy.google.com/~r/engineyard/~3/67u-udqFY7g/</link>
		<comments>http://www.engineyard.com/blog/2009/rails-roadshow-recap/#comments</comments>
		<pubDate>Thu, 05 Nov 2009 22:05:11 +0000</pubDate>
		<dc:creator>Abheek Anand</dc:creator>
				<category><![CDATA[Cloud]]></category>
		<category><![CDATA[Events]]></category>

		<guid isPermaLink="false">http://www.engineyard.com/blog/?p=2788</guid>
		<description><![CDATA[It's been a whirlwind few weeks, but the Rails Performance in the Cloud Roadshow is complete, and we're all back home! We had a great time getting out and meeting customers and prospects, and, of course, talking about cloud performance. If you missed it, or just want a refresher, the three Engine Yard slide decks are now available.

Attendee James McElhiney beat me to the punch with his review, but I still wanted to take a few minutes to talk about how things went.]]></description>
			<content:encoded><![CDATA[<p>It&#8217;s been a whirlwind few weeks, but the Rails Performance in the Cloud Roadshow is complete, and we&#8217;re all back home! We had a great time getting out and meeting customers and prospects, and, of course, talking about cloud performance. If you missed it, or just want a refresher, <a href="http://www.engineyard.com/community/railsroadshow">the three Engine Yard slide decks are now available</a>.</p>
<p>Attendee <a href="http://www.bitwelder.com/?p=21">James McElhiney</a> beat me to the punch with his review, but I still wanted to take a few minutes to talk about how things went.</p>
<p>We started with Boston, and then moved to Chicago, Austin, Los Angeles and Seattle. Our partners Amazon Web Services, CVSDude, New Relic and Soasta joined us for the trip, meeting with various customers, developers and managers. The crowd was varied,  highly technical, and despite the early-morning start, had great energy.</p>
<p>The Roadshow was all about performance, and between the presentations and audience questions, we covered close to everything you&#8217;d need to know to meet and exceed your performance goals. Some specifics:</p>
<p>Tom Mornini and I presented Engine Yard&#8217;s value proposition, along with a live demo of the <a href="http://www.engineyard.com/products/cloud">Engine Yard Cloud</a> platform. Tom focused on end-user performance and the very significant impact it can have on business metrics, while I went into detail on some performance metrics that should be relevant to all rails developers, highlighting how Engine Yard Cloud can help users scale in the cloud.</p>
<p>Next <a href="http://newrelic.com/">New Relic</a> walked us through RPM, and how they use it internally to monitor, well, RPM. Self-referential coolness aside, it was a great way to see how a tool can help you identify the bottlenecks in your application, while still remaining easy to setup and maintain. When the first audience question started with &#8220;I&#8217;m signing up for your service even as I ask this&#8230;&#8221; we knew things were on the right track.</p>
<p><a href="http://cvsdude.com/">CVSDude</a>&#8217;s Willie Wang presented on enterprise-grade source control management, and how their solutions can help achieve agility in deployments. This is a key component of performance optimization—agile deployments enable us to fix performance problems faster and deliver features to customers more effectively. With Git support in their near-term roadmap, CVSDude was a great addition for our Rails audience.</p>
<p>Many of the attendees were customers of Amazon AWS, and Mike Culver&#8217;s presentations described the value proposition of AWS very well. The Amazon team had a busy week—with newly announced <a href="http://aws.typepad.com/aws/2009/10/introducing-rds-the-amazon-relational-database-service-.html">services</a>, <a href="http://aws.typepad.com/aws/2009/10/two-new-ec2-instance-types-additional-memory.html">instance types</a> and a <a href="http://aws.typepad.com/aws/2009/10/amazon-ec2-now-an-even-better-value.html">price reduction</a> (which we at Engine Yard immediately <a href="http://www.engineyard.com/blog/2009/announcement-engine-yard-cloud-price-reduction/">passed along to our customers</a>). As expected, a lot of the questions were around their new announcements, and Mike explained how each of these increased the value proposition of cloud computing even more.</p>
<p>Finally, <a href="http://soasta.com/">Soasta</a>&#8217;s Dave Murphy had a great presentation on load testing using Soasta Cloud Test. Some of his numbers were particularly impressive—seeing them use cloud infrastructure to deploy multi-million user load tests on live production environments really brought home the value of using the cloud model. Tools like CloudTest, in conjunction with Engine Yard, can make it trivial for users to test deployments that would have been impossible to test before.</p>
<p>For me personally, the roadshow was a great way to meet customers, developers and spend cycles understanding their needs better. The biggest takeaway for me was that customers really care about scalability, but realize that they add most value by focusing on user happiness. This was exactly the philosophy we used while designing Engine Yard Cloud—we want you to continue to focus on features that add value to your products, and leave all your scalability, performance and agility needs to the Engine Yard Cloud platform.</p>
<p>If you missed the Roadshow, be sure to register for our upcoming <a href="http://engineyard.cmail3.com/t/y/l/uydkdh/kjiythgi/r">Engine Yard Cloud Demo Webinar</a>; I&#8217;ll be walking through some of the topics covered at the Roadshow and taking questions.</p>
<p>We look forward to visiting more cities soon. Until then, enjoy the presentations, and send us your comments and feedback!</p>
<img src="http://www.engineyard.com/blog/?ak_action=api_record_view&id=2788&type=feed" alt="" /><img src="http://feeds.feedburner.com/~r/engineyard/~4/67u-udqFY7g" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://www.engineyard.com/blog/2009/rails-roadshow-recap/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		<feedburner:origLink>http://www.engineyard.com/blog/2009/rails-roadshow-recap/</feedburner:origLink></item>
		<item>
		<title>Win a Motorola DROID Programming Contest: “Worst App Server Technology Ever”</title>
		<link>http://feedproxy.google.com/~r/engineyard/~3/eWWKRwy-OAY/</link>
		<comments>http://www.engineyard.com/blog/2009/win-a-motorola-droid-programming-contest-worst-app-server-technology-ever/#comments</comments>
		<pubDate>Tue, 03 Nov 2009 15:30:26 +0000</pubDate>
		<dc:creator>Michael Mullany</dc:creator>
				<category><![CDATA[Contests]]></category>
		<category><![CDATA[actors]]></category>
		<category><![CDATA[contest]]></category>
		<category><![CDATA[dataflow]]></category>
		<category><![CDATA[twitter]]></category>

		<guid isPermaLink="false">http://www.engineyard.com/blog/?p=2710</guid>
		<description><![CDATA[The goal of this contest is to collaborate with your other contestants to build the &#8220;worst app server ever&#8221; (WASE) , and use it to complete one or more challenge computations. The challenge computation(s) and their input data-set(s) will be announced and posted next week on Thursday, November 12. The contest will remain open until [...]]]></description>
			<content:encoded><![CDATA[<p>The goal of this contest is to collaborate with your other contestants to build the &#8220;worst app server ever&#8221; (WASE) , and use it to complete one or more challenge computations. The challenge computation(s) and their input data-set(s) will be announced and posted next week on Thursday, November 12. The contest will remain open until Monday, November 16th at 6pm PST.  Winners will be announced within the following week.</p>
<p>(<span style="color: #ff9900;">Update: </span>We think the rule-set below is now complete, but we still welcome any suggestions or tweaks that you might have.)</p>
<p>There will be three prizes.</p>
<ul>
<li>The first prize (a Motorola DROID and $1,000 of Engine Yard Cloud credit) goes to the person who completes our challenge task correctly first.</li>
<li>Second prize (a DROID and $500 of cloud credit) goes to the person who builds the most popular WASE endpoint (the one used the most often in the most submissions).</li>
<li>Third prize (a DROID) goes to the &#8220;best&#8221; WASE endpoint written in Ruby (as determined by us). The contest DROIDS are full price, non-contract-linked, US models.</li>
</ul>
<h2>How WASE Works</h2>
<p>Why is WASE the worlds worst app server technology? Well, instead of a sane message bus like AMQP, WASE uses Twitter as its message bus. Instead of a proper message router, WASE uses a list of twitter accounts as its program listing. And instead of encapsulating data with each message, WASE messages only contain a reference to JSON objects or arrays at input and output location(s) specified by a bit.ly.</p>
<h2>&#8220;This Sounds Like the World&#8217;s Worst App Server. Tell Me More.&#8221;</h2>
<p>Well here is an example of how a sample computation might work. Let&#8217;s say @engineyard wants to take a JSON file containing an array of names, and get a list of  the top quartile of names after sorting the array. We know that there are two Twitter accounts (which we will henceforth call WASEpoints) @ey-sort and @ey-firsthalf that can be useful to us. We know @ey-sort takes an input data set, sorts it and outputs the result. We also know that @ey-firsthalf takes an input data set, and outputs the first &#8220;half&#8221; of the dataset. To perform a computation, we set up URI&#8217;s for the program listing, the input data and the output data, and kick off the computation with an appropriate message. (For those of you with dataflow or actor-based programming experience, WASE should look like a vague, but disreputable cousin.)</p>
<p>So let&#8217;s go through the message flow:</p>
<p>We&#8217;ll put our program listing at:</p>
<p>www.engineyard.com/top-quartile-sorted-list.json,  (bit.ly/7yQK6) whose body contents are a JSON array:</p>
<p>["@ey-sort", "@ey-firsthalf", "@ey-firsthalf", "@engineyard"]</p>
<p>We put our input data here:  www.engineyard.com/unsortedmegalist.json (bit.ly/3kl0xs)</p>
<p>And set up a location for our output data here:  www.engineyard.com/top25percentofmymegalist.json (bit.ly/2uhGcl)</p>
<p>Or to summarize the <a href="http://code.google.com/p/bitly-api/wiki/ApiDocumentation">bit.ly&#8217;s</a>,</p>
<p>Program listing: bit.ly/7yQK6 (read with a http: GET)</p>
<p>Output location: bit.ly/2uhGcl (written with a http: PUT)</p>
<p>Input location: bit.ly/3kl0xs (read with a http: GET)</p>
<p>To perform the computation, we&#8217;d simply send the following twitter message from our @engineyard account: @ey-sort #wase, 0, bit.ly/7yQK6, 1256850843, bit.ly/2uhGcl, bit.ly/3kl0xs</p>
<p>So the message format of a WASTE message is: [WASEpoint], [WASE hashtag] [Program Counter (0 initially)], [Program listing URI], [<a href="http://www.unixtimestamp.com/index.php">Unix Timestamp</a>], [Output URI] [,Input URI (optional)] [, Input URI 2 (optional)]</p>
<p>In the case of this computation, the message and computation sequence would look like:</p>
<ul>
<li>@engineyard sends: &#8220;@ey-sort #wase, 0, bit.ly/7yQK6, 1256850843, bit.ly/2uhGcl, bit.ly/3kl0xs&#8221;</li>
</ul>
<p>&#8230;. @ey-sort reads the message from @engineyard in its twitter list and parses the message. First it GETs the program listing from bit.ly/7yQK6, GETS the input data set from bit.ly/3kl0xs, sorts it, PUTS the output to bit.ly/2uhGcl, then looks for the 0+1 WASEpoint in the program listing (@ey-sort) and then..</p>
<ul>
<li>@ey-sort sends: &#8220;@ey-firsthalf #wase, 1, bit.ly/7yQK6, 1256850875, bit.ly/2uhGcl&#8221;</li>
</ul>
<p>&#8230;. @ey-firsthalf reads the message from @ey-sort in its twitter list and parses the message. First it GETs the program listing from bit.ly/7yQK6, GETS the input data set from bit.ly/2uhGcl, halves it, then PUTS the output to bit.ly/2uhGcl,  looks for the 1+1 WASEpoint in the program listing (@ey-firsthalf) and then..</p>
<ul>
<li>@ey-firsthalf sends (to itself): &#8220;@ey-firsthalf #wase, 2, bit.ly/7yQK6, 1256850885, bit.ly/2uhGcl&#8221;</li>
</ul>
<p>&#8230; etc. &#8230;</p>
<ul>
<li>@ey-firsthalf sends: &#8220;@engineyard #wase, 3, bit.ly/7yQK6, 1256850899, bit.ly/2uhGcl&#8221;</li>
</ul>
<p>&#8212; finally @engineyard receives this message with the pointer to the final location of output data.</p>
<p>A few new things here. There&#8217;s a program counter that tells the WASEpoint where in the program listing the computation is, and there&#8217;s a Unix timestamp (could be useful for discarding messages that get held up in the twitterverse?). If no input URI is specified, then the WASEpoint should use the Output URI as both Input and Output locations. One restriction that we will enforce for the contest is that a WASEPOINT MAY NOT DECREMENT A PROGRAM COUNTER: to avoid infinite looping. (<span style="color: #ff9900;">Update: </span>And a WASEpoint must conserve the program listing and output URI&#8217;s from the input to the output message.)</p>
<p>Hey, maybe we should have some basic error handling. Hmm, let&#8217;s say @ey-firsthalf is expecting a standard JSON object but the input data fails to parse properly. Let&#8217;s have it send the following message:</p>
<ul>
<li>@ey-firsthalf sends: &#8220;@engineyard, #wase, -1, bit.ly/7yQK6, 1256850885, bit.ly/2uhGcl</li>
</ul>
<p>So the error message structure is: [<span style="color: #ff9900;">Update: </span>first WASEpoint in program listing], [WASE hashtag], [Negative of Program Counter], [Program listing URI], [Unix Timestamp], [Output URI] [, Input URI (optional)] [, Input URI 2 (optional)]</p>
<p>Note that there are no type declarations in the message format because the only data-type supported by WASE are JSON objects and arrays.</p>
<h2>What are Guidelines for the Contest?</h2>
<p>Apart from the message format and data guidelines above &#8212; here are additional guidelines:</p>
<p>1. Each contestant may register no more than 5 WASE endpoints/twitter accounts. WASEpoints must be registered here: www.engineyard.com/contests/waste/ to be eligible for use. Your WASEpoints must follow @eycontest. This is also where you should go to pick and choose good WASEpoints for constructing your app. Each contest entry must use a minimum of 10 WASEpoints from at least four separate contestants, where each WASEpoints performs functionally significant data operations.</p>
<p>2. Please do not submit WASEpoints whose twitter accounts you do not own :-) We do not want to encourage the business of spamming Ashton Kutcher with mysterious messages. We will test each submitted WASEpoint with a DM to make sure they are legitimate.</p>
<p>3. Source code for your WASEpoint must be posted to a public repository (e.g codaset, github, sourceforge, kenai) for other contestants to inspect :-) If observed behavior deviates from the posted code (aka you have filed a prank WASEpoint), then your entry and all your WASEpoints will be removed from the registered list.</p>
<p>4. A WASEpoint must not store state, and may not rely on any state data other than the input data (of course, it&#8217;s easy to generate a private data set programmatically, but this will also be considered state). Trivial WASEpoints (e.g. identity) will be disqualified, although triviality is hard to define, you know it when you see it.</p>
<p>5. You must use bit.ly as your URL shortener(to make everyone&#8217;s job building parsers easier &#8212; and bit.ly has a http: interface.</p>
<p>7. Challenge calculations submissions must be in the form of a tweet to @engineyard with the initializing message from your home twitter account. You must be following @engineyard with your home account in order to enter.</p>
<p>8. We must be able to reproduce your computation using your program listing and our own output URL.</p>
<p>9. We STRONGLY encourage people to write their WASEpoints in Ruby, but we&#8217;ll also accept Perl, Scala and Python. Although, be prepared for people avoiding your WASEpoint since the common denominator among people reading this blog is the fact that they know Ruby!</p>
<p>10. We may alter these guidelines along the way, based on your input and feedback, although the spirit and philosophy of them will remain.</p>
<img src="http://www.engineyard.com/blog/?ak_action=api_record_view&id=2710&type=feed" alt="" /><img src="http://feeds.feedburner.com/~r/engineyard/~4/eWWKRwy-OAY" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://www.engineyard.com/blog/2009/win-a-motorola-droid-programming-contest-worst-app-server-technology-ever/feed/</wfw:commentRss>
		<slash:comments>26</slash:comments>
		<feedburner:origLink>http://www.engineyard.com/blog/2009/win-a-motorola-droid-programming-contest-worst-app-server-technology-ever/</feedburner:origLink></item>
		<item>
		<title>Distlockrun: Lockrun for Your Cloud</title>
		<link>http://feedproxy.google.com/~r/engineyard/~3/yMLdZiUAWEU/</link>
		<comments>http://www.engineyard.com/blog/2009/distlockrun-lockrun-for-your-cloud/#comments</comments>
		<pubDate>Fri, 30 Oct 2009 18:40:13 +0000</pubDate>
		<dc:creator>Sam Merritt</dc:creator>
				<category><![CDATA[Technology]]></category>
		<category><![CDATA[Lockrun]]></category>

		<guid isPermaLink="false">http://www.engineyard.com/blog/?p=2636</guid>
		<description><![CDATA[Lockrun is a handy little utility for ensuring you don&#8217;t run two of the same cron job (or other task) at the same time on one machine. It&#8217;s especially handy when the cron job in question has a widely varying duration. Lockrun was written by Steve Friedl and initially released in 2006.
However, when you have [...]]]></description>
			<content:encoded><![CDATA[<p><a href="http://unixwiz.net/tools/lockrun.html">Lockrun is a handy little utility</a> for ensuring you don&#8217;t run two of the same cron job (or other task) at the same time on one machine. It&#8217;s especially handy when the cron job in question has a widely varying duration. Lockrun was written by Steve Friedl and <a href="http://blog.unixwiz.net/2006/06/new_tool_lockru.html">initially released in 2006</a>.</p>
<p>However, when you have jobs that use resources from multiple machines, lockrun isn&#8217;t adequate. Consider a cron job that builds a local index of a bunch of NFS-mounted files. You don&#8217;t want more than one consumer of that NFS volume to index it at a time, otherwise performance will be degraded.</p>
<p>Enter <a href="http://github.com/smerritt/distlockrun">distlockrun</a>: it works similarly to lockrun except that it talks to a central server for mutual exclusion instead of locking a file.</p>
<p>First of all, you need the lock server running. It&#8217;s really lightweight, so you can run it on any machine you&#8217;ve already got. For example, on Engine Yard Cloud, it should be run on the database master in a screen session.</p>
<p>Start the server like so: <code>distlockrun-server</code></p>
<p>By default, it starts on port 7890, but that can be changed with the <code>--port</code> option.</p>
<p>Then, on the client, run your job as follows: <code>distlockrun --server {server's hostname} big-expensive-indexer.rb</code></p>
<p>If there&#8217;s already a big-expensive-indexer.rb running, then distlockrun will just exit. Otherwise, it runs the job and then tells the lock server that it&#8217;s finished.</p>
<p>That&#8217;s all it takes to get cluster-wide mutual exclusion; short but sweet!</p>
<img src="http://www.engineyard.com/blog/?ak_action=api_record_view&id=2636&type=feed" alt="" /><img src="http://feeds.feedburner.com/~r/engineyard/~4/yMLdZiUAWEU" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://www.engineyard.com/blog/2009/distlockrun-lockrun-for-your-cloud/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		<feedburner:origLink>http://www.engineyard.com/blog/2009/distlockrun-lockrun-for-your-cloud/</feedburner:origLink></item>
		<item>
		<title>Security vulnerability in nginx</title>
		<link>http://feedproxy.google.com/~r/engineyard/~3/OIZfHsBuR5Y/</link>
		<comments>http://www.engineyard.com/blog/2009/security-vulnerability-in-nginx/#comments</comments>
		<pubDate>Thu, 29 Oct 2009 19:40:18 +0000</pubDate>
		<dc:creator>Michael Mullany</dc:creator>
				<category><![CDATA[Uncategorized]]></category>
		<category><![CDATA[nginx]]></category>
		<category><![CDATA[secur]]></category>
		<category><![CDATA[security vulnerability]]></category>

		<guid isPermaLink="false">http://www.engineyard.com/blog/?p=2755</guid>
		<description><![CDATA[A security vulnerability in nginx has been reported. This vulnerability is exploited via a null pointer dereference, and although this has been characterized as a Denial of Service attack, we suspect that it can be exploited to execute arbitrary code. As such, it&#8217;s important for all nginx users to upgrade or patch this vulnerability as [...]]]></description>
			<content:encoded><![CDATA[<p>A <a href="http://seclists.org/fulldisclosure/2009/Oct/306">security vulnerability in nginx</a> has been reported. This vulnerability is exploited via a null pointer dereference, and although this has been characterized as a Denial of Service attack, we suspect that it can be exploited to execute arbitrary code. As such, it&#8217;s important for all nginx users to <a href="http://article.gmane.org/gmane.comp.web.nginx.english/16422">upgrade or patch this vulnerability</a> as soon as practicable.</p>
<p>All versions of nginx prior to 0.8.15, 0.7.62, 0.6.39 and 0.5.38 in the 0.8, 0.7, 0.6 and 0.5 nginx codelines are vulnerable.</p>
<p>Engine Yard customers have already been contacted via email about this issue. For Engine Yard Cloud customers, this patch will be automatically applied the next time you perform a deploy. All other customers should open a support ticket so that you can arrange an appropriate maintenance window with support.</p>
<img src="http://www.engineyard.com/blog/?ak_action=api_record_view&id=2755&type=feed" alt="" /><img src="http://feeds.feedburner.com/~r/engineyard/~4/OIZfHsBuR5Y" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://www.engineyard.com/blog/2009/security-vulnerability-in-nginx/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		<feedburner:origLink>http://www.engineyard.com/blog/2009/security-vulnerability-in-nginx/</feedburner:origLink></item>
		<item>
		<title>Announcement: Engine Yard Cloud Price Reduction</title>
		<link>http://feedproxy.google.com/~r/engineyard/~3/Aq82pAzoIfE/</link>
		<comments>http://www.engineyard.com/blog/2009/announcement-engine-yard-cloud-price-reduction/#comments</comments>
		<pubDate>Tue, 27 Oct 2009 18:28:20 +0000</pubDate>
		<dc:creator>Michael Mullany</dc:creator>
				<category><![CDATA[Cloud]]></category>
		<category><![CDATA[News]]></category>
		<category><![CDATA[Amazon]]></category>
		<category><![CDATA[Pricing]]></category>

		<guid isPermaLink="false">http://www.engineyard.com/blog/?p=2731</guid>
		<description><![CDATA[As many of you have heard, Amazon announced pricing changes this morning. Customers have been asking if Engine Yard pricing will be adjusted accordingly, so before you call your rep, here&#8217;s your answer!
Effective November 1st, we will be reducing the prices that we charge for instance hours in Engine Yard Cloud, passing through the announced [...]]]></description>
			<content:encoded><![CDATA[<p>As many of you have heard, Amazon announced pricing changes this morning. Customers have been asking if Engine Yard pricing will be adjusted accordingly, so before you call your rep, here&#8217;s your answer!</p>
<p>Effective November 1st, we will be reducing the prices that we charge for instance hours in Engine Yard Cloud, passing through the announced price reduction in Amazon EC2 pricing. These prices will be in effect automatically for the November billing cycle. New and existing customers will not have to do anything to activate the new pricing, you will get the price reduction automatically starting with your November usage.</p>
<p>This table displays the current and new pricing for Engine Yard Cloud instance hours:</p>
<table style="border-collapse: collapse;" border="0" cellspacing="0" cellpadding="0" width="228">
<tbody>
<tr height="13">
<td width="78" height="13"></td>
<td style="text-align: right;" width="75"><span style="text-decoration: underline;">Current</span></td>
<td style="text-align: right;" width="75"><span style="text-decoration: underline;">Nov 1st<br />
</span></td>
</tr>
<tr height="13">
<td height="13">Small</td>
<td style="text-align: right;"><span> </span>$0.160</td>
<td style="text-align: right;"><span> </span>0.145</td>
</tr>
<tr height="13">
<td height="13">Large</td>
<td style="text-align: right;"><span> </span>0.480</td>
<td style="text-align: right;"><span> </span>0.420</td>
</tr>
<tr height="13">
<td height="13">XL</td>
<td style="text-align: right;"><span> </span>0.900</td>
<td style="text-align: right;"><span> </span>0.780</td>
</tr>
<tr height="13">
<td height="13"></td>
<td></td>
<td></td>
</tr>
<tr height="13">
<td height="13">High CPU Med</td>
<td style="text-align: right;"><span> </span>0.270</td>
<td style="text-align: right;"><span> </span>0.240</td>
</tr>
<tr height="13">
<td height="13">High CPU XL</td>
<td style="text-align: right;"><span> </span>0.900</td>
<td style="text-align: right;"><span> </span>0.780</td>
</tr>
</tbody>
</table>
<p>Our main web-site and pricing pages will be updated on November 1st to reflect these new prices. As always, we&#8217;re here to address an questions or comments, and look forward to hearing from you!</p>
<img src="http://www.engineyard.com/blog/?ak_action=api_record_view&id=2731&type=feed" alt="" /><img src="http://feeds.feedburner.com/~r/engineyard/~4/Aq82pAzoIfE" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://www.engineyard.com/blog/2009/announcement-engine-yard-cloud-price-reduction/feed/</wfw:commentRss>
		<slash:comments>3</slash:comments>
		<feedburner:origLink>http://www.engineyard.com/blog/2009/announcement-engine-yard-cloud-price-reduction/</feedburner:origLink></item>
		<item>
		<title>Improving the Rubinius Bytecode Compiler</title>
		<link>http://feedproxy.google.com/~r/engineyard/~3/jPRuo7V03fg/</link>
		<comments>http://www.engineyard.com/blog/2009/improving-the-rubinius-bytecode-compiler/#comments</comments>
		<pubDate>Thu, 22 Oct 2009 17:00:37 +0000</pubDate>
		<dc:creator>Brian Ford</dc:creator>
				<category><![CDATA[Technology]]></category>
		<category><![CDATA[compiler]]></category>
		<category><![CDATA[MRI]]></category>
		<category><![CDATA[ParseTree]]></category>
		<category><![CDATA[Rubinius]]></category>

		<guid isPermaLink="false">http://www.engineyard.com/blog/?p=2649</guid>
		<description><![CDATA[The Rubinius bytecode compiler is the gateway to all the magic that makes your Ruby code run. As you probably know, the Rubinius virtual machine is a bytecode interpreter. The Rubinius JIT compiler also processes bytecode, converting it into native machine code. Without bytecode, we&#8217;d be dead in the water. Recently, I&#8217;ve been working on [...]]]></description>
			<content:encoded><![CDATA[<p>The Rubinius bytecode compiler is the gateway to all the magic that makes your Ruby code run. As you probably know, the Rubinius virtual machine is a bytecode interpreter. The Rubinius JIT compiler also processes bytecode, converting it into native machine code. Without bytecode, we&#8217;d be dead in the water. Recently, I&#8217;ve been working on improving the Rubinius bytecode compiler.</p>
<p>In this post, I&#8217;ll explain what I&#8217;ve been doing and how it relates to getting Rubinius ready for 1.0. We recently released version 0.12 and we&#8217;re going to be doing releases about every two weeks. If you haven&#8217;t built Rubinius yet to check it out, head over to our <a href="http://rubini.us/download.html">download page</a> and get started!</p>
<h2>Principles</h2>
<p>Before we get into code examples, let&#8217;s lay the groundwork and review some general principles for writing software. We learn early in computer science that programming involves a series of trade-offs. There&#8217;s always the the classic trade-off between execution time and storage space. If we write a function that saves each value it computes, it can return the saved value instead of re-computing it, but saving the value requires using memory or disk space. A function that computes millions of values would potentially need millions of storage locations.</p>
<p>While speed versus space is probably the most well-known classic trade-off, there are others. With a nod to <a href="http://bit.ly/three-pillars-of-zen">Philip Kapleau</a>, here are the three pillars of developing Rubinius: <strong>Code Quality</strong>, <strong>Compatibility</strong>, and <strong>Performance</strong>, or <strong>QCP</strong>. These interact in complex ways and there&#8217;s no simple way to prioritize them. By compatibility, I mean conforming to the same behavior as MRI (or Matz&#8217;s Ruby).</p>
<p>These three characteristics are interdependent. Clean, high-quality code enables working more quickly on compatibility. When code is behaving correctly, it&#8217;s easier to profile and improve performance. Good performance, in turn, can make it easier to identify and fix compatibility issues. Quality code is easier to understand and work with when improving performance. These feedback loops push each of these code characteristics forward. The converse is also true. At times, it may be tempting to sacrifice quality to improve performance, but quick and dirty <em>never</em> pays off in the end.</p>
<p>To sum up, these are the goals we&#8217;re pursuing: simplify and improve the bytecode compiler code, improve performance, make it simpler and easier to bootstrap Rubinius, and ultimately make it easier to fix compatibility issues running fantastic Ruby software, like your Rails applications.</p>
<h2>Parsers and Compilers</h2>
<p>There&#8217;s a certain mystique that surrounds compilers. Rather than just accept the mystique, let&#8217;s take a peek behind the unicorns and dragons and check out the simple gears and pistons that make it all work. In general terms, a compiler is a process for converting data from one form into another. In the specific case of Ruby, the compiler converts text in the form of Ruby syntax, into operations performed by the computer&#8217;s CPU.</p>
<p>In discussing compilers, two rather distinct operations are often lumped together, namely, parsing and code generation. Parsing is the process of converting the source code into a data structure that the compiler can process to produce code that the computer, or a virtual machine, can execute, to perform computation. There are specific issues with each operation so let&#8217;s look at them separately.</p>
<h2>A. The Parser</h2>
<p>Humans are the most adept and complex parsers in existence. One natural language can choke up a powerful computer, but humans typically handle one and sometimes several languages with relative ease. Not just the words or sounds of a language either, but also the intonations, facial expressions and body language that accompany even the simplest communications.</p>
<p>Compared to natural languages, programming languages are very simple, but even the simplest languages can be challenging to parse. Syntactically, Ruby is a rich and complex language. We love it for the expressive programs we can write—but parsing Ruby is <em>hard</em>. Every Ruby parser in the Ruby implementations that I know of are based on Matz&#8217;s parser. From the beginning, Rubinius has used a directly imported version of Matz&#8217;s parser with a few minor modifications.</p>
<h3>Detour—Bootstrapping</h3>
<p>Before continuing, we need to take a short detour. I&#8217;ll be writing a post on the Rubinius bootstrapping process in the future, but I&#8217;ll start by briefly describing part of the problem here.</p>
<p>The Rubinius bytecode compiler, and most of the Ruby core library (classes like <code>Array</code> and <code>Hash</code>), are written in pure Ruby—and herein lies the challenge. The Rubinius VM interprets bytecode. To run the Rubinius compiler, it needs to translate the Ruby source code for the compiler into bytecode, but to do so, Rubinius needs to run the compiler. You can probably see where this is heading: around and around without getting much done.</p>
<p>That is the essence of the problem of bootstrapping. To break this loop, we need to insert a process that does not depend on Rubinius. In other words, we need something that&#8217;s <em>not</em> Rubinius to compile the core library and bytecode compiler so that Rubinius can load the bytecode and run the compiler. <em>Then</em> Rubinius can compile its own compiler and core library.</p>
<p>One way to do this would be to load the Ruby source code in MRI and use the <a href="http://bit.ly/parse-tree">ParseTree</a> gem to extract the MRI parse tree as a recursive array of symbols and values, something also known as an S-expression or sexp. But, since you can&#8217;t change the MRI parser without impacting its run time behavior, <a href="http://blog.fallingsnow.net">Evan Phoenix</a> wrote the sydparse gem.</p>
<p>This gem is essentially a C extension combining the MRI parser with the sexp generation code from ParseTree. The gem enabled Evan to modify the parser if desired and get the parse results as a sexp. The sydparse gem is basically built into Rubinius currently under the <em>vm/parser</em> directory.</p>
<p>For example, try the following code:</p>
<pre>$ bin/rbx
irb(main):001:0&gt; "a = 1".to_sexp
=&gt; s(:lasgn, :a, s(:lit, 1))</pre>
<p>So, with the <code>String#to_sexp</code> method, we have something that will take Ruby code and transform it to a data structure that should be fairly easy for a computer to process. And that is precisely how the early Rubinius bytecode compiler worked: it processed sexps into bytecode. It didn&#8217;t matter whether the sexps are output by sydparse running in MRI or by the Rubinius built-in parser. With that, a big part of bootstrapping was solved.<br />
<strong><span id="more-2649"></span></strong></p>
<h3>Back to the Main Road</h3>
<p>So we&#8217;ve got a compiler that processes sexps to bytecode and it runs in both MRI and Rubinius. Everything sounds copacetic—what&#8217;s the trouble, you ask? Well, compiling Ruby is <em>hard</em>, too.</p>
<p>To make the process more tractable, Evan rewrote the compiler so that the sexps are processed into an abstract syntax tree (AST) of Ruby objects. Each object has a <code>#bytecode</code> method. To generate bytecode, start at the root of the tree and visit each node, calling the <code>bytecode</code> method for the node.</p>
<p>For perspective, let&#8217;s enumerate all the stages in the compiler. I&#8217;m glossing over a few details here, but you&#8217;ll see the basic picture:</p>
<ol>
<li><em>parse tree</em>: A tree of C data structs created by the MRI parser.</li>
<li> <em>sexp</em>: A recursive array of symbols and values created by processing the MRI parse tree.</li>
<li><em>rewritten sexp</em>: The form of the sexp after certain structures are normalized to make converting the sexp simpler.</li>
<li><em>AST</em>: The abstract syntax tree of Ruby objects created by processing the rewritten sexp.</li>
<li><em>bytecode</em>: A stream of instructions that the Rubinius VM can execute.</li>
<li><em>compiled method</em>: The bytecode packaged with some additional information like the names of local variables and the amount of stack space needed when the compiled method runs. A typical Ruby source code file compiles to a tree of compiled methods.</li>
<li><em>compiled file</em>: The Rubinius VM can execute the compiled methods directly. But to avoid having to recompile them, the tree of compiled methods is serialized to a compiled file on disk. Rubinius can read the compiled file to recreate the tree of compiled methods in memory.</li>
</ol>
<p>That&#8217;s a significant number of stages and it&#8217;s not hard to see where we can simplify to improve the process. Those sexp stages appear to just be passing data along. In fact, they require creating a lot of additional objects, time to process, and some seriously complex code to process them. The latter has a significant, negative impact on code quality.</p>
<p>S-expressions are just data, dumb and brittle. They only have form (e.g. <code>[:x, :y]</code> versus <code>[:x, [:y]]</code>) and position (e.g. <code>[:alias, :x, :y]</code> versus <code>[:alias, :y, :x]</code>) to encode information. If you wanted to add, for example, a line number to every sexp, you&#8217;d need to put the information in a form and at a particular position in every sexp, or you&#8217;d need to wrap every sexp in one that encodes the line number. The simplicity of sexps is beguiling, but you know what they say when all you have are s-expressions&#8230; Everything looks like function application.</p>
<p>On the other hand, we have this rich, object-oriented language that makes it trivially easy to create objects that conform to a consistent interface. We want to get from the Ruby text to Ruby objects as quickly, and simply, as possible.</p>
<p>One option would be to write the compiler so that it processes the MRI parse tree directly into bytecode. Unfortunately, that would require either rewriting the compiler in C (not gonna happen) or making the C data structs available in Ruby. The latter option sounds promising. We could translate the parse tree directly into an AST.</p>
<h3>Melbourne</h3>
<p>Enter Melbourne, a C extension that can run in MRI or Rubinius and process the MRI parse tree directly into an AST. In case you were wondering, it was named in honor of Evan&#8217;s sydparse gem, and some rowdy Aussie developers we know.</p>
<p>In Melbourne, each node of the MRI parse tree is processed using a mechanism we all know and love: a method call. At each parse tree node, all children are processed by recursively calling the <code>process_parse_tree</code> function (see <em>lib/ext/melbourne/visitor.cpp</em>). At a leaf node, there are no children to create, so an appropriately named Ruby method is called on the <code>Rubinius::Melbourne</code> instance that is passed to <code>process_parse_tree</code>.</p>
<p>Once all of a node&#8217;s children are created, a method is called for the parent node, passing along the objects that were already created for the node&#8217;s children. You can see the result of this process by running the following command:</p>
<pre>$ bin/rbx -r compiler-ng -e '"a = 1".to_ast.ascii_graph'
LocalVariableAssignment
  @line: 1
  @name: a
  FixnumLiteral
    @line: 1
    @value: 1</pre>
<p>In <em>lib/melbourne/processor.rb</em>, you can see code like the following. Whenever a local variable assignment parse tree node (lasgn) is processed, this method is called and the LocalVariableAssignment AST node is created. Pretty straightforward.</p>
<pre>def process_lasgn(line, name, value)
  AST::LocalVariableAssignment.new line, name, value
end</pre>
<p>One of my goals for improving the code quality in the compiler was to replace the use of conditionals by creating more explicit nodes in the AST. Sometimes code suffers from what I&#8217;d call <em>conditionalitis</em>, or inflammation of your conditionals (sounds painful, huh?). That&#8217;s where you maintain a lot of state and use conditionals to figure out what to do.</p>
<p>An alternative is to create different forms of things that just do what they are supposed to do. Simply avoiding using conditionals is not really an option, but <em>where</em> they&#8217;re used can have a big impact on code quality. It&#8217;s can be hard to understand by just looking at the code why one branch or the other would be taken when the program is running, but when the conditional results in different forms being created, it&#8217;s much easier to comprehend how the program will behave</p>
<p>For example, in MRI <code>a.b = 1</code> and <code>a[b] = 1</code> are parsed into the same parse tree node. But there are enough things that need to be done differently in the two cases that just creating different forms can make these tasks simpler and more explicit. The code in <em>lib/melbourne/processor.rb</em> for processing an attrasgn node looks like this:</p>
<pre>def process_attrasgn(line, receiver, name, arguments)
  if name == :[]=
    AST::ElementAssignment.new line, receiver, arguments
  else
    AST::AttributeAssignment.new line, receiver, name, arguments
  end
end</pre>
<h2>B. The Compiler</h2>
<p>By this point we have a fully formed AST that faithfully represents the Ruby code we started with. Emitting bytecode now is almost anticlimactic. Not really, of course, because there is plenty of work left to do. Just keeping track of local variables in methods, blocks and evals could take up a whole post, but the basic idea is really simple. Start at the root node and call the <code>#bytecode</code> method, walking down the tree until every node has been visited. All the details (and there are a lot of them) can be found in the <code>#bytecode</code> methods on the AST nodes (see <em>lib/compiler-ng/ast</em>).</p>
<p>Besides generating bytecode, there are other interesting things you can do simply by defining methods on the AST nodes and visiting them. I&#8217;ll give you two examples. First, let&#8217;s look at <code>defined?</code>.</p>
<pre># defined.rb
class A
  class B
  end
end

def x
  puts "hey there"
  A
end

p defined? A::B
p defined? x::B</pre>
<p>If you run this code in Ruby, you should see the following output.</p>
<pre>$ ruby defined.rb
"constant"
hey there
"constant"</pre>
<p>This code illustrates something about <code>defined?</code>. It doesn&#8217;t just check internal data structures. In some cases, like <code>x::B</code>, it must do some evaluation.</p>
<p>In Rubinius, we simply add a <code>defined(g)</code> method to any relevant AST node that takes an instance of the bytecode generator object and emits bytecode appropriate for evaluating the expression passed to <code>defined?</code>. Here is the full code for the <code>Defined</code> AST node.</p>
<pre>class Defined &lt; Node
  attr_accessor :expression

  def initialize(line, expr)
    @line = line
    @expression = expr
  end

  def bytecode(g)
    pos(g)

    @expression.defined(g)
  end
end</pre>
<p>Rather than emitting bytecode, it&#8217;s possible to simply evaluate the AST, performing actions when visiting each node. Rubinius has an evaluator for being able to write bits of code for which there is no simple Ruby syntax. The evaluator makes it possible to write directly in Rubinius <em>assembly language</em> (i.e. in the operations that the VM directly executes). Consider this example:</p>
<pre># asm.rb
def hello(name)
  Rubinius.asm(name) do |name|
    push :self
    run name
    string_dup
    push_literal "hello, "
    string_dup
    string_append
    send :p, 1, true
  end
end

hello "world"</pre>
<p>If you run this in Rubinius, you should see the following output:</p>
<pre>$ bin/rbx asm.rb
"hello, world"</pre>
<p>Of course, this example is much easier to write in plain old Ruby. However, we use this facility in the Ruby core library. For example, in the <code>Class#new</code> method.</p>
<p>The basic idea is that a tree of Ruby objects representing Ruby source code is a powerful tool that can be easily extended to accomplish various tasks.</p>
<h2>Compiler, Part Deux</h2>
<p>Let&#8217;s revisit the list we presented earlier of compiler stages. How do we coordinate those stages? What if we need to insert a stage? Well, by creating more things we can name, and on which we can define behavior.</p>
<p>You can see the various stages laid out in <em>lib/compiler-ng/stages.rb</em>. By default, each stage know which stage follows it. You create an instance of the compiler by specifying the starting stage and the ending stage. Each stage has an interface with the preceding and following stage, with the compiler object itself, and with the object that performs the work for that stage. For example, if the compiler will compile a String to a CompiledMethod, it will consist of the StringParser, Generator, Encoder, and Packager stages.</p>
<p>The goal was to create an API for the compiler that made it simple to use programatically in a variety of circumstances. For example, using the compiler internally to compile a Ruby file when <code>#require</code> is called or in a command line script like <em>lib/bin/compile-ng.rb</em>. The command line script may give the option to show the data structures as the compiler is processing them. Here&#8217;s an example:</p>
<pre>$ cat var.rb
a = 1
p a

$ bin/rbx compile-ng -AB var.rb
Script
  @name: __script__
  @file: "var.rb"
  Block
    @line: 1
    @array:
      LocalVariableAssignment
        @line: 1
        @name: a
        FixnumLiteral
          @line: 1
          @value: 1
      SendWithArguments
        @line: 2
        @name: p
        @privately: true
        Self
          @line: 2
        ActualArguments
          @line: 2
          @array:
            LocalVariableAccess
              @line: 2
              @name: a

============= :__script__ ==============
Arguments:   0 required, 0 total
Locals:      1: a
Stack size:  3
Lines to IP: 1: 0-4, 2: 4-14

0000:  meta_push_1
0001:  set_local                  0    # a
0003:  pop
0004:  push_self
0005:  push_local                 0    # a
0007:  allow_private
0008:  send_stack                 :p, 1
0011:  pop
0012:  push_true
0013:  ret
----------------------------------------

$ bin/rbx var.rbc
1</pre>
<p>Take a look at <code>bin/rbx compile-ng -h</code> for more options and try this out on your Ruby code. It opens up a pretty exciting world of understanding.</p>
<h2>A Final Note</h2>
<p>Melbourne and the new compiler will both be enabled in the up-coming 0.13 release, so anywhere I&#8217;ve used <em>compile-ng</em> or <em>compiler-ng</em> in this post, will become just <em>compile</em> or <em>compiler</em>. Until then, all the code exists in the Rubinius <a href="http://github.com/evanphx/rubinius">Github repository</a>, so you don&#8217;t have to wait to check it out.</p>
<p>Thoughts? Questions? Leave them here!</p>
<img src="http://www.engineyard.com/blog/?ak_action=api_record_view&id=2649&type=feed" alt="" /><img src="http://feeds.feedburner.com/~r/engineyard/~4/jPRuo7V03fg" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://www.engineyard.com/blog/2009/improving-the-rubinius-bytecode-compiler/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		<feedburner:origLink>http://www.engineyard.com/blog/2009/improving-the-rubinius-bytecode-compiler/</feedburner:origLink></item>
		<item>
		<title>The Art of Library</title>
		<link>http://feedproxy.google.com/~r/engineyard/~3/-d7XxTY8IDA/</link>
		<comments>http://www.engineyard.com/blog/2009/the-art-of-library/#comments</comments>
		<pubDate>Mon, 19 Oct 2009 18:45:16 +0000</pubDate>
		<dc:creator>Wesley Beary</dc:creator>
				<category><![CDATA[Technology]]></category>
		<category><![CDATA[AWS]]></category>
		<category><![CDATA[Ruby]]></category>
		<category><![CDATA[S3]]></category>
		<category><![CDATA[testing]]></category>

		<guid isPermaLink="false">http://www.engineyard.com/blog/?p=2521</guid>
		<description><![CDATA[Honk if you love hacking! I know I do, and boy is it ever satisfying to turn a tough problem into a clever solution. But as you tumble down the slippery hacking slopes, picking up loose bits of code, you may find the itch to start writing code for other hackers. Good thought, but realize [...]]]></description>
			<content:encoded><![CDATA[<p>Honk if you love hacking! I know I do, and boy is it ever satisfying to turn a tough problem into a clever solution. But as you tumble down the slippery hacking slopes, picking up loose bits of code, you may find the itch to start writing code for other hackers. Good thought, but realize that implementation is just the tip of the iceberg.</p>
<h2>Library What, Huh?</h2>
<p>What makes libraries so special? They save time and effort by providing code you don&#8217;t want to write. Skip the nitty gritty of this authentication scheme or that request protocol and get on with the task at hand. There are many ways for a library to capture wily domain knowledge and not all are created equal. After working on some libraries and reading many more, what follows are my personal guidelines for creating lovely encapsulating bits.</p>
<h2>First, Do No Harm</h2>
<p>It should be easier to work with a library than without it. People will grudgingly use libraries that are painful, but will recommend and rave about libraries that feel good. You should start with some idea of what an ideal interaction with the library might feel like. Your first pass at implementation will tend to be rough work, but keep utopia in mind. Subsequent iterations can work to unify reality and awesome.</p>
<p>This happened when I wrote <a href="http://github.com/geemus/shindo">shindo</a>, a test framework that I wrote because I wanted something short and sweet. I have a love/hate relationship with testing. I like the confidence that tests give me, but I&#8217;m just not a big fan of the <em>process</em>. Shindo lets me easily understand what&#8217;s actually happening when tests run. It looks like this:</p>
<pre># Each test is a Ruby block, with success determined by return value
Shindo.tests('examples') do
  test('successful test') { true }
  test('failing test')    { false }
  test('pending test')    # no block provided
end</pre>
<p>My first pass at implementation was basic, but then I started iterating. I wanted the runner to be more interactive than what I was used to. So I added a prompt system that halts the test run when it runs into an error and asks you what you want to do. This led to rapid iterations:</p>
<ol>
<li>I needed backtraces, but I didn&#8217;t want to raise errors. So I wrote a nice backtrace object and added some code to view its output.</li>
<li>I wanted a better way to sift through files referenced in the backtrace, so I added the ability to choose a line from the backtrace and get an excerpt of the file containing it.</li>
<li>I wanted a better way to debug errors, so I added the choice to launch irb with the same binding as the test block. That&#8217;s my kind of digging in.</li>
<li>There was no need to run all the tests all the time, so I added tags to help narrow a run down to the relevant ones.</li>
<li>Finally, I added the option to reload files and restart the test run.</li>
</ol>
<p><strong><span id="more-2521"></span></strong></p>
<h2>Keep It Simple</h2>
<p>Libraries should be less complicated than the problems they address. Simple libraries are easier to understand, improve and maintain. Abstractions are a tempting place to start, but they are easier to build on a solid foundation. Start with the simplest thing that could work, staying close to the problem you are trying to solve. For instance, when wrapping public APIs, match the names of attributes and functions to the API documentation. It makes it easier to refer to things and provides documentation from the start.</p>
<p>When I found out I would be joining the <a href="http://www.engineyard.com/products/cloud">Engine Yard Cloud</a> team, it occurred to me that I should have more than a passing knowledge of <a href="http://aws.amazon.com">AWS</a>. I started by reading tons of <a href="http://aws.amazon.com/documentation">docs</a>. Equipped with a rough idea of what was involved, I wanted an easy way to experiment and code AWS. Such was the genesis of <a href="http://github.com/geemus/fog">fog</a>.  AWS interactions can be super complicated, so I started by mapping the docs almost directly to code. Using it looks like this:</p>
<p><a name="low_level"></a></p>
<pre>s3 = Fog::AWS::S3.new(
 :aws_access_key_id =&gt; id,
 :aws_secret_access_key =&gt; key
)

# get a list of buckets
s3.get_service

# create a bucket
s3.put_bucket('bucketname')

# get the contents of that bucket
s3.get_bucket('bucketname')

# delete that bucket
s3.delete_bucket('bucketname')</pre>
<p>I&#8217;ll be the first to admit that its not the prettiest, but it worked! And when in doubt, the AWS documentation could remind me what in the world some method I wrote a month ago was supposed to do. Recently someone asked if I had generated this code somehow. I wrote it all by hand (unless you count copy/paste as code generation), but for once it was oddly comforting to be mistaken for a robot.</p>
<h2>Stay Focused</h2>
<p>Stick to capturing stuff for one problem at a time. If sub-sections start getting distracting, consider extracting them into their own libraries. Avoid monkey patching; use inheritance or something that is not monkey patching. Premature optimization is the root of all evil, iteration is the trunk of all good. Do not undermine your tree, and do furnish it with leaves of documentation so that it does not wither and die.</p>
<p>While working on shindo, I ended up adding lots of backtrace related code. Since I was not relying on raised errors, I needed some other way to control starting and stopping tracing. At first it was small and self-contained, like this:</p>
<pre>class Annals

  attr_accessor :buffer, :max

  def initialize(max = 20, &amp;block)
    @max = max
    start
  end

  def lines
    @buffer.map do |event|
      "#{event[:file]}:#{event[:line]} #{event[:method]}"
    end
  end

  def start
    @buffer = []
    @size   = 0
    Kernel.set_trace_func(
      lambda { |event, file, line, id, binding, classname|
        if event == 'call'
          unshift(:file =&gt; file, :line =&gt; line, :method =&gt; "in #{id}")
        end
      }
    )
  end

  def stop
    Kernel.set_trace_func(lambda {})
    @buffer.shift # remove Annals#stop from buffer
  end

  def unshift(line)
    @buffer.unshift(line)
    if @size == @max
      @buffer.pop
    else
      @size += 1
    end
  end

end</pre>
<p>But it just kept growing. So I pulled it out into its own project, <a href="http://github.com/geemus/annals">annals</a>. After extraction I could proceed to add other handy backtrace related features, even ones I had no real need for just then. Small libraries like this make great building blocks for newer, crazier stuff.</p>
<h2>Add Value</h2>
<p>Once equipped with more knowledge about the problem than is healthy for any one person, abstraction is a go. Tackle low hanging fruit first. Ruby is, afterall, object-oriented, so consider providing some nice models that encapsulate the nitty gritty bits. Write mocks. No one knows better what mocks should do. Add some command line tools or an interactive mode. Focus on eliminating pain points, increasing usability and generally making the project awesome to work with.</p>
<p>With a good foundation of low level functionality in <a href="http://github.com/geemus/fog">fog</a>, it was time to start adding value. I added easy mocking, because running tests against AWS can be expensive and slow. Then I started building objects around the various entities. Here is how the objects replace the low level calls from earlier:</p>
<pre># this is optional and great for testing
Fog.mock!

# connection
s3 = Fog::AWS::S3.new(
 :aws_access_key_id =&gt; id,
 :aws_secret_access_key =&gt; key
)

# get a list of buckets
s3.buckets.all

# create a bucket
bucket = s3.buckets.create(:name =&gt; 'bucketname')

# get the contents of that bucket
bucket.objects.all

# delete that bucket
bucket.destroy</pre>
<p>The new interface mimics <a href="http://github.com/datamapper/dm-core">DataMapper</a>, an interface myself and many others are already familiar with. I did my best to abstract away the nitty gritty captured in the low level functionality while leaving the ability to get stuff done. I think it is better, but it is still a work in progress. I dedicate a little time every day to filling in the blanks, but libraries have a way of never truly being finished.</p>
<h2>Lather, Rinse, Repeat</h2>
<p>Making libraries that capture a domain and feel good to use is hard. Implement the simplest thing first, but never stop iterating. Be absurdly consistent. Keep fiddling with the look and feel. If something seems painful, it probably is, so fix it. Make your code a joy to use and people will reward you by using it, and submitting patches (hint, hint)!</p>
<p>What do you think makes a good library? How can I improve <a href="http://github.com/geemus">mine</a>? Comment and let me know!</p>
<img src="http://www.engineyard.com/blog/?ak_action=api_record_view&id=2521&type=feed" alt="" /><img src="http://feeds.feedburner.com/~r/engineyard/~4/-d7XxTY8IDA" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://www.engineyard.com/blog/2009/the-art-of-library/feed/</wfw:commentRss>
		<slash:comments>4</slash:comments>
		<feedburner:origLink>http://www.engineyard.com/blog/2009/the-art-of-library/</feedburner:origLink></item>
		<item>
		<title>3 Ruby Quirks You Have to Love</title>
		<link>http://feedproxy.google.com/~r/engineyard/~3/5qzmqRt_Yek/</link>
		<comments>http://www.engineyard.com/blog/2009/3-ruby-quirks-you-have-to-love/#comments</comments>
		<pubDate>Thu, 15 Oct 2009 19:15:29 +0000</pubDate>
		<dc:creator>Thomas Enebo</dc:creator>
				<category><![CDATA[Technology]]></category>
		<category><![CDATA[heredocs]]></category>
		<category><![CDATA[jruby]]></category>
		<category><![CDATA[Ruby]]></category>

		<guid isPermaLink="false">http://www.engineyard.com/blog/?p=2559</guid>
		<description><![CDATA[Ruby's a fantastic language; we love it because it's flexible, readable and concise, to name just a few reasons. The Ruby language is also incredibly complex as far as language syntaxes (grammar) are concerned. This sometimes leads to some dark seedy corners... but by examining the stranger aspects of Ruby's syntax, it helps us to better understand the power of Ruby. This entry will show some of the stranger aspects of the language and reflect on how we rarely see these used in real life.]]></description>
			<content:encoded><![CDATA[<p>Ruby&#8217;s a fantastic language; we love it because it&#8217;s flexible, readable and concise, to name just a few reasons. The Ruby language is also incredibly complex as far as language syntaxes (grammar) are concerned. This sometimes leads to some dark seedy corners&#8230; but by examining the stranger aspects of Ruby&#8217;s syntax, it helps us to better understand the power of Ruby. This entry will show some of the stranger aspects of the language and reflect on how we rarely see these used in real life.</p>
<p><strong>Warning</strong>: Most of the code you see in this post should never be used in real life by actual programmers.  The snippets are meant to provide insight into the power of Ruby, and more obviously, are for entertainment value.</p>
<h2>1. Expressions are Cool</h2>
<p>One of the great things about Ruby is that everything is just executable code. In fact, everything is an expression.  This makes it nice because you can then programmatically build up a set of methods in a class body, like this:</p>
<pre>class Numeric
  MM_SIZE = 0.00025

  [:mm, :cm, :dm, :m, :dam, :hm, :km, :Mm].inject(MM_SIZE) do |size, unit|
    define_method(unit) { self * size }
    size * 10
  end
end</pre>
<p>Ignoring the fact that I just modified <code>Numeric</code>, you have to admit that being able to do stuff like this in Ruby makes it a great language; a language with the flexibility to define methods progammatically because everything is just code and expressions.</p>
<p>Over the years of supporting JRuby, I began to realize that the grammar is a bit <em>too </em>too generous.  At this point you can argue that it&#8217;s up to programmers to hang themselves with any bizarre language feature. So what&#8217;s the fuss?</p>
<pre>def foo(a, b=def foo(a); "F"; end)
  a
end

p foo("W", 1) + foo("T") + foo("bar")      # =&gt; "WTF"</pre>
<p>Is there a sane reason to allow a <code>def</code> as the value of an optional argument? Since we have a language where everything is an expression, this is just life in the Ruby lane.  Luckily, <code>def</code> returns <code>nil</code> and not <code>UnboundMethod</code> or something that&#8217;s considered <em>useful</em>; otherwise there would be some pretty weird code floating around.</p>
<p>Before I go on, I wondered about variable scoping&#8230;</p>
<pre>def foo(a, b = self.class.send(:define_method, :foo) {|_| ": I captured #{a}" })
  a
end

p foo("foo") + foo("bar")  #=&gt; foo: I captured foo</pre>
<p>Of course it makes sense that the optional argument value of &#8216;b&#8217; evaluates in a scope where it can capture &#8216;a&#8217;&#8230; but wow! The potential for strange Ruby code is just amazing!</p>
<h2><strong><span id="more-2559"></span></strong>2. It Makes Sense But&#8230;</h2>
<p>Other times the grammar has oddities which seem to make sense on the <em>surface</em> but generally confuse you when you start to stray off the path of idiomatic Ruby.  Heredocs are a great example:</p>
<pre>a = &lt;&lt;EOF
hooray
multi-lines
EOF</pre>
<p>This how we normally see them.  The idiomatic example may have us define a heredoc as: when encountering a heredoc statement (&lt;&lt;EOF) take all lines after that statement until we encounter the heredoc marker on a line by itself and use those lines as a multi-line string.  Certainly, that would still work for this example:</p>
<pre>{:skybox =&gt; "/data/skyboxes/mountains/",
 :floors =&gt; [
  {:location =&gt; [0,0,0],:data =&gt; &lt;&lt;EOL, :texture =&gt; "data/texture/wall.jpg"},
........BCB.........
.........P..........
....................
........DDD.........
.......D....D.......
....................
....D.....D....D....
....................
..D....D....D....D..
...BBBBBBBBBBBBBB...
EOL
  {:location =&gt; [0,10,0],:data =&gt; &lt;&lt;EOL, :texture =&gt; "data/texture/wall.jpg"},
# More elided ...
}</pre>
<p>Heredocs in this code are interleaved in the middle of a hash literal&#8230; This is <em>odd</em>, but the definition still holds up.  Let&#8217;s break the definition:</p>
<pre>def foo(a,b)
  p a, b
end

foo(&lt;&lt;ONE, &lt;&lt;TWO)
This is one
ONE
This is two
TWO</pre>
<p>Two heredocs on the same line break the definition.  I think with a little work we could fix the definition to talk about what to do about multiple heredocs on the same line, but then consider this case:</p>
<pre>a = &lt;&lt;ONE
This is one. #{&lt;&lt;TWO}
This is two. #{&lt;&lt;THREE}
This is three.
THREE
TWO
ONE    # =&gt; "This is one. This is two. This is three.\n\n\n"</pre>
<p>Coming up with an easy to read definition is starting to get rough.  Maybe just accepting that you can do weird things with heredoc without actually <em>explaining</em> them in a common definition is the right thing to do.  Do we really want people to use heredocs this way?  Is it cool that we can do stuff like this?</p>
<h2>3. Mystical String Concatenation</h2>
<p>Ruby&#8217;s grammar is <em>huge</em>.  Super huge, and sometimes you see something in the grammar that makes you wonder how it got there.  For me the one I wonder about the most is what I call the &#8220;Mystical String Concatenation&#8221; feature:</p>
<pre>string        : string1
              | string string1 {
                  $$ = support.literal_concat(getPosition($1), $1, $2);
              }</pre>
<p>Since string1 must be a type of string literal this translates into the following Ruby syntax:</p>
<pre>a = "foo" "bar"
p a # =&gt; "foobar"</pre>
<p>Is this really useful?  The performance benefit is that the parser will concatenate the string before any execution happens&#8230;. but this only works for string literals!  A programmer could just rewrite the string as &#8220;foobar&#8221;. Perhaps someone wanted to inspect strings into an eval statement?</p>
<pre>one = "foo"
two = "bar"
a = eval "#{one.inspect} #{two.inspect}"</pre>
<p>It mystifies me&#8230;</p>
<h2>Conclusion</h2>
<p>Ruby is beautiful, powerful, and in some cases, wacky.  I&#8217;ve only just scratched the surface of the weird convoluted things you can do with Ruby&#8217;s syntax.  What&#8217;s most interesting to me though is that Ruby programmers rarely touch these strange bits unless they&#8217;re trying to be cute&#8230;</p>
<p>Compared to another language I used to always use (it starts with a P and is four letters, but I seem to have completely forgotten its name&#8230;), it&#8217;s surprising how rarely <em>real</em> Ruby code ventures into the land of the indecipherable.</p>
<p>Both Ruby and the previously mentioned forgotten language are very powerful, and can get a lot of the same things done, but it seems that Ruby code ends up being written in a largely idiomatic way.  Ruby may be a rat&#8217;s nest for writing a coherent specification of the language, but the Ruby that people write using this underspecified language ends up looking really nice.</p>
<img src="http://www.engineyard.com/blog/?ak_action=api_record_view&id=2559&type=feed" alt="" /><img src="http://feeds.feedburner.com/~r/engineyard/~4/5qzmqRt_Yek" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://www.engineyard.com/blog/2009/3-ruby-quirks-you-have-to-love/feed/</wfw:commentRss>
		<slash:comments>13</slash:comments>
		<feedburner:origLink>http://www.engineyard.com/blog/2009/3-ruby-quirks-you-have-to-love/</feedburner:origLink></item>
		<item>
		<title>Open Sourcing Rails Development Directory Code</title>
		<link>http://feedproxy.google.com/~r/engineyard/~3/q6QXkC1dnxE/</link>
		<comments>http://www.engineyard.com/blog/2009/open-sourcing-rails-development-directory-code/#comments</comments>
		<pubDate>Tue, 13 Oct 2009 22:41:28 +0000</pubDate>
		<dc:creator>Tom Mornini</dc:creator>
				<category><![CDATA[News]]></category>
		<category><![CDATA[Technology]]></category>

		<guid isPermaLink="false">http://www.engineyard.com/blog/?p=2635</guid>
		<description><![CDATA[Several months ago, Engine Yard created the Rails Development Directory. We created this directory because we didn&#8217;t see a web-based resource that listed professional web development firms with Rails experience that had the oomph of customer endorsements. There was &#8220;Working with Rails&#8221;, which was more focused on individual developers, and then for true freelancers there [...]]]></description>
			<content:encoded><![CDATA[<p>Several months ago, Engine Yard created the <a title="Rails Development Directory" href="http://www.railsdevelopment.com/">Rails Development Directory</a>. We created this directory because we didn&#8217;t see a web-based resource that listed professional web development firms with Rails experience that had the oomph of customer endorsements. There was &#8220;Working with Rails&#8221;, which was more focused on individual developers, and then for true freelancers there was oDesk, but nothing for this part of the market.</p>
<p>The model for the directory was that any firm could add an account, and once they received three customer endorsements, they would be checked as &#8220;confirmed&#8221; and prioritized in search results. Firms could also add up to three &#8220;showcase&#8221; projects so that customers could click through and see the quality of work. The goal was to let the work (and the customer endorsements) speak for themselves.</p>
<p>So far, the directory has been pretty successful. It now has about 250 development firms listed. Although we built in a &#8220;request for work&#8221; submission feature, it&#8217;s mostly being used by potential development customers as a way to research firms, rather than as a way to submit work requests, but we have high hopes here. So far, we&#8217;ve had a few problems with spam accounts and bogus customer endorsements but not many. As far as development details, the site was built from scratch in Ruby on Rails in 3 weeks, then added 3 weeks of polish before launch.</p>
<p>There are 272 customer endorsements for member work, and as you&#8217;ll see from the code, currently, the formula used to return search results is pretty basic:</p>
<ol>
<li>first it includes companies with matching or lower minimum budgets</li>
<li>then it includes companies with matching locations (by state in the US and by country everywhere else &#8211; this filter is optional)</li>
<li>then it orders by whether a company is confirmed (has 3 endorsements)</li>
<li>then it orders by how many endorsements a company has</li>
<li>then it returns results randomly.</li>
</ol>
<p>If your company profile is confirmed and you have lots of endorsements, all else being equal, you will rank higher in the search results. But in our experience, matching by budget and location is very important, as it is a qualifying step for most potential customers.</p>
<p>Continuing our support of open source projects and initiatives, and after discussing the directory&#8217;s future with some of the community, we felt it made sense to release this directory application as open source, and today we&#8217;re officially releasing the source code of the application in a generalized form that can quickly and easily be customized and put into use by anyone that needs a directory site.</p>
<p>You can find the Rails Directory source code released under the MIT License on GitHub at <a href="http://github.com/engineyard/rails_dev_directory">http://github.com/engineyard/rails_dev_directory</a>.</p>
<p>Feel free to fork the repo and review it, play with it, or modify it as you see fit. If you&#8217;re interested in contributing to the source code, or have any feedback, drop us a line at info@railsdevelopment.com.</p>
<p>Credit for developing the application in large part goes to <a href="http://github.com/paulca">Paul Campbell</a>, Nick French and Ben De Jesus here at Engine Yard.</p>
<img src="http://www.engineyard.com/blog/?ak_action=api_record_view&id=2635&type=feed" alt="" /><img src="http://feeds.feedburner.com/~r/engineyard/~4/q6QXkC1dnxE" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://www.engineyard.com/blog/2009/open-sourcing-rails-development-directory-code/feed/</wfw:commentRss>
		<slash:comments>4</slash:comments>
		<feedburner:origLink>http://www.engineyard.com/blog/2009/open-sourcing-rails-development-directory-code/</feedburner:origLink></item>
		<item>
		<title>Rails in the Wild: 5 Client-Side Performance Observations</title>
		<link>http://feedproxy.google.com/~r/engineyard/~3/z1eg3X35hAU/</link>
		<comments>http://www.engineyard.com/blog/2009/rails-in-the-wild-5-client-side-performance-observations/#comments</comments>
		<pubDate>Tue, 13 Oct 2009 17:30:30 +0000</pubDate>
		<dc:creator>Michael Mullany</dc:creator>
				<category><![CDATA[Technology]]></category>
		<category><![CDATA[CSS]]></category>
		<category><![CDATA[JavaScript]]></category>
		<category><![CDATA[Rails]]></category>
		<category><![CDATA[Rails Performance]]></category>
		<category><![CDATA[S3]]></category>

		<guid isPermaLink="false">http://www.engineyard.com/blog/?p=2618</guid>
		<description><![CDATA[We're putting together the presentation materials for our five-city "Rails Performance in the Cloud" Roadshow at the end of the month (Boston, Austin, Seattle, LA and Chicago). We'll be presenting some findings on page load performance for a casual survey sample of 100 North American Rails web-sites. Since some of the findings are not what you'd expect, we thought we'd share some of these early findings with you (come to the Roadshow and be the first to hear the rest of the analysis!).]]></description>
			<content:encoded><![CDATA[<p>We&#8217;re putting together the presentation materials for our five-city &#8220;Rails Performance in the Cloud&#8221; Roadshow at the end of the month (<a href="http://www.railsroadshow.com/location-boston.html">Boston</a>, <a href="http://www.railsroadshow.com/location-austin.html">Austin</a>, <a href="http://www.railsroadshow.com/location-seattle.html">Seattle</a>, <a href="http://www.railsroadshow.com/location-los-angeles.html">LA</a> and <a href="http://www.railsroadshow.com/location-chicago.html">Chicago</a>). We&#8217;ll be presenting some findings on page load performance for a casual survey sample of 100 North American Rails web-sites. Since some of the findings are not what you&#8217;d expect, we thought we&#8217;d share some of these early findings with you (come to the <a href="http://www.railsroadshow.com">Roadshow</a> and be the first to hear the rest of the analysis!).</p>
<h2>1. It&#8217;s easy to forget to compress your JavaScript and CSS</h2>
<p>It seems like people are pretty good about gzipping their HTML and images, but for whatever reason, a lot of people forget to tell <code>mod_deflate</code> to compress JavaScript and CSS files. JavaScript payloads are becoming a much bigger percentage of total downloads (even a majority of the payload for many sites), and they&#8217;re blocking the rest of the page from loading.</p>
<h2>2. Watch out for slow third party services</h2>
<p>Some of the big outliers in page load performance are caused by poor response times from third party services. Services like Google Ads and Analytics, Doubleclick, and Facebook Connect can kill your performance if you loading them early, so almost all sites (sensibly) put them as late as possible in the page load. Response times of up to eight seconds from Google Analytics are not uncommon, so this can result in a big road-bump in your page load if it&#8217;s in the wrong place. Google Analytics, in particular, uses document.write, so most people have to <a href="http://github.com/choonkeat/postload_google_ads">work around it</a>, but a significant minority of sites don&#8217;t.</p>
<h2>3. Using multiple image hosts doesn&#8217;t always mean higher performance</h2>
<p>There&#8217;s nothing magical about using multiple image hosts. It <em>should</em> produce higher performance by allowing parallel downloads, but only if you&#8217;ve put the necessary resources to work. An interesting performance outlier in the survey was a site that had configured multiple image hosts, but response times from those hosts were multi-second &#8212; probably a good sign that they were under-resourced.</p>
<h2>4. S3 is NOT a Webserver!</h2>
<p>Amazon S3 is a reliable, cheap storage service, but don&#8217;t treat it like just another web-server. Response time in the wild was regularly between 0.5 and 1.5 seconds, so make sure that you&#8217;re not serving performance sensitive content from it. And if you do, try to use pre-loading to hide latency. Unlike a regular web-server, S3 (still) does not gzip content, so you also need to use a pre-compression utility like Yahoo&#8217;s Smush.It to reduce image sizes before you put them up there.</p>
<h2>5. Most performance variability is NOT attributable to page factors</h2>
<p>When we did the analysis, we found that less than half of total page-load time in our sample was attributable to front-end factors like the number of http requests made by the page, the size of the page payload and whether or not you were scaling images in HTML. A majority of performance variability (a little surprisingly) was attributable to stuff that had nothing to do with page construction (basically network and back-end factors).</p>
<p>To learn more about what we found about average page response time, page size targets and http chatter, as well as what the analysis said about front-end performance, <a href="http://www.railsroadshow.com/register-now.html">sign up for the Rails Roadshow</a>, coming to a city near you in two weeks time. We&#8217;ll be presenting along with our partners New Relic, Soasta, Amazon, CVSDude and more.</p>
<p>See you there!</p>
<img src="http://www.engineyard.com/blog/?ak_action=api_record_view&id=2618&type=feed" alt="" /><img src="http://feeds.feedburner.com/~r/engineyard/~4/z1eg3X35hAU" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://www.engineyard.com/blog/2009/rails-in-the-wild-5-client-side-performance-observations/feed/</wfw:commentRss>
		<slash:comments>3</slash:comments>
		<feedburner:origLink>http://www.engineyard.com/blog/2009/rails-in-the-wild-5-client-side-performance-observations/</feedburner:origLink></item>
	</channel>
</rss>
