<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Coffee Powered &#187; Uncategorized</title>
	<atom:link href="http://www.coffeepowered.net/category/uncategorized/feed/" rel="self" type="application/rss+xml" />
	<link>http://www.coffeepowered.net</link>
	<description>code and content</description>
	<lastBuildDate>Mon, 09 Jan 2012 18:32:06 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.3</generator>
		<item>
		<title>Enabling brightness controls on an HP Envy 17 under Fedora 16</title>
		<link>http://www.coffeepowered.net/2011/11/13/enabling-brightness-controls-on-an-hp-envy-17-under-fedora-16/</link>
		<comments>http://www.coffeepowered.net/2011/11/13/enabling-brightness-controls-on-an-hp-envy-17-under-fedora-16/#comments</comments>
		<pubDate>Sun, 13 Nov 2011 21:01:15 +0000</pubDate>
		<dc:creator>Chris Heald</dc:creator>
				<category><![CDATA[Uncategorized]]></category>

		<guid isPermaLink="false">http://www.coffeepowered.net/?p=485</guid>
		<description><![CDATA[I&#8217;ve recently set up Fedora 16 on my laptop, and all has been smooth, save for the brightness switches. The on-screen display would show up when I used the fn-F2/fn-F3 key combinations, but the brightness just wouldn&#8217;t change. Additionally, the brightness was stuck at the lowest level. Turns out there&#8217;s a pretty easy fix in [...]]]></description>
			<content:encoded><![CDATA[<p>I&#8217;ve recently set up Fedora 16 on my laptop, and all has been smooth, save for the brightness switches. The on-screen display would show up when I used the fn-F2/fn-F3 key combinations, but the brightness just wouldn&#8217;t change. Additionally, the brightness was stuck at the lowest level.</p>
<p>Turns out there&#8217;s a pretty easy fix in the form of a couple of module parameters:</p>
<p>In /etc/defaults/grub, add the following kernel parameters:</p>
<pre class="brush: bash; title: ; notranslate">video.brightness_switch_enabled=1 video.use_bios_initial_backlight=0</pre>
<p>(You may also want to add <code>radeon.modeset=1</code> and <code>acpi_osi=Linux</code> for this particular machine, but they aren&#8217;t related to the brightness fix.)</p>
<p>Then update your grub2 config:</p>
<pre class="brush: bash; title: ; notranslate"> grub2-mkconfig &gt; /boot/grub2/grub.cfg </pre>
<p>Reboot, and your brightness controls should work as expected. The brightness slider in GNOME still doesn&#8217;t work, but I&#8217;m content with hardware brightness controls over no brightness controls.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.coffeepowered.net/2011/11/13/enabling-brightness-controls-on-an-hp-envy-17-under-fedora-16/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Comps &#8211; design vs reality comparisons in Chrome</title>
		<link>http://www.coffeepowered.net/2011/08/30/comps-design-vs-reality-comparisons-in-chrome/</link>
		<comments>http://www.coffeepowered.net/2011/08/30/comps-design-vs-reality-comparisons-in-chrome/#comments</comments>
		<pubDate>Wed, 31 Aug 2011 03:45:38 +0000</pubDate>
		<dc:creator>Chris Heald</dc:creator>
				<category><![CDATA[Uncategorized]]></category>
		<category><![CDATA[chrome]]></category>
		<category><![CDATA[Design]]></category>
		<category><![CDATA[Extensions]]></category>
		<category><![CDATA[Original Software]]></category>
		<category><![CDATA[Web Development]]></category>

		<guid isPermaLink="false">http://www.coffeepowered.net/?p=481</guid>
		<description><![CDATA[For a long time, I used the PixelPerfect Firefox add-on to compare rendered comps with my finished web work. This was a fast and effective way to make sure that I got the spacings, font sizes, and other such things done properly. However, PixelPerfect doesn&#8217;t work all that well (well, at all) anymore, and Firefox [...]]]></description>
			<content:encoded><![CDATA[<p>For a long time, I used the <a href="https://addons.mozilla.org/en-US/firefox/addon/pixel-perfect/">PixelPerfect Firefox add-on</a> to compare rendered comps with my finished web work. This was a fast and effective way to make sure that I got the spacings, font sizes, and other such things done properly.</p>
<p>However, PixelPerfect doesn&#8217;t work all that well (well, at all) anymore, and Firefox is no longer my browser of choice. There weren&#8217;t any good options for Chrome, so I wrote one.</p>
<p>You can <a href="https://chrome.google.com/webstore/detail/ginhbdfcoiigpedgaidclojolemiincd">grab it straight from the Chrome store</a> if you want (it&#8217;s free!), or you might be interested in <a href="https://github.com/cheald/Comps">perusing the source code</a>.</p>
<p>Comps is very lightweight, and gets straight to the point &#8211; click the button, drop your comp into the webpage, and position it to make direct comparisons. Mousewheel over the comp to change its opacity, or you can toggle it on and off using the thumbnail drawer or the Comps button. It&#8217;ll remember your settings between sessions (and even pageloads!) so that you can tweak and refresh to your heart&#8217;s content, and instantly have your comp right there for comparison.</p>
<p>Give it a shot, let me know if/how you like it.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.coffeepowered.net/2011/08/30/comps-design-vs-reality-comparisons-in-chrome/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Restarting Resque workers (or anything, really) with Monit, Passengers-style.</title>
		<link>http://www.coffeepowered.net/2011/08/19/restarting-resque-workers-or-anything-really-with-monit-passengers-style/</link>
		<comments>http://www.coffeepowered.net/2011/08/19/restarting-resque-workers-or-anything-really-with-monit-passengers-style/#comments</comments>
		<pubDate>Sat, 20 Aug 2011 05:15:10 +0000</pubDate>
		<dc:creator>Chris Heald</dc:creator>
				<category><![CDATA[Uncategorized]]></category>

		<guid isPermaLink="false">http://www.coffeepowered.net/?p=467</guid>
		<description><![CDATA[Easy way to trigger off a reload of a service managed by Monit without having to become root. In my case, I&#8217;ve got a monit service called resque-worker, and I can restart it by just touching tmp/resque-restart.txt. Ties in nicely with deploy tasks, and you don&#8217;t have to end up leaving root access SSH keypairs [...]]]></description>
			<content:encoded><![CDATA[<p>Easy way to trigger off a reload of a service managed by Monit without having to become root. In my case, I&#8217;ve got a monit service called resque-worker, and I can restart it by just touching <code>tmp/resque-restart.txt</code>.</p>
<pre class="brush: plain; title: ; notranslate">
check file resque-restart.txt with path /path/to/your/app/tmp/resque-restart.txt
  if changed timestamp then
    exec &quot;/usr/bin/monit restart resque-worker&quot;
</pre>
<p>Ties in nicely with deploy tasks, and you don&#8217;t have to end up leaving root access SSH keypairs laying around.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.coffeepowered.net/2011/08/19/restarting-resque-workers-or-anything-really-with-monit-passengers-style/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Hack night at Gangplank</title>
		<link>http://www.coffeepowered.net/2010/11/17/hack-night-at-gangplank/</link>
		<comments>http://www.coffeepowered.net/2010/11/17/hack-night-at-gangplank/#comments</comments>
		<pubDate>Thu, 18 Nov 2010 02:36:44 +0000</pubDate>
		<dc:creator>Chris Heald</dc:creator>
				<category><![CDATA[Uncategorized]]></category>

		<guid isPermaLink="false">http://www.coffeepowered.net/?p=337</guid>
		<description><![CDATA[So, I finally made it down to Gankplank. I&#8217;ve been meaning to get down here for a while, but I&#8217;ve just not made it happen. I took the weekend to get my development environment up to snuff on my laptop (so I can actually work anywhere now!) and decided to give it a shot. This [...]]]></description>
			<content:encoded><![CDATA[<p>So, I finally made it down to <a href="http://gangplankhq.com/">Gankplank</a>. I&#8217;ve been meaning to get down here for a while, but I&#8217;ve just not made it happen. I took the weekend to get my development environment up to snuff on my laptop (so I can actually work anywhere now!) and decided to give it a shot.</p>
<p>This place is cool. Very cool. The atmosphere is invigorating &#8211; it&#8217;s exactly the sort of place I&#8217;d love to really put in some serious time at. Smart people, lots of technology, and a laid-back, fun, productive atmosphere. Awesome.</p>
<p>It also highlights fairly starkly the fact that I don&#8217;t have much in the way of &#8220;hack&#8221; projects to work on. Most all my stuff these days is Real Work(TM), which, good as it is, isn&#8217;t the sort of thing that you can really unwind with. I need a project that I can work on and let it fail &#8211; something I can sink some serious creative juices into. I want to break out of what I&#8217;ve been working on for 90 hours per week for the last couple of years, and sink my teeth into something truly creative.</p>
<p>I&#8217;ve got a lot of directions I could go &#8211; I have a few webapps in mind, some node.js ideas, a few experiments with MongoDB. I want to do more Android development and get better with Java; I want to learn Clojure and maybe even poke at Haskell. I want to find an excuse to use Amazon&#8217;s compute clusters to solve some ridiculously huge-scale problem. More than that, I want to find out what I don&#8217;t know about yet, and put some effort towards learning it.</p>
<p>I don&#8217;t have a good answer for what I want to do tonight, but it&#8217;s a good starting point. I&#8217;m excited at the opportunity and sheer energy this place provides &#8211; a place to work, to be creative, to talk and play and laugh and hack and enjoy. I&#8217;ll find something interesting to do yet.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.coffeepowered.net/2010/11/17/hack-night-at-gangplank/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Solving error n8156-6003 when trying to play Netflix Watch Instantly content.</title>
		<link>http://www.coffeepowered.net/2010/09/18/solving-error-n8156-6003-when-trying-to-play-netflix-watch-instantly-content/</link>
		<comments>http://www.coffeepowered.net/2010/09/18/solving-error-n8156-6003-when-trying-to-play-netflix-watch-instantly-content/#comments</comments>
		<pubDate>Sun, 19 Sep 2010 00:07:16 +0000</pubDate>
		<dc:creator>Chris Heald</dc:creator>
				<category><![CDATA[Uncategorized]]></category>
		<category><![CDATA[drm]]></category>
		<category><![CDATA[n8156-6003]]></category>
		<category><![CDATA[netflix]]></category>
		<category><![CDATA[watch instantly]]></category>

		<guid isPermaLink="false">http://www.coffeepowered.net/?p=332</guid>
		<description><![CDATA[This is mostly search engine bait, because I couldn&#8217;t find a solution on my own when searching, but managed to stumble across it anyhow. I recently did a Windows 7 x64 reinstall, and after doing so, Netflix wouldn&#8217;t play in any of my clients &#8211; Windows Media Center, Chrome, IE, you name it. After various [...]]]></description>
			<content:encoded><![CDATA[<p>This is mostly search engine bait, because I couldn&#8217;t find a solution on my own when searching, but managed to stumble across it anyhow.</p>
<p>I recently did a Windows 7 x64 reinstall, and after doing so, Netflix wouldn&#8217;t play in any of my clients &#8211; Windows Media Center, Chrome, IE, you name it. After various solutions (DRM reset, security component upgrade, Silverlight uninstall and reinstall), it turns out the solution is stupidly easy:</p>
<p>Run your browser as an Administrator. Open Netflix, start watching a movie. Once it successfully starts playing, you can close your browser and then resume watching whatever you&#8217;d like to watch.</p>
<p>I can only guess that some initial setup doesn&#8217;t get done properly, and it fails if you&#8217;re trying to do the initial license acquisition in a non-elevated program. Hopefully this saves someone else a headache!</p>
]]></content:encoded>
			<wfw:commentRss>http://www.coffeepowered.net/2010/09/18/solving-error-n8156-6003-when-trying-to-play-netflix-watch-instantly-content/feed/</wfw:commentRss>
		<slash:comments>5</slash:comments>
		</item>
		<item>
		<title>FlexAuth: Portable authentication for Battle.net</title>
		<link>http://www.coffeepowered.net/2010/06/10/flexauth-portable-authentication-for-battle-net/</link>
		<comments>http://www.coffeepowered.net/2010/06/10/flexauth-portable-authentication-for-battle-net/#comments</comments>
		<pubDate>Thu, 10 Jun 2010 22:58:14 +0000</pubDate>
		<dc:creator>Chris Heald</dc:creator>
				<category><![CDATA[Uncategorized]]></category>

		<guid isPermaLink="false">http://www.coffeepowered.net/?p=257</guid>
		<description><![CDATA[I&#8217;ve just released my first Android app, called FlexAuth. It&#8217;s mostly an excuse to learn Android development, but it does something useful, too &#8211; it serves as a souped-up mobile authenticator for Blizzard&#8217;s Battle.net login infrastructure. If you&#8217;d like the gory details, there&#8217;s a specification floating around on the internet that&#8217;ll help you understand the [...]]]></description>
			<content:encoded><![CDATA[<p>I&#8217;ve just released my first Android app, called <a href="http://www.cyrket.com/p/android/com.chrisheald.flexauth/">FlexAuth</a>. It&#8217;s mostly an excuse to learn Android development, but it does something useful, too &#8211; it serves as a souped-up mobile authenticator for Blizzard&#8217;s Battle.net login infrastructure. If you&#8217;d like the gory details, <a href="http://bnetauth.freeportal.us/specification.html">there&#8217;s a specification floating around on the internet</a> that&#8217;ll help you understand the protocol.</p>
<p><a href="http://i.imgur.com/yW496.png"><img src="http://i.imgur.com/yW496.png" align="right" style="margin: 1em; width:200px;" /></a><br />
Mobile authenticators work by transforming a seed value (called the &#8220;token secret&#8221;) + the current time into your 8-digit authentication code. FlexAuth lets you set up multiple authenticators by providing the secret, or will let you have Blizzard generate one for you.</p>
<p>Why would you need this?</p>
<ul>
<li>You want to use a mobile authenticator, but don&#8217;t want to be locked out if you ever lose your phone (just setup a new token with your registered token secret).</li>
<li>You want to use multiple mechanisms to log in &#8211; maybe you need token authentication in a script, or you want to have the same authenticator values on multiple mobile phones.</li>
<li>You already have a token secret from another source and want to use it on your mobile phone.</li>
</ul>
<p>Obviously, these won&#8217;t apply to most people, but some folks will definitely find it useful.</p>
<hr style="clear: both;" />
<a href="http://i.imgur.com/NbAGQ.png"><img src="http://i.imgur.com/NbAGQ.png" align="right" style="margin: 1em; width:200px;" /></a><br />
Using it:</p>
<ol>
<li>Menu -> Add Account</li>
<li>Enter a name for this token/account. It can be whatever you&#8217;d like.</li>
<li>Either enter a serial + secret, or you can use the already-provided one, or generate a new one.</li>
<li>Save the token. You&#8217;ll notice that auth codes start generating right away.</li>
<li>It is highly recommended that you back up your token secret. If you uninstall the app, wipe your phone, etc, then you will lose the secret, and consequently lose the ability to generate auth codes. To back up a code, click into the token&#8217;s details, and long press on the secret to copy it. You can then paste it into a note or email or whatnot. To restore a token, simply generate a new token and use your backed up secret. It will generate compatible auth codes.</li>
</ol>
<p>All that said, <span style="color: #ff0000;">a word of caution</a>: <b>Never ever ever run authenticator software on the same machine that you&#8217;re logging in on.</b> It&#8217;s bad, it&#8217;s dumb, and you shouldn&#8217;t do it. Keep your authentication token generation on a separate device if you value your account.</p>
<p>If <a href="http://www.wowwiki.com/Battle.net_Mobile_Authenticator#Desktop_port">any particular same-machine authentication scheme</a> gained any measure of popularity, it would be targeted by malware and your authenticator would be useless. Don&#8217;t do it.</p>
<p>Other than that, enjoy!</p>
]]></content:encoded>
			<wfw:commentRss>http://www.coffeepowered.net/2010/06/10/flexauth-portable-authentication-for-battle-net/feed/</wfw:commentRss>
		<slash:comments>5</slash:comments>
		</item>
		<item>
		<title>Serving files out of GridFS</title>
		<link>http://www.coffeepowered.net/2010/02/17/serving-files-out-of-gridfs/</link>
		<comments>http://www.coffeepowered.net/2010/02/17/serving-files-out-of-gridfs/#comments</comments>
		<pubDate>Wed, 17 Feb 2010 20:54:11 +0000</pubDate>
		<dc:creator>Chris Heald</dc:creator>
				<category><![CDATA[Uncategorized]]></category>

		<guid isPermaLink="false">http://www.coffeepowered.net/?p=233</guid>
		<description><![CDATA[GridFS is a nifty little feature in MongoDB that allows you to store files of all shapes and sizes in Mongo itself, getting the benefits of Mongo&#8217;s sharding and replication. However, since they&#8217;re in a database, and not on the filesystem directly, how do we serve them? There are lots of benchmarks and numbers under [...]]]></description>
			<content:encoded><![CDATA[<p><a href="http://www.mongodb.org/display/DOCS/GridFS+Specification">GridFS</a> is a nifty little feature in <a href="http://www.mongodb.org/display/DOCS/Home">MongoDB</a> that allows you to store files of all shapes and sizes in Mongo itself, getting the benefits of Mongo&#8217;s sharding and replication. However, since they&#8217;re in a database, and not on the filesystem directly, how do we serve them?</p>
<p>There are lots of benchmarks and numbers under the cut. Keep reading!</p>
<p><span id="more-233"></span></p>
<p>Right now, there are three options:</p>
<ol>
<li>Use a &#8220;low-level&#8221; script handler, like a Rack script or Rails Metal handler to serve them out of the database</li>
<li>Use something like <a href="http://github.com/mikejs/gridfs-fuse/">gridfs-fuse</a> to mount the database as a filesystem, and read it with the Fileserver directly</li>
<li>Use something like <a href="http://github.com/mdirolf/nginx-gridfs">nginx-gridfs</a> to talk directly to MongoDB from your webserver.</li>
</ol>
<p>I wasn&#8217;t able to get gridfs-fuse to build on my system, but I was able to build the nginx module. The question, of course, is how fast are you going be serving files with each solution?</p>
<h2>Filesystem read through Apache</h2>
<p>First, I&#8217;ll establish a baseline. I&#8217;m running Apache as my frontend server, and we&#8217;ll use ab to benchmark its throughput.</p>
<pre class="brush: ruby; title: ; notranslate">[chris@polaris conf]# ab -n 50000 -c 10 http://advice/images/embed/alliance-60.png

Server Software:        Apache/2.2.13
Server Hostname:        advice
Server Port:            80

Document Path:          /images/embed/normal_alliance-60.png
Document Length:        31596 bytes

Concurrency Level:      10
Time taken for tests:   1.904 seconds
Complete requests:      5000
Failed requests:        0
Write errors:           0
Total transferred:      159463760 bytes
HTML transferred:       158043192 bytes
Requests per second:    2625.37 [#/sec] (mean)
Time per request:       3.809 [ms] (mean)
Time per request:       0.381 [ms] (mean, across all concurrent requests)
Transfer rate:          81767.87 [Kbytes/sec] received

Connection Times (ms)
              min  mean[+/-sd] median   max
Connect:        0    1   0.4      1       4
Processing:     1    3   0.5      3       6
Waiting:        0    1   0.4      1       4
Total:          2    4   0.4      4       8

Percentage of the requests served within a certain time (ms)
  50%      4
  66%      4
  75%      4
  80%      4
  90%      4
  95%      4
  98%      5
  99%      5
 100%      8 (longest request)
</pre>
<p>Nice and fast, like like we&#8217;d expect.</p>
<h2>Filesystem read through nginx</h2>
<pre class="brush: ruby; title: ; notranslate">[chris@polaris conf]# ab -n 50000 -c 10 http://advice:81/images/embed/normal_alliance-60.png

Server Software:        nginx/0.8.33
Server Hostname:        advice
Server Port:            81

Document Path:          /images/embed/normal_alliance-60.png
Document Length:        31596 bytes

Concurrency Level:      10
Time taken for tests:   7.623 seconds
Complete requests:      50000
Failed requests:        0
Write errors:           0
Total transferred:      1590513618 bytes
HTML transferred:       1579863192 bytes
Requests per second:    6559.31 [#/sec] (mean)
Time per request:       1.525 [ms] (mean)
Time per request:       0.152 [ms] (mean, across all concurrent requests)
Transfer rate:          203763.10 [Kbytes/sec] received

Connection Times (ms)
              min  mean[+/-sd] median   max
Connect:        0    0   0.2      0       9
Processing:     1    1   0.4      1      11
Waiting:        0    0   0.1      0       9
Total:          1    1   0.5      1      12

Percentage of the requests served within a certain time (ms)
  50%      1
  66%      1
  75%      1
  80%      2
  90%      2
  95%      2
  98%      3
  99%      3
 100%     12 (longest request)
</pre>
<p>nginx <i>screams</i>. At 6500 requests/sec, it&#8217;s blisteringly fast.</p>
<h2>GridFS read through nginx-gridfs</h2>
<pre class="brush: ruby; title: ; notranslate">[chris@polaris conf]# ab -n 5000 -c 10 http://advice:81/images/gfs/uploads/user/avatar/4b7b2c0e98db7475fc000003/normal_alliance-60.png

Server Software:        nginx/0.8.33
Server Hostname:        advice
Server Port:            81

Document Path:          /images/gfs/uploads/user/avatar/4b7b2c0e98db7475fc000003/normal_alliance-60.png
Document Length:        31596 bytes

Concurrency Level:      10
Time taken for tests:   4.613 seconds
Complete requests:      5000
Failed requests:        0
Write errors:           0
Total transferred:      158580000 bytes
HTML transferred:       157980000 bytes
Requests per second:    1083.88 [#/sec] (mean)
Time per request:       9.226 [ms] (mean)
Time per request:       0.923 [ms] (mean, across all concurrent requests)
Transfer rate:          33570.65 [Kbytes/sec] received

Connection Times (ms)
              min  mean[+/-sd] median   max
Connect:        0    0   0.0      0       1
Processing:     1    9   4.7      9     103
Waiting:        1    9   4.7      9     102
Total:          2    9   4.7      9     103

Percentage of the requests served within a certain time (ms)
  50%      9
  66%      9
  75%      9
  80%      9
  90%      9
  95%      9
  98%      9
  99%     11
 100%    103 (longest request)
</pre>
<p>Definitely a lot slower, but still very respectable. 1051 requests/sec is going to be more than adequate for most purposes, particularly if fronted with a CDN.</p>
<p>And finally&#8230;</p>
<h2>Rails Metal handler</h2>
<p>The nice thing about the Rails metal handler solution is that it&#8217;s easy. No recompiling, just drop the handler into your project and you&#8217;re off to the races. That said&#8230;</p>
<pre class="brush: ruby; title: ; notranslate">[chris@polaris nginx-gridfs]$ ab -n 250 -c 4  http://advice/images/gfs/uploads/user/avatar/4b7b2c0e98db7475fc000003/normal_alliance-60.png

Server Software:        Apache/2.2.13
Server Hostname:        advice
Server Port:            80

Document Path:          /images/gfs/uploads/user/avatar/4b7b2c0e98db7475fc000003/normal_alliance-60.png
Document Length:        31596 bytes

Concurrency Level:      4
Time taken for tests:   4.646 seconds
Complete requests:      250
Failed requests:        0
Write errors:           0
Total transferred:      7960000 bytes
HTML transferred:       7899000 bytes
Requests per second:    53.81 [#/sec] (mean)
Time per request:       74.338 [ms] (mean)
Time per request:       18.585 [ms] (mean, across all concurrent requests)
Transfer rate:          1673.10 [Kbytes/sec] received

Connection Times (ms)
              min  mean[+/-sd] median   max
Connect:        0    0   0.1      0       1
Processing:    15   74  75.6     34     287
Waiting:        0   72  75.8     30     276
Total:         15   74  75.6     34     288

Percentage of the requests served within a certain time (ms)
  50%     34
  66%     39
  75%    139
  80%    192
  90%    201
  95%    210
  98%    239
  99%    245
 100%    288 (longest request)
</pre>
<p>I obviously ran far fewer requests this go-round. The reason is pretty obvious &#8211; running 5000 requests through the Ruby stack would have taken approximately <em>forever</em>. At 53 requests per second, this is not an attractive solution, particularly if you consider the CPU overhead that it&#8217;s incurring.</p>
<h2>Conclusions</h2>
<table class='data' border='1'>
<tr>
<th>Solution</th>
<th>Requests/second</th>
<th>% Apache FS</th>
<th>% Nginx FS</th>
<th>% Nginx GridFS</th>
<th>% Apache Ruby</th>
</tr>
<tr>
<td>Filesystem via Apache</th>
<td>2625.37</td>
<td>-</td>
<td>40.03%</td>
<td>242.22%</td>
<td>4,878.96%</td>
</tr>
<tr>
<td>Filesystem via Nginx</th>
<td>6559.31</td>
<td>249.84%</td>
<td>-</td>
<td>605.17%</td>
<td>12,189.76%</td>
</tr>
<tr>
<td>GridFS via nginx module</th>
<td>1083.88</td>
<td>41.28%</td>
<td>16.52%</td>
<td>-</td>
<td>2014.27%</td>
</td>
</tr>
<tr>
<td>Rails metal handler via Passenger</th>
<td>53.81</td>
<td>2.05%</td>
<td>0.82%</td>
<td>4.96%</td>
<td>-</td>
</tr>
</table>
<p>If you&#8217;re looking to abstract away from storing files on a filesystem, GridFS is a feasable solution. It can really crank some mean output numbers, and though it&#8217;s not up to par with a raw filesystem read, also consider that in many production environments, such a raw filesystem read might be happening via an NFS or GFS share, which is going to massively degrade the performance of that request. Given the no-hassle store-and-forget-about-it solution that GridFS offers, even when faced with the challenge of multi-server replication, it seems that you can get enough performance out of it to justify it as a solution.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.coffeepowered.net/2010/02/17/serving-files-out-of-gridfs/feed/</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
		<item>
		<title>Fine tuning your garbage collector</title>
		<link>http://www.coffeepowered.net/2009/06/13/fine-tuning-your-garbage-collector/</link>
		<comments>http://www.coffeepowered.net/2009/06/13/fine-tuning-your-garbage-collector/#comments</comments>
		<pubDate>Sun, 14 Jun 2009 02:52:13 +0000</pubDate>
		<dc:creator>Chris Heald</dc:creator>
				<category><![CDATA[Rails]]></category>
		<category><![CDATA[Ruby]]></category>
		<category><![CDATA[Uncategorized]]></category>

		<guid isPermaLink="false">http://www.coffeepowered.net/?p=173</guid>
		<description><![CDATA[If you&#8217;re familiar with Ruby at all, you know that it can be a little wacky when it comes to memory usage. Most of us have observed a Mongrel/Passenger instance that starts out small and then grows by leaps and bounds, eventually settling on some uncomfortably high number. We&#8217;re going to fix that with Ruby [...]]]></description>
			<content:encoded><![CDATA[<p>If you&#8217;re familiar with Ruby at all, you know that it can be a little wacky when it comes to memory usage. Most of us have observed a Mongrel/Passenger instance that starts out small and then grows by leaps and bounds, eventually settling on some uncomfortably high number. We&#8217;re going to fix that with <a href="http://www.rubyenterpriseedition.com/">Ruby Enterprise Edition</a> and <a href="http://github.com/cheald/scrap/tree/master">Scrap</a>.<br />
<span id="more-173"></span><br />
The Ruby garbage collector&#8217;s behavior is controlled by a number of constants. In the MRI, these are compiled into Ruby itself, and don&#8217;t change. However, if you&#8217;re using REE you can override them with environment variables on startup. It&#8217;s terribly handy.</p>
<h3>First, the boring documentation</h3>
<p>All the juicy information is available <a href="http://www.rubyenterpriseedition.com/documentation.html#_garbage_collector_performance_tuning">in the documentation</a>, but I&#8217;m going to just go over the key points real quick.</p>
<p><code>RUBY_HEAP_MIN_SLOTS</code>: This is the number of &#8220;heap slots&#8221; that each Ruby instance starts up with. One heap slot can hold one Ruby object. By default, this is 10,000. By controlling this value, we can get our apps to stabilize very quickly. More on this later.</p>
<p><code>RUBY_HEAP_SLOTS_INCREMENT</code>: Once Ruby has allocated <code>RUBY_HEAP_MIN_SLOTS</code> objects on its first heap, it will have to allocate a second heap to make room for more. This variable controls the size of this second heap, and sets the baseline for future heaps, as well.</p>
<p><code>RUBY_HEAP_SLOTS_GROWTH_FACTOR</code>: For heaps #3 and onward, Ruby uses <code>RUBY_HEAP_SLOTS_INCREMENT</code> and this value to determine the size to allocate for the new heap. By default, this is 1.8, meaning that your third heap will end up with 10,000 * 1.8 = 18,000 slots in it.</p>
<p><code>RUBY_HEAP_FREE_MIN</code>: After each garbage collection run, if the number of free slots is less than <code>RUBY_HEAP_FREE_MIN</code>, a new heap will be allocated. The default is 4096.</p>
<p>So, let&#8217;s look at this practically. Presume that we have a Rails process that is going to require 50,000 Ruby objects before it&#8217;s fully initialized. The allocation process, when at defaults, will look something like this:</p>
<p>Allocate 10,000 slots (10,000 total available)<br />
Allocate 10,000 slots (20,000 total available)<br />
Allocate 18,000 slots (38,000 total available)<br />
Allocate 68,400 slots (106,400 total available)</p>
<p>So, we end up with about 53% more slots than we actually needed, and it took us four heap allocations to even boot the process. Surely we can do better.</p>
<h3>Enter Scrap.</h3>
<p><a href="http://github.com/cheald/scrap/tree/master">Scrap</a> is a little <a href="http://weblog.rubyonrails.org/2008/12/17/introducing-rails-metal">Metal</a> handler I wrote for tracking memory usage and garbage statistics over an instance&#8217;s lifetime. Installing it is trivial &#8211; just drop it into your vendor directory, restart your app, and navigate to <code>http://yoururl.com/stats/scrap</code>.</p>
<p>With this in hand, we can peek our memory usage and see what we can see.</p>
<p>There are some stats at the top, but for our purposes, we&#8217;re interested in the per-request garbage statistics. The newest request is near the top of the file, and the oldest request is at the bottom of the file. The last 50 requests are tracked. Each request looks something like this:</p>
<pre><code>
[71.92 MB] GET /apps/176568-WordPress

Number of objects    : 817571 (658305 AST nodes, 80.52%)
Heap slot size       : 20
GC cycles so far     : 503
Number of heaps      : 7
Total size of objects: 15968.18 KB
Total size of heaps  : 18036.81 KB (2068.63 KB = 11.47% unused)
Leading free slots   : 27104 (529.38 KB = 2.93%)
Trailing free slots  : 1 (0.02 KB = 0.00%)
Number of contiguous groups of 16 slots: 2829 (4.90%)
Number of terminal objects: 4307 (0.47%)
</code></pre>
<p>Key points here for the time being are <code>Number of objects</code> and <code>Number of heaps</code>. When we look at the number of objects &#8211; in this case, 817,000, it&#8217;s obvious that we&#8217;re going to have to allocate a number of heaps to handle all those objects. Rails&#8217; boot-up cost is fairly significant, and the default Ruby settings just really don&#8217;t cut it here. As you can see, we&#8217;ve allocated 7 heaps, and we&#8217;re using 15.9 of 18.0 MB allocated to the heap. Once a heap is allocated, it&#8217;s never de-allocated, so we&#8217;re perma-stuck at 18 MB of heap usage. Note that this isn&#8217;t the size of all the data in the program &#8211; just the space allocated for objects. A string that contains 100MB of data will only consume 20 bytes (that&#8217;s the &#8220;heap slot size &#8211; the amount of memory each object on the heap consumes&#8221;) on the heap. </p>
<p>However, what if we could just allocate the whole startup cost in the initial heap, and save ourselves the problems of having to reallocate so often?</p>
<p>We note that we have 891k slots allocated, so we can guesstimate at a number to set our initial allocation to. In my production app, I set mine to 1,250,000 &#8211; I was observing peaks around the 1,100,000 mark, and just increased it by 10% and rounded up.</p>
<p>So, my first custom environment variable is </p>
<p><code>RUBY_HEAP_MIN_SLOTS=1250000</code></p>
<p>And it results in something like this on the app&#8217;s first boot:</p>
<p>[137.99 MB] GET /movies/7505-Star-Wars-Episode-V-The-Empire-Strikes-Back</p>
<pre><code>Number of objects    : 933037 (664785 AST nodes, 71.25%)
Heap slot size       : 20
GC cycles so far     : 12
Number of heaps      : 1
Total size of objects: 18223.38 KB
Total size of heaps  : 24414.08 KB (6190.70 KB = 25.36% unused)
Leading free slots   : 316963 (6190.68 KB = 25.36%)
Trailing free slots  : 0 (0.00 KB = 0.00%)
Number of contiguous groups of 16 slots: 19810 (25.36%)
Number of terminal objects: 25941 (2.08%)</code></pre>
<p>Yowza, a full 25% of my heap is unused after boot. But&#8230;well, that&#8217;s okay. We&#8217;ve only allocated 1 heap, and later on, my object allocation grows to around 1,100,000. This is still 15k under the heap size, and I&#8217;ve set <code>RUBY_HEAP_FREE_MIN=12500</code> (1% of the initial size), so if I have less than 12,500 heap objects free after a GC cycle, a new heap will be allocated. Stabilizing there means that I end up with 1 heap for the lifetime of my app, and I end up sitting just under the threshold that&#8217;d cause a new heap to be born. If I have a leak, or a super heavy action or something, though, that might kick me over my limit and require a new heap. So, we come to&#8230;</p>
<p><code>RUBY_HEAP_SLOTS_INCREMENT=100000</code></p>
<p>This value says &#8220;Hey, if you have to allocate a second heap, start with this many slots&#8221;. If we go over our limit of 1.25 million slots, we&#8217;ll allocate a second heap that&#8217;s about 8% the size of the original. That seems awfully small, but consider that we&#8217;re hoping to never get to that heap.</p>
<p>Should we end up using that entire second heap, then we have to worry about our third setting, <code>RUBY_HEAP_SLOTS_GROWTH_FACTOR=1</code>. This says &#8220;Each new heap should be 1.0 as large as the previous heap.&#8221; In this case, it means I&#8217;ll keep allocating 100k-slot heaps until the cows come home. In an untuned environment, this could be bad &#8211; we would either end up having to do a <em>ton</em> of allocations to get to our target, or we would overallocate very badly. However, because we know our app&#8217;s memory requirements, and know about where we want it to end up, a relatively small, linear growth factor is just what the doctor ordered here.</p>
<h3>Okay, now what?</h3>
<p>So, we have a collection of settings with which to run our app. Great! Now, how do we use it?</p>
<p>Fortunately, it&#8217;s easy.</p>
<pre><code>
pushd `which ruby | xargs dirname`
sudo vim ruby-with-env
</code></pre>
<p>We&#8217;re going to create a little bash script with the following:</p>
<pre class="brush: ruby; title: ; notranslate">
#!/bin/bash
export RUBY_HEAP_MIN_SLOTS=1250000
export RUBY_HEAP_SLOTS_INCREMENT=100000
export RUBY_HEAP_SLOTS_GROWTH_FACTOR=1
export RUBY_GC_MALLOC_LIMIT=30000000
export RUBY_HEAP_FREE_MIN=12500
exec &quot;/opt/ree/bin/ruby&quot; &quot;$@&quot;
</pre>
<p>Note that last line &#8211; the path will have to match the path to your Ruby executable, which fortunately, should be in the directory that you&#8217;re in.</p>
<p>Save it, don&#8217;t forget to <code>chmod a+x ruby-with-env</code>, and then edit your Apache or nginx configuration.</p>
<p>Under nginx, you&#8217;ll have a line like this:</p>
<p><code>passenger_ruby /opt/ruby-enterprise-1.8.6-20090610/bin/ruby;</code></p>
<p>Just change it to use your new wrapper script, like so:</p>
<p><code>passenger_ruby /opt/ruby-enterprise-1.8.6-20090610/bin/ruby-with-env;</code></p>
<p>The process is similarly easy for Apache &#8211; the line you need is something like:</p>
<p><code>PassengerRuby /opt/ruby-enterprise-1.8.6-20090610/bin/ruby</code></p>
<p>It might be in either your <code>httpd.conf</code> or <code>conf.d/passenger.conf</code>.</p>
<p>Once you&#8217;re all edited up, restart your webserver, and congratulations, you&#8217;ve got a fine-tuned garbage collector humming along with your app.</p>
<h3>Taking out the garbage</h3>
<p>&#8220;But Chris!&#8221;, you say, &#8220;There&#8217;s a variable in there that you didn&#8217;t talk about! What gives?&#8221; You are indeed correct, astute reader. We&#8217;ve thus far avoided the <code>RUBY_GC_MALLOC_LIMIT</code> variable. This is a handle little setting that lets you tell Ruby how often to clean up after itself. Ruby is written in C, and C uses <code>malloc</code> to allocate memory. Ruby just keeps a little counter each time it allocates an object with malloc, and it runs its garbage collector after so many malloc calls have been made. I haven&#8217;t found a great way to tune this one yet, except via experimentation, but here&#8217;s what to know about it:</p>
<ol>
<li>The lower this value is, the more often your garbage collector runs. Garbage collection is slow. Garbage collection is painfully slow. If a user is waiting on garbage collection, they are going to become impatient. You want as few users waiting on garbage collection as possible.</li>
<li>The higher this value is, the more memory Ruby will allocate before it tries to clean up after itself. If this value is too high, you&#8217;ll have dead objects hanging around eating up heap space, and possibly causing Ruby to crap itself and allocate a new heap. This is bad.</li>
<li>To tune this value, you want to find the happy medium, wherein you stabilize under your initial heap allocation value, but with as few garbage collection passes as possible. Read up on <a href="http://blog.evanweaver.com/articles/2009/04/09/ruby-gc-tuning/">Evan Weaver&#8217;s blog</a> for some more in-depth analysis of what garbage collection frequency tuning can do to your app&#8217;s performance.
<li>If you have excess memory and want a faster app, err on the side of this being too high. If you are on a tight memory budget, and would prefer slower actions in exchange for not blowing your heap and allocating a whole new one, err on the side of this being too low.</li>
<li>Recommended values for this are all over the board. Evan recommends a setting of 50 million. I&#8217;m using a setting of 30 million. The Ruby default is 8 million. You&#8217;ll have to play around and find what works best for you. Just pay attention to how many requests there are in between that &#8220;GC cycles so far&#8221; number incrementing in Scrap, and you&#8217;ll be able to measure approximately how often you&#8217;re entering a GC cycle.
</ol>
<p>Good luck with it, and have fun!</p>
]]></content:encoded>
			<wfw:commentRss>http://www.coffeepowered.net/2009/06/13/fine-tuning-your-garbage-collector/feed/</wfw:commentRss>
		<slash:comments>7</slash:comments>
		</item>
		<item>
		<title>Quick tip: Strip URLs before parsing!</title>
		<link>http://www.coffeepowered.net/2009/03/28/quick-tip-strip-urls-before-parsing/</link>
		<comments>http://www.coffeepowered.net/2009/03/28/quick-tip-strip-urls-before-parsing/#comments</comments>
		<pubDate>Sat, 28 Mar 2009 08:09:32 +0000</pubDate>
		<dc:creator>Chris Heald</dc:creator>
				<category><![CDATA[Uncategorized]]></category>
		<category><![CDATA[error]]></category>
		<category><![CDATA[parse]]></category>
		<category><![CDATA[Rails]]></category>
		<category><![CDATA[Ruby]]></category>
		<category><![CDATA[uri]]></category>
		<category><![CDATA[usability]]></category>

		<guid isPermaLink="false">http://www.coffeepowered.net/?p=170</guid>
		<description><![CDATA[Rather than roll my own URL regexes, I prefer to let the existing libraries do the heavy lifting. Ruby has a uri library which is fantastic for parsing (and validating) URLs. For example, something like this might be used in a model validation: I noticed a bit ago that I started getting invalid URL errors [...]]]></description>
			<content:encoded><![CDATA[<p>Rather than roll my own URL regexes, I prefer to let the existing libraries do the heavy lifting. Ruby has a <code>uri</code> library which is fantastic for parsing (and validating) URLs.</p>
<p>For example, something like this might be used in a model validation:</p>
<pre class="brush: ruby; title: ; notranslate">
require 'uri'

def validate_url(url)
	parsed_uri = URI::parse(url)
rescue URI::InvalidURIError
	errors.add :url, &quot;Sorry, that doesn't look like a valid URL&quot;
end
</pre>
<p>I noticed a bit ago that I started getting invalid URL errors where there shouldn&#8217;t be any. After far too long spent in the library&#8217;s code, I realized my error: the URLs were being pasted with a trailing space. Stripping the string before attempting to parse it fixed it right up.</p>
<p>I&#8217;d argue that URI::parse should likely strip any incoming strings, but in the meantime, remember to strip your user input before trying to determine whether it&#8217;s valid or not, or you may end up with frustrated users.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.coffeepowered.net/2009/03/28/quick-tip-strip-urls-before-parsing/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>Things to do when upgrading to Rails 2.3</title>
		<link>http://www.coffeepowered.net/2009/03/16/things-to-do-when-upgrading-to-rails-23/</link>
		<comments>http://www.coffeepowered.net/2009/03/16/things-to-do-when-upgrading-to-rails-23/#comments</comments>
		<pubDate>Tue, 17 Mar 2009 00:55:49 +0000</pubDate>
		<dc:creator>Chris Heald</dc:creator>
				<category><![CDATA[Uncategorized]]></category>

		<guid isPermaLink="false">http://www.coffeepowered.net/?p=153</guid>
		<description><![CDATA[I&#8217;m upgrading blippr to Rails 2.3. Here are some of the things that had to be changed to upgrade: Switch the application entirely to LibXML for all its XML parsing needs In config/environment.rb: Add the following This means that the faster_xml_simple monkeypatch is no longer needed. I don&#8217;t think we&#8217;re doing much else with XML [...]]]></description>
			<content:encoded><![CDATA[<p>I&#8217;m upgrading <a href="http://www.blippr.com">blippr</a> to Rails 2.3. Here are some of the things that had to be changed to upgrade:</p>
<h3>Switch the application entirely to LibXML for all its XML parsing needs</h3>
<p>In <code>config/environment.rb</code>: Add the following</p>
<pre class="brush: ruby; title: ; notranslate">ActiveSupport::XmlMini.backend = 'LibXML'</pre>
<p>This means that the <a href="http://www.coffeepowered.net/2008/09/27/powerful-easy-dry-multi-format-rest-apis/">faster_xml_simple monkeypatch</a> is no longer needed. I don&#8217;t think we&#8217;re doing much else with XML on blippr, but it&#8217;ll be nice to have libxml-backed parsing all around. I must not use REXML. REXML is the app-killer. REXML is the little-death that brings total obliteration.</p>
<h3>Fixes for will_paginate and SQL errors when counting records with a custom :select clause</h3>
<p>* Upgrade <a href="http://wiki.github.com/mislav/will_paginate">will_paginate</a>. Even after the upgrade, something about 2.3&#8242;s named scope handling was still breaking my app. I have a named scope like so:</p>
<pre class="brush: ruby; title: ; notranslate">
  :select =&gt; &quot;*, (blips.vote_score+2)/WEIGHT_FACTOR as weighted_score&quot;,
  :order =&gt; &quot;weighted_score desc&quot;
</pre>
<p>This was causing <code>.paginate</code> calls with this named scope to fail with an invalid SQL error. will_paginate should automatically clobber <code>:select</code> phrases before attempting to count records, but it wasn&#8217;t. The solution is to specify a <code>:count</code> condition to my <code>.paginate</code> calls with the right select clause.</p>
<pre class="brush: ruby; title: ; notranslate">Blip.best.paginate(:page =&gt; current_page, :per_page =&gt; 30, :count =&gt; {:select =&gt; &quot;blips.id&quot;})</pre>
<p>In general, any paginate call with a <code>:select</code> specified seems to break. The <code>:count</code> clause fixes them.</p>
<h3>Upgrade my libmemcached plugin</h3>
<p>A lot of the internal session stuff has changed. We use Evan Weaver&#8217;s libmemcached client, and an <a href="http://github.com/cheald/libmemcached_store/tree/master">upgraded copy of 37signals&#8217; libmemcached store</a> for Rails. The plugin&#8217;s been upgraded to work with 2.3, and provides a session store on top of the general Rails store.</p>
<p>Our caching config now looks something like this:</p>
<pre class="brush: ruby; title: ; notranslate">
GENERAL_CACHE_SERVERS = [&quot;localhost:11211&quot;]
GENERAL_CACHE_OPTIONS = {:untaint =&gt; true}
SESSION_CACHE_SERVERS = [&quot;localhost:11212&quot;]
SESSION_CACHE_OPTIONS = { :prefix_key =&gt; &quot;session:blippr&quot; }
SESSION_MEMCACHE_CLIENT = Memcached.new(SESSION_CACHE_SERVERS, SESSION_CACHE_OPTIONS)

config.cache_store = :libmemcached_store, GENERAL_CACHE_SERVERS, GENERAL_CACHE_OPTIONS
config.action_controller.session_store = :libmemcached_store
config.action_controller.session = {
	:cache =&gt; SESSION_MEMCACHE_CLIENT,
	:expires_after =&gt; 86400
}
</pre>
<p>Works great with libmemcached, with separate memcached instances for fragments and sessions (so that an over-populated fragment store won&#8217;t start clobbering sessions).</p>
<h3>Update query parsing</h3>
<p>I parse query parameters for some funky filtering. In 2.2.2 I used:</p>
<pre class="brush: ruby; title: ; notranslate">ActionController::AbstractRequest.parse_query_parameters(query_string)</pre>
<p>In 2.3, that becomes:</p>
<pre class="brush: ruby; title: ; notranslate">Rack::Utils.parse_query(query_string)</pre>
<p>That&#8217;s about it for now, but as problems arise I&#8217;ll be sure to add them.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.coffeepowered.net/2009/03/16/things-to-do-when-upgrading-to-rails-23/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
	</channel>
</rss>

