<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet href="http://feeds.feedburner.com/~d/styles/rss2full.xsl" type="text/xsl" media="screen"?><?xml-stylesheet href="http://feeds.feedburner.com/~d/styles/itemcontent.css" type="text/css" media="screen"?><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" version="2.0">

<channel>
	<title>bradleyboy :: the online home of Brad Daily</title>
	
	<link>http://blog.bradleyboy.com</link>
	<description>Just another WordPress weblog</description>
	<pubDate>Thu, 03 Jul 2008 13:05:35 +0000</pubDate>
	<generator>http://wordpress.org/?v=2.6.2</generator>
	<language>en</language>
			<atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="self" href="http://feeds.feedburner.com/bradleyboy" type="application/rss+xml" /><item>
		<title>Tomorrow</title>
		<link>http://blog.bradleyboy.com/2008/07/03/tomorrow/</link>
		<comments>http://blog.bradleyboy.com/2008/07/03/tomorrow/#comments</comments>
		<pubDate>Thu, 03 Jul 2008 11:58:46 +0000</pubDate>
		<dc:creator>bradleyboy</dc:creator>
		
		<category><![CDATA[Uncategorized]]></category>

		<guid isPermaLink="false">http://blog.bradleyboy.com/?p=27</guid>
		<description><![CDATA[In the last scene of the TV series The West Wing (a show I liked a lot more the first 4 years than the last, but that&#8217;s another story), President Bartlett and his wife are aboard Air Force One heading home after 8 years in office. Mrs. Bartlett asks the now former President what he [...]]]></description>
			<content:encoded><![CDATA[<p>In the last scene of the TV series <em>The West Wing</em> (a show I liked a lot more the first 4 years than the last, but that&#8217;s another story), President Bartlett and his wife are aboard Air Force One heading home after 8 years in office. Mrs. Bartlett asks the now former President what he is thinking about. President Bartlett, a man who usually had no issue giving a 20 word answer when 5 would do, looked out the window and said simply, &#8220;Tomorrow&#8221;. Today, I can relate.</p>
<p><span id="more-27"></span>As you are reading this, Nora and I are returning from Charlottetown, Prince Edward Island, where we attended the 24th annual convention of the <a href="http://ccsb.ca/">Canadian Convention of Southern Baptists</a>. It was the last annual meeting under that name, as messengers approved a name change (to the Canadian National Baptist Convention) and it was also my last official function as Communications Director for the CCSB, a position I held for almost six years.</p>
<p>I came to the CCSB as a 23-year old single fella who thought he knew a lot more than he actually did. Luckily, I was given the opportunity to learn my trade and work on great projects with even better people. I&#8217;m particularly proud of our work with<a href="http://www.e-quip.net"> </a><a href="http://e-quip.net" target="_blank">e-quip.net</a>, which has expanded to include 15 or so partner sites across North America.</p>
<p>I was also given the freedom to pursue other ventures, most notably my work on SSPAdmin (in 2005) which became <a href="http://slideshowpro.net/products/slideshowpro_director/slideshowpro_director">SlideShowPro Director</a> in 2006. Since then, Todd Dominey and I have continued to mold Director and SlideShowPro into products that I&#8217;m really proud to be a part of. But with continued success came more demands for time in support/development/etc. and toward the end of last year it became clear to me that something had to give. Combined with the monthly travel that my position with the CCSB required, I was burning out pretty quick. So, after months of transitioning to the next <a href="http://twitter.com/cajuncanuck">Brad</a>, I&#8217;m finally a free man and the CCSB is left in good hands. I&#8217;ll certainly miss the people at the CCSB, but I won&#8217;t miss those 5am wake up calls and four hour flights.</p>
<p>So, what&#8217;s next? Well, it&#8217;s full-steam ahead working on some really exciting stuff with SlideShowPro and Director (and beyond). Finally being able to devote myself to that full-time is something I&#8217;ve been looking forward to for quite some time. Other than that, our personal life will be plenty busy as Nora and I are preparing to move to the US sometime next spring (watch out America, here we come!).</p>
<p>Here&#8217;s to tomorrow.</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.bradleyboy.com/2008/07/03/tomorrow/feed/</wfw:commentRss>
		</item>
		<item>
		<title>Lessons in Scaling: Divide and Conquer</title>
		<link>http://blog.bradleyboy.com/2008/05/23/lessons-in-scaling-divide-and-conquer/</link>
		<comments>http://blog.bradleyboy.com/2008/05/23/lessons-in-scaling-divide-and-conquer/#comments</comments>
		<pubDate>Fri, 23 May 2008 19:25:20 +0000</pubDate>
		<dc:creator>bradleyboy</dc:creator>
		
		<category><![CDATA[Uncategorized]]></category>

		<guid isPermaLink="false">http://blog.bradleyboy.com/2008/05/23/lessons-in-scaling-divide-and-conquer/</guid>
		<description><![CDATA[We&#8217;ve seen solid growth in our Director hosting offering over the past few months, and with it came all the fun that is scaling a new application that is handling millions of requests a day. After finally moving the entire Director hosting platform to a dedicated server of its own and also changing over to [...]]]></description>
			<content:encoded><![CDATA[<p>We&#8217;ve seen solid growth in our <a href="http://slideshowpro.net/products/slideshowpro_director/slideshowpro_director">Director hosting offering</a> over the past few months, and with it came all the fun that is scaling a new application that is handling millions of requests a day. After finally moving the entire Director hosting platform to a dedicated server of its own and also changing over to the super fast <a href="http://wiki.codemongers.com/Main">nginx</a> web sever, we still saw issues during the busy midday hours. Content served to slideshows was working fairly well, but attempting to login to Director was horrendously slow and, at times, impossible.</p>
<p><span id="more-26"></span><span id="more-26"></span></p>
<p>Our initial setup was to pass all PHP requests from nginx back to a pool of PHP FCGIs running on high ports. We use the <a href="http://wiki.codemongers.com/NginxHttpUpstreamFairModule">fair upstream module</a> for nginx that essentially passes the request to the least busy PHP backend. This worked well until things got really busy, when the aforementioned problem would start and my blood pressure would go through the roof.</p>
<p>As always, tailing the log provided a useful wake up call. As the requests flew by, I realized that 95% (maybe more) of the requests were for one of two files: images.php and p.php. These are the two files used by SlideShowPro to communicate with Director. One (images.php) sends back the XML file and the other (p.php) parses all image requests for Director&#8217;s on-demand image publishing. Most of the time, these files are simply serving caches back to the client, but because of the sheer number of requests they seemed to be hogging (for lack of a better term) all the PHP backends and not leaving much for the other PHP requests.</p>
<p>The solution was to setup two different pools of PHP FCGIs, one for the images.php and p.php calls and one for everything else. With nginx, this was simple:</p>
<pre>upstream main {</pre>
<pre>    fair;</pre>
<pre>    server    127.0.0.1:8100;</pre>
<pre>    # etc...</pre>
<pre>}</pre>
<pre></pre>
<pre>upstream workhorse  {</pre>
<pre>    fair;</pre>
<pre>    server   127.0.0.1:8000;</pre>
<pre>    # etc...</pre>
<pre>}</pre>
<p>So, now we have two separate backends to serve the different requests and we can add more servers to each as need be. Then, we use the nginx location and regex features to split up the requests:</p>
<pre>location ~ (images|p)\.php$ {</pre>
<pre>    fastcgi_pass   workhorse;</pre>
<pre>}</pre>
<pre></pre>
<pre>location ~ .php$ {</pre>
<pre>    fastcgi_pass   main;</pre>
<pre>}</pre>
<p>Since nginx handles regex location directives in the order they appear in the configuration file, any images.php or p.php file will be captured by the first directive and sent to one of the &#8220;workhorse&#8221; backends. All other PHP requests are sent to the &#8220;main&#8221; backends.</p>
<p>The verdict? After days of struggling with uptime (including a few early morning Pingdom text messages), we are now enjoying solid uptime while still churning through approximately 4 million requests a day.</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.bradleyboy.com/2008/05/23/lessons-in-scaling-divide-and-conquer/feed/</wfw:commentRss>
		</item>
		<item>
		<title>All blogged out</title>
		<link>http://blog.bradleyboy.com/2008/02/16/all-blogged-out/</link>
		<comments>http://blog.bradleyboy.com/2008/02/16/all-blogged-out/#comments</comments>
		<pubDate>Sat, 16 Feb 2008 18:34:53 +0000</pubDate>
		<dc:creator>bradleyboy</dc:creator>
		
		<category><![CDATA[Uncategorized]]></category>

		<guid isPermaLink="false">http://blog.bradleyboy.com/2008/02/16/all-blogged-out/</guid>
		<description><![CDATA[Well, been pretty dormant here for a while. Lots of good stuff going on, just not a lot of time and motivation to write. I&#8217;ll get back into it eventually, but for now you can keep up with me at Twitter. See ya there!
]]></description>
			<content:encoded><![CDATA[<p>Well, been pretty dormant here for a while. Lots of good stuff going on, just not a lot of time and motivation to write. I&#8217;ll get back into it eventually, but for now you can keep up with me at <a href="http://twitter.com/bradleyboy">Twitter</a>. See ya there!</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.bradleyboy.com/2008/02/16/all-blogged-out/feed/</wfw:commentRss>
		</item>
		<item>
		<title>AssetPackager for CakePHP</title>
		<link>http://blog.bradleyboy.com/2007/07/28/assetpackager-for-cakephp/</link>
		<comments>http://blog.bradleyboy.com/2007/07/28/assetpackager-for-cakephp/#comments</comments>
		<pubDate>Sat, 28 Jul 2007 11:30:16 +0000</pubDate>
		<dc:creator>bradleyboy</dc:creator>
		
		<category><![CDATA[Uncategorized]]></category>

		<guid isPermaLink="false">http://blog.bradleyboy.com/2007/07/28/assetpackager-for-cakephp/</guid>
		<description><![CDATA[This past week, web geeks were abuzz with the news of ySlow, a Firebug plugin released by Yahoo! that analyzed the loading of your web site and made suggestions on how to improve it. ySlow gives your site a letter grade, which is really genius as nothing presents a challenge like seeing your website score [...]]]></description>
			<content:encoded><![CDATA[<p>This past week, web geeks were abuzz with the news of <a href="http://developer.yahoo.com/yslow/">ySlow</a>, a <a href="http://www.getfirebug.com/">Firebug</a> plugin released by Yahoo! that analyzed the loading of your web site and made suggestions on how to improve it. ySlow gives your site a letter grade, which is really genius as nothing presents a challenge like seeing your website score an &#8220;F&#8221;.</p>
<p>Of particular interest to me was seeing how the new version of <a href="http://slideshowpro.net/products/slideshowpro-director">SlideShowPro Director</a> fared. Not good. We got an F and it was in the mid 40s at that. Our biggest offense was loading 18 different javascript files and several CSS files as well. Also, we weren&#8217;t doing anything to minimize our CSS or JS files. Removing white space, line breaks, etc can save a lot of bandwidth.</p>
<p>Turns out that in the Rails world, there is already a tool to help you do this. It&#8217;s called <a href="http://synthesis.sbecker.net/pages/asset_packager">AssetPackager</a>. So, needing that very functionality in CakePHP, I set out to create AssetPackager for CakePHP.</p>
<p><span id="more-23"></span></p>
<p>The goal of AssetPackager is to have the best of both worlds. Having separate javascript and CSS files eases development headaches, so we want to maintain the ability to do that. Once in production, we want to combine and compress assets for performance. AssetPackager for CakePHP does this through a Cake console script and a custom helper. This only works with the Cake 1.2.x.x branch, since it relies on the new console.</p>
<p><strong>Installation</strong><br />
Download AssetPackager for CakePHP <a href="http://bradleyboy.com/projects/ap/AssetPackager_for_CakePHP.zip">here</a>. Once you unzip the package, you should find a single &#8220;vendors&#8221; folder. Place the contents of that folder into either of your CakePHP vendors folders (ROOT/vendors or APP/vendors). That&#8217;s it for installation.</p>
<p><strong>Configuration</strong><br />
One important part of any combination/compression strategy is the order in which the files are added. Often times (in both CSS and Javascript) the order that the files are loaded in is extremely important. AssetPackager for CakePHP maintains this order through the use of configuration files.</p>
<p>Throughout this article, I&#8217;m going to be using a javascript example but the same technique is used for CSS as well.</p>
<p>First, create a new file in app/webroot/js and name it _asset_config.php. Inside, we will define the different &#8220;groups&#8221; that we want combined. For example, let&#8217;s say I have the following javascript files: main.js, form.js, ajax.js. At runtime, we want all these to be combined to one file and compressed. In app/webroot/js/_asset_config.php we add this:</p>
<div class="ch_code_container" style="font-family: monospace;height:100%;">
<div style="">Code (php)</div>
<ol>
<li style="font-family: 'Courier New', Courier, monospace; color: black; font-weight: normal; font-style: normal;">
<div style="font-family: 'Courier New', Courier, monospace; font-weight: normal;"><span style="color: #0000ff;">$groups</span> = <a href="http://www.php.net/array"><span style="color: #000066;">array</span></a><span style="color: #66cc66;">&#40;</span> <span style="color: #ff0000;">&#8216;base&#8217;</span> =&gt; <a href="http://www.php.net/array"><span style="color: #000066;">array</span></a><span style="color: #66cc66;">&#40;</span> <span style="color: #ff0000;">&#8216;main&#8217;</span>, <span style="color: #ff0000;">&#8216;form&#8217;</span>, <span style="color: #ff0000;">&#8216;ajax&#8217;</span> <span style="color: #66cc66;">&#41;</span> <span style="color: #66cc66;">&#41;</span>;</div>
</li>
</ol>
</div>
<p>This defines a group (base) and its members. It also defines the order in which we want the files to be combined. You can add as many groups as you like:</p>
<div class="ch_code_container" style="font-family: monospace;height:100%;">
<div style="">Code (php)</div>
<ol>
<li style="font-family: 'Courier New', Courier, monospace; color: black; font-weight: normal; font-style: normal;">
<div style="font-family: 'Courier New', Courier, monospace; font-weight: normal;"><span style="color: #0000ff;">$groups</span> = <a href="http://www.php.net/array"><span style="color: #000066;">array</span></a><span style="color: #66cc66;">&#40;</span> <span style="color: #ff0000;">&#8216;base&#8217;</span> =&gt; <a href="http://www.php.net/array"><span style="color: #000066;">array</span></a><span style="color: #66cc66;">&#40;</span> <span style="color: #ff0000;">&#8216;main&#8217;</span>, <span style="color: #ff0000;">&#8216;form&#8217;</span>, <span style="color: #ff0000;">&#8216;ajax&#8217;</span> <span style="color: #66cc66;">&#41;</span>, <span style="color: #ff0000;">&#8216;another&#8217;</span> =&gt; <a href="http://www.php.net/array"><span style="color: #000066;">array</span></a><span style="color: #66cc66;">&#40;</span> <span style="color: #ff0000;">&#8216;main&#8217;</span>, <span style="color: #ff0000;">&#8216;form&#8217;</span> <span style="color: #66cc66;">&#41;</span> <span style="color: #66cc66;">&#41;</span>;</div>
</li>
</ol>
</div>
<p><strong>The View</strong><br />
The second step is using the custom helper included with AssetPackager for CakePHP to write our includes into the page. First, make sure your controller has initialized the asset_helper. (I do this in app_controller, since I use it throughout the application).</p>
<div class="ch_code_container" style="font-family: monospace;height:100%;">
<div style="">Code (php)</div>
<ol>
<li style="font-family: 'Courier New', Courier, monospace; color: black; font-weight: normal; font-style: normal;">
<div style="font-family: 'Courier New', Courier, monospace; font-weight: normal;">vendor<span style="color: #66cc66;">&#40;</span><span style="color: #ff0000;">&#8216;asset_packager/asset_helper&#8217;</span><span style="color: #66cc66;">&#41;</span>;</div>
</li>
<li style="font-family: 'Courier New', Courier, monospace; color: black; font-weight: normal; font-style: normal;">
<div style="font-family: 'Courier New', Courier, monospace; font-weight: normal;">&nbsp;</div>
</li>
<li style="font-family: 'Courier New', Courier, monospace; color: black; font-weight: normal; font-style: normal;">
<div style="font-family: 'Courier New', Courier, monospace; font-weight: normal;"><span style="color: #000000; font-weight: bold;">class</span> AppController <span style="color: #000000; font-weight: bold;">extends</span> Controller <span style="color: #66cc66;">&#123;</span></div>
</li>
<li style="font-family: 'Courier New', Courier, monospace; color: black; font-weight: normal; font-style: normal;">
<div style="font-family: 'Courier New', Courier, monospace; font-weight: normal;">&nbsp; &nbsp; <span style="color: #000000; font-weight: bold;">var</span> <span style="color: #0000ff;">$helpers</span> = <a href="http://www.php.net/array"><span style="color: #000066;">array</span></a><span style="color: #66cc66;">&#40;</span><span style="color: #ff0000;">&#8216;Asset&#8217;</span><span style="color: #66cc66;">&#41;</span>;</div>
</li>
</ol>
</div>
<p>Now to the view itself. Before, you would have had something like this:</p>
<div class="ch_code_container" style="font-family: monospace;height:100%;">
<div style="">Code (php)</div>
<ol>
<li style="font-family: 'Courier New', Courier, monospace; color: black; font-weight: normal; font-style: normal;">
<div style="font-family: 'Courier New', Courier, monospace; font-weight: normal;">e<span style="color: #66cc66;">&#40;</span><span style="color: #0000ff;">$javascript</span>-&gt;<span style="color: #006600;">link</span><span style="color: #66cc66;">&#40;</span><span style="color: #ff0000;">&#8216;main&#8217;</span><span style="color: #66cc66;">&#41;</span><span style="color: #66cc66;">&#41;</span>;</div>
</li>
<li style="font-family: 'Courier New', Courier, monospace; color: black; font-weight: normal; font-style: normal;">
<div style="font-family: 'Courier New', Courier, monospace; font-weight: normal;">e<span style="color: #66cc66;">&#40;</span><span style="color: #0000ff;">$javascript</span>-&gt;<span style="color: #006600;">link</span><span style="color: #66cc66;">&#40;</span><span style="color: #ff0000;">&#8216;form&#8217;</span><span style="color: #66cc66;">&#41;</span><span style="color: #66cc66;">&#41;</span>;</div>
</li>
<li style="font-family: 'Courier New', Courier, monospace; color: black; font-weight: normal; font-style: normal;">
<div style="font-family: 'Courier New', Courier, monospace; font-weight: normal;">e<span style="color: #66cc66;">&#40;</span><span style="color: #0000ff;">$javascript</span>-&gt;<span style="color: #006600;">link</span><span style="color: #66cc66;">&#40;</span><span style="color: #ff0000;">&#8216;ajax&#8217;</span><span style="color: #66cc66;">&#41;</span><span style="color: #66cc66;">&#41;</span>;</div>
</li>
</ol>
</div>
<p>We can now replace that with:</p>
<div class="ch_code_container" style="font-family: monospace;height:100%;">
<div style="">Code (php)</div>
<ol>
<li style="font-family: 'Courier New', Courier, monospace; color: black; font-weight: normal; font-style: normal;">
<div style="font-family: 'Courier New', Courier, monospace; font-weight: normal;">e<span style="color: #66cc66;">&#40;</span><span style="color: #0000ff;">$asset</span>-&gt;<span style="color: #006600;">js</span><span style="color: #66cc66;">&#40;</span><span style="color: #ff0000;">&#8216;base&#8217;</span><span style="color: #66cc66;">&#41;</span><span style="color: #66cc66;">&#41;</span>;</div>
</li>
</ol>
</div>
<p>Here&#8217;s how it works: If your application is in any DEBUG level other than 0 (thus indicating you are in development mode), the asset_helper will write in the includes one at a time just like you were doing before. However, if you are in DEBUG level 0 (production), the asset_helper looks for a file in your js folder like this: base_1185637913.js. The timestamp ensures that your end users always get the new version, regardless of whether they had the previous version cached.</p>
<p>So, how does that base_1185637913.js file get created?</p>
<p><strong>The Console</strong><br />
The heart of AssetPackager for CakePHP is a Cake console script. First, for a help screen of all the options and commands, enter:</p>
<div class="ch_code_container" style="font-family: monospace;height:100%;">
<div style="">Code (shell)</div>
<ol>
<li style="font-family: 'Courier New', Courier, monospace; color: black; font-weight: normal; font-style: normal;">
<div style="font-family: 'Courier New', Courier, monospace; font-weight: normal;">/path/to/console/cake asset_packager help</div>
</li>
</ol>
</div>
<p>The easiest way to run it is interactively, like so:</p>
<div class="ch_code_container" style="font-family: monospace;height:100%;">
<div style="">Code (shell)</div>
<ol>
<li style="font-family: 'Courier New', Courier, monospace; color: black; font-weight: normal; font-style: normal;">
<div style="font-family: 'Courier New', Courier, monospace; font-weight: normal;">/path/to/console/cake asset_packager</div>
</li>
</ol>
</div>
<p>Once it has started, it will ask you what assets you would like to combine. Since we are doing just javascript right now, we enter J. It loads your config file, combines all the javascript into one file, then uses JSMin to strip all the whitespace, line breaks, etc out of the file (NOTE: JSMin requires PHP5, if you are on PHP4, AssetPackager for CakePHP will silently skip this step). The finished file is then named using the group name (<em>base</em> in our case) and a timestamp. If multiple versions exist in the js folder, the asset_helper always loads the most recent version. To clean up old versions, just run:</p>
<div class="ch_code_container" style="font-family: monospace;height:100%;">
<div style="">Code (shell)</div>
<ol>
<li style="font-family: 'Courier New', Courier, monospace; color: black; font-weight: normal; font-style: normal;">
<div style="font-family: 'Courier New', Courier, monospace; font-weight: normal;">/path/to/console/cake asset_packager clean</div>
</li>
</ol>
</div>
<p>That&#8217;s it! Try changing your DEBUG setting to 0 and watch what happens. All your individual includes should now be replaced with one include to your combined, compressed javascript file.</p>
<p>AssetPackager for CakePHP is released under the MIT license, so feel free to modify to your own tastes.</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.bradleyboy.com/2007/07/28/assetpackager-for-cakephp/feed/</wfw:commentRss>
		</item>
		<item>
		<title>Movin’ on up</title>
		<link>http://blog.bradleyboy.com/2007/07/18/movin-on-up/</link>
		<comments>http://blog.bradleyboy.com/2007/07/18/movin-on-up/#comments</comments>
		<pubDate>Wed, 18 Jul 2007 17:05:06 +0000</pubDate>
		<dc:creator>bradleyboy</dc:creator>
		
		<category><![CDATA[Uncategorized]]></category>

		<guid isPermaLink="false">http://blog.bradleyboy.com/2007/07/18/movin-on-up/</guid>
		<description><![CDATA[We&#8217;ve finally found a new home for the SlideShowPro website and the move was completed last weekend with only about 30 minutes of downtime. How&#8217;d we do it? Why&#8217;d we move? Who did we choose? Read on to find out.

The Why
First, why move in the first place? Moving a website to another host is a [...]]]></description>
			<content:encoded><![CDATA[<p>We&#8217;ve finally found a new home for the <a href="http://slideshowpro.net/">SlideShowPro website</a> and the move was <a href="http://www.slideshowpro.net/news/archive/2007/07/weve-moved.php">completed</a> last weekend with only about 30 minutes of downtime. How&#8217;d we do it? Why&#8217;d we move? Who did we choose? Read on to find out.</p>
<p><span id="more-22"></span><br />
<strong>The Why</strong><br />
First, why move in the first place? Moving a website to another host is a pain. Just like a house or apartment, the longer you stay at one host the more embedded you get and the harder it is to move. The SlideShowPro site has been hosted on a shared account at Dreamhost since its inception in early 2005 and includes a variety of components (customer management application, statistical tracking, user forums, etc) all of which use their own database. Moving all that wouldn&#8217;t be easy, so there must have been a compelling reason for the move, right?</p>
<p><a href="http://dreamhost.com/">Dreamhost</a> became the darling of shared web hosts a few years ago and this very blog is hosted there (and will remain there) as a matter of fact. However, over the last year or so, we&#8217;ve encountered several setbacks with Dreamhost that included quite a bit of downtime as well as our site getting hacked a few times. In June alone, we had 2 or 3 complete site outages ranging from 3 hours to (in one case) over 12 hours. So, the reason is very simple: Downtime equals money. And if our site goes down for 12 hours, it hurts and at the end of June our sales numbers showed just how much. Turns out that many of the outages were caused by another account on our server getting DOS&#8217;d (Denial of Service attack), which Dreamhost remedied by continually changing the IP address of the server which caused DNS lookups to fail.</p>
<p>A non-technical way of putting this: Imagine owning a store. Randomly, a security guard comes by and throws people out of your store, then locks the doors. When will he come back? Well, you have to call the security company, leave a message and they will get back to you. They eventually do, and you find out that the reason your store was closed down was because of what the store <em>next</em> to you was doing. That is very similar to what happens in shared hosting environments.</p>
<p><strong>The Who</strong><br />
We chose <a href="http://weblog.mediatemple.net/weblog/2007/07/17/mt-welcomes-slideshowpro/">Media Temple</a> for a lot of reasons, but mostly because of how impressed we were with their people and the good things we had heard about their <a href="http://www.mediatemple.net/webhosting/dv/">Dedicated Virtual</a> system. In a DV setup, the server&#8217;s assets are apportioned to you and only you, so you are guaranteed a certain amount of those resources and any other account on the same box will never be able to touch them. You also have full root access, so you can set up the server however you please.</p>
<p>We got the keys to our new server the first week in July and started getting things setup. A big thanks to Chris Lea and the rest of his team at (mt) for providing great support along the way. We even had a little fun trying to track down one of the strangest problems any of us had seen in a while. If it were Dreamhost, something tells me I would still be trying to convince them there was a problem in the first place.</p>
<p><strong>How to minimize downtime</strong><br />
One of the more painful portions of migrating to a new host is the time it takes for the new DNS settings to resolve. A few years ago, this could take 2-3 days and even though it is better now (most of the time less than 24 hours), it still means downtime since some of your users will be visiting the old site while some will be directed to the new one. When everything is built on top of a database (or in our case, databases), that can be a very messy situation.</p>
<p>Once we had the new box up and working with a snapshot of some old data, we were ready to migrate the live data. What we did was take the site down for about 30 minutes just after midnight on Saturday. We used a rewrite rule to make sure any request was routed to a &#8220;Be right back&#8221; page, ensuring that no purchases, forum posts, or any other data was added or modified. During those 30 minutes we dumped all of our databases to SQL files, transferred them to our new server and used the dump files to populate the new database. Then it was as simple as pointing all of our existing config files to use the new database, that way both sites would be using the same data set and provider. Once that was done, we could change the DNS whenever we wanted. Even if users landed on the old site, it would be using the new database so nothing was stale or lost.</p>
<p>One tip for doing this: Point a domain that you aren&#8217;t using for anything to the new server early in the process (we did this as soon as we setup the new server). This will give you an external domain to test with and use to reference the database so that it is not tied to your main domain, which is what makes this work through the DNS switch.</p>
<p>All in all, we couldn&#8217;t be happier. The site is snappy and we fell much better knowing that we have full control of our box and will no longer feel the pain of the &#8220;bad neighbor&#8221; effect of shared hosting. It&#8217;s also nice to know that a solid, prompt support team will be ready to help if and when things go south.</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.bradleyboy.com/2007/07/18/movin-on-up/feed/</wfw:commentRss>
		</item>
		<item>
		<title>Introducing: Farkleberrys</title>
		<link>http://blog.bradleyboy.com/2007/07/17/introducing-farkleberrys/</link>
		<comments>http://blog.bradleyboy.com/2007/07/17/introducing-farkleberrys/#comments</comments>
		<pubDate>Tue, 17 Jul 2007 12:00:54 +0000</pubDate>
		<dc:creator>bradleyboy</dc:creator>
		
		<category><![CDATA[Uncategorized]]></category>

		<guid isPermaLink="false">http://blog.bradleyboy.com/2007/07/16/introducing-farkleberrys/</guid>
		<description><![CDATA[If you are in or around Nixa, Missouri, and are looking for some unique gifts and an all around good time, head on over to the recently opened (as in today) Farkleberrys on North Main Street. To me, this is literally a Mom and Pop store and they look to have some really cool stuff. [...]]]></description>
			<content:encoded><![CDATA[<p>If you are in or around Nixa, Missouri, and are looking for some unique gifts and an all around good time, head on over to the recently opened (as in <em>today</em>) <a href="http://farkleberrys.com/">Farkleberrys</a> on North Main Street. To me, this is literally a Mom and Pop store and they look to have some really cool stuff. They also have a <a href="http://farkleberrys.com/">website</a> built by an unknown third party that utilizes <a href="http://slideshowpro.net/products/slideshowpro">SlideShowPro</a> and <a href="http://slideshowpro.net/products/slideshowpro-director">SlideShowPro Director</a> (fancy!). Well, I seem to have run out of things to plug, so head on over to <a href="http://farkleberrys.com/">Farkleberrys</a> and tell &#8216;em I sent ya!</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.bradleyboy.com/2007/07/17/introducing-farkleberrys/feed/</wfw:commentRss>
		</item>
		<item>
		<title>CakePHP, Lighttpd and ModMagnet</title>
		<link>http://blog.bradleyboy.com/2007/06/15/cakephp-lighttpd-and-modmagnet/</link>
		<comments>http://blog.bradleyboy.com/2007/06/15/cakephp-lighttpd-and-modmagnet/#comments</comments>
		<pubDate>Sat, 16 Jun 2007 00:43:07 +0000</pubDate>
		<dc:creator>bradleyboy</dc:creator>
		
		<category><![CDATA[Uncategorized]]></category>

		<guid isPermaLink="false">http://blog.bradleyboy.com/2007/06/15/cakephp-lighttpd-and-modmagnet/</guid>
		<description><![CDATA[
Since finding Rails a few years back, I have grown more and more fond of lighttpd (aka lighty), a lightweight, fast alternative to Apache. Now that I spend some time back in the land of PHP (in particular CakePHP), I wanted to try and get a CakePHP app up and running using lighty instead of [...]]]></description>
			<content:encoded><![CDATA[
<p>Since finding <a href="http://rubyonrails.org/">Rails</a> a few years back, I have grown more and more fond of <a href="http://lighttpd.net/">lighttpd</a> (aka lighty), a lightweight, fast alternative to Apache. Now that I spend some time back in the land of PHP (in particular CakePHP), I wanted to try and get a CakePHP app up and running using lighty instead of Apache. Here&#8217;s a walkthrough&#8230;</p>
<p><span id="more-20"></span><br />
<strong>Prerequisites</strong><br />
First, you will need to have lighttpd installed, 1.4.12 or above or anything in the newer 1.5.x branch. I&#8217;ll be delving into <a href="http://trac.lighttpd.net/trac/wiki/Docs%3AModMagnet">ModMagnet</a> here, so you will need to have lua installed (5.1 or greater) and have lighty compiled &#8211;with-lua.</p>
<p><strong>Why ModMagnet?<br />
</strong>One of the first things you have to do when moving from Apache to lighty is transition your use of Apache&#8217;s fantastic ModRewrite to a suitable replacement in lighty land. The most obvious candidate is lighty&#8217;s own rewrite module, but it lacks some of the more advanced features of Apache&#8217;s module, notably Apache&#8217;s rewrite condition statements.</p>
<p>ModMagnet is a newer module in lighty and aims to give a lot more flexibility to handling, rewriting and forwarding requests. Since we needed to do a lot more than just the standard CakePHP rewrites, ModMagnet fit the bill perfectly.</p>
<p>Let&#8217;s look at our lighty configuration. First and foremost, make sure you have ModMagnet in your list of modules to load:</p>
<div class="ch_code_container" style="font-family: monospace;height:100%;">
<div style="">Code (lighttpd)</div>
<ol>
<li style="font-family: 'Courier New', Courier, monospace; color: black; font-weight: normal; font-style: normal;">
<div style="font-family: 'Courier New', Courier, monospace; font-weight: normal;">server.modules = ( &#8230;, mod_magnet, &#8230; )</div>
</li>
</ol>
</div>
<p>Also, for the purposes of this demo, we have our document root set to the webroot foldler of our Cake application:</p>
<div class="ch_code_container" style="font-family: monospace;height:100%;">
<div style="">Code (lighttpd)</div>
<ol>
<li style="font-family: 'Courier New', Courier, monospace; color: black; font-weight: normal; font-style: normal;">
<div style="font-family: 'Courier New', Courier, monospace; font-weight: normal;">server.document-root = &quot;/path/to/cake/app/webroot/&quot;</div>
</li>
</ol>
</div>
<p>Now, we want to tell ModMagnet to attract (get it&#8230;it&#8217;s a magnet!) any request that comes down the pipe:</p>
<div class="ch_code_container" style="font-family: monospace;height:100%;">
<div style="">Code (lighttpd)</div>
<ol>
<li style="font-family: 'Courier New', Courier, monospace; color: black; font-weight: normal; font-style: normal;">
<div style="font-family: 'Courier New', Courier, monospace; font-weight: normal;">magnet.attract-physical-path-to = ( server.document-root + &quot;/rewrite.lua&quot; )</div>
</li>
</ol>
</div>
<p>This will take any request that is made and pass it directly to the rewrite.lua file, which is placed in our document root.</p>
<p><strong>What a minute&#8230;Lua who?</strong><br />
That&#8217;s right, ModMagnet uses Lua, a fast, lightweight scripting language that works really well in this environment. You can read more about Lua <a href="http://www.lua.org/about.html">here</a>.</p>
<p><strong>Writing the Lua Script<br />
</strong>Our needs are simple. We want to check if the file requested actually exists on the filesystem. If it does, simply serve up that file without invoking PHP and FastCGI. If the file does not exist, we want to rewrite the request to CakePHP.</p>
<div class="ch_code_container" style="font-family: monospace;height:300px;">
<div style="">Code (lua)</div>
<ol>
<li style="font-family: 'Courier New', Courier, monospace; color: black; font-weight: normal; font-style: normal;">
<div style="font-family: 'Courier New', Courier, monospace; font-weight: normal;"><span style="color: #808080; font-style: italic;">&#8211; rewrite.lua</span></div>
</li>
<li style="font-family: 'Courier New', Courier, monospace; color: black; font-weight: normal; font-style: normal;">
<div style="font-family: 'Courier New', Courier, monospace; font-weight: normal;">&nbsp;</div>
</li>
<li style="font-family: 'Courier New', Courier, monospace; color: black; font-weight: normal; font-style: normal;">
<div style="font-family: 'Courier New', Courier, monospace; font-weight: normal;"><span style="color: #808080; font-style: italic;">&#8211; Does file exist on file system?</span></div>
</li>
<li style="font-family: 'Courier New', Courier, monospace; color: black; font-weight: normal; font-style: normal;">
<div style="font-family: 'Courier New', Courier, monospace; font-weight: normal;">attr = lighty.stat<span style="color: #66cc66;">&#40;</span>lighty.env<span style="color: #66cc66;">&#91;</span><span style="color: #ff0000;">&quot;physical.path&quot;</span><span style="color: #66cc66;">&#93;</span><span style="color: #66cc66;">&#41;</span></div>
</li>
<li style="font-weight: bold;">
<div style="font-family: 'Courier New', Courier, monospace; font-weight: normal;">&nbsp;</div>
</li>
<li style="font-family: 'Courier New', Courier, monospace; color: black; font-weight: normal; font-style: normal;">
<div style="font-family: 'Courier New', Courier, monospace; font-weight: normal;"><span style="color: #808080; font-style: italic;">&#8211; It does not</span></div>
</li>
<li style="font-family: 'Courier New', Courier, monospace; color: black; font-weight: normal; font-style: normal;">
<div style="font-family: 'Courier New', Courier, monospace; font-weight: normal;"><span style="color: #b1b100;">if</span> <span style="color: #66cc66;">&#40;</span><span style="color: #b1b100;">not</span> attr<span style="color: #66cc66;">&#41;</span> <span style="color: #b1b100;">then</span></div>
</li>
<li style="font-family: 'Courier New', Courier, monospace; color: black; font-weight: normal; font-style: normal;">
<div style="font-family: 'Courier New', Courier, monospace; font-weight: normal;">&nbsp; <span style="color: #808080; font-style: italic;">&#8211; Pass request to CakePHP&#8217;s index.php</span></div>
</li>
<li style="font-family: 'Courier New', Courier, monospace; color: black; font-weight: normal; font-style: normal;">
<div style="font-family: 'Courier New', Courier, monospace; font-weight: normal;">&nbsp; lighty.env<span style="color: #66cc66;">&#91;</span><span style="color: #ff0000;">&quot;uri.query&quot;</span><span style="color: #66cc66;">&#93;</span> = &#8216;url=&#8217; .. lighty.env<span style="color: #66cc66;">&#91;</span><span style="color: #ff0000;">&quot;uri.path&quot;</span><span style="color: #66cc66;">&#93;</span></div>
</li>
<li style="font-weight: bold;">
<div style="font-family: 'Courier New', Courier, monospace; font-weight: normal;">&nbsp; lighty.env<span style="color: #66cc66;">&#91;</span><span style="color: #ff0000;">&quot;uri.path&quot;</span><span style="color: #66cc66;">&#93;</span> = <span style="color: #ff0000;">&quot;/index.php&quot;</span></div>
</li>
<li style="font-family: 'Courier New', Courier, monospace; color: black; font-weight: normal; font-style: normal;">
<div style="font-family: 'Courier New', Courier, monospace; font-weight: normal;">&nbsp; lighty.env<span style="color: #66cc66;">&#91;</span><span style="color: #ff0000;">&quot;physical.rel-path&quot;</span><span style="color: #66cc66;">&#93;</span> = lighty.env<span style="color: #66cc66;">&#91;</span><span style="color: #ff0000;">&quot;uri.path&quot;</span><span style="color: #66cc66;">&#93;</span></div>
</li>
<li style="font-family: 'Courier New', Courier, monospace; color: black; font-weight: normal; font-style: normal;">
<div style="font-family: 'Courier New', Courier, monospace; font-weight: normal;">&nbsp; lighty.env<span style="color: #66cc66;">&#91;</span><span style="color: #ff0000;">&quot;physical.path&quot;</span><span style="color: #66cc66;">&#93;</span> = lighty.env<span style="color: #66cc66;">&#91;</span><span style="color: #ff0000;">&quot;physical.doc-root&quot;</span><span style="color: #66cc66;">&#93;</span> .. lighty.env<span style="color: #66cc66;">&#91;</span><span style="color: #ff0000;">&quot;physical.rel-path&quot;</span><span style="color: #66cc66;">&#93;</span></div>
</li>
<li style="font-family: 'Courier New', Courier, monospace; color: black; font-weight: normal; font-style: normal;">
<div style="font-family: 'Courier New', Courier, monospace; font-weight: normal;"><span style="color: #b1b100;">end</span></div>
</li>
</ol>
</div>
<p>That&#8217;s it! The lighty.stat() call attempts to stat the file, and if it fails we form the path and query string just like CakePHP likes it. The request then is taken back by lighty and continues to completion. There is a lot you can do here, be sure to check the <a href="http://trac.lighttpd.net/trac/wiki/Docs%3AModMagnet">ModMagnet</a> docs for all the variables available.</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.bradleyboy.com/2007/06/15/cakephp-lighttpd-and-modmagnet/feed/</wfw:commentRss>
		</item>
		<item>
		<title>Screencasts for perfectionists</title>
		<link>http://blog.bradleyboy.com/2007/05/24/screencasts-for-perfectionists/</link>
		<comments>http://blog.bradleyboy.com/2007/05/24/screencasts-for-perfectionists/#comments</comments>
		<pubDate>Thu, 24 May 2007 23:28:33 +0000</pubDate>
		<dc:creator>bradleyboy</dc:creator>
		
		<category><![CDATA[Uncategorized]]></category>

		<guid isPermaLink="false">http://blog.bradleyboy.com/2007/05/24/screencasts-for-perfectionists/</guid>
		<description><![CDATA[Screencasts can be a powerful tool when promoting your product or providing support to existing customers. Some things that are very hard to document in a manual are super simple to illustrate through a simple screencast. We utilize screencasts more and more at SlideShowPro, including our current series previewing the forthcoming update to Director.
The problem [...]]]></description>
			<content:encoded><![CDATA[<p>Screencasts can be a powerful tool when promoting your product or providing support to existing customers. Some things that are very hard to document in a manual are super simple to illustrate through a simple screencast. We utilize screencasts more and more at <a href="http://slideshowpro.net/">SlideShowPro</a>, including our current series previewing the forthcoming update to <a href="http://slideshowpro.net/products/slideshowpro-director">Director</a>.</p>
<p>The problem with screencasts is that they can be an absolute time-eater. Typically you use a program that records the screen and the input from your microphone at the same time. That&#8217;s all well and good, but when you are picky about quality, you end up stopping and starting over at every little &#8220;um&#8221; and &#8220;ah&#8221;.</p>
<p>So when I sat down to do my last screencast, I decided to take a different tack and do it in pieces. Here&#8217;s how I did it.</p>
<p><span id="more-19"></span><br />
First, some prerequisites. You&#8217;ll need a Mac with <a href="http://www.ambrosiasw.com/utilities/snapzprox/">Snapz Pro X</a>, <a href="http://www.apple.com/ilife/garageband/">Garageband</a> and <a href="http://www.apple.com/quicktime/pro/mac.html">Quicktime Pro</a> (or some other program to export the video to its final state, like <a href="http://www.sorensonmedia.com/">Squeeze</a>).</p>
<p><strong>Step 1: Capture the video</strong><br />
The frustrating part of trying to do both the audio and video at the same time is that the video is pretty simple, its the audio that you always end up screwing up. With this process, we&#8217;ll capture the video on its own first, then use GarageBand to add the voiceover.</p>
<p>So, pop open Snapz Pro X and make sure that you deselect the &#8220;Microphone track&#8221; box, ensuring you capture just the video. Record your screencast, talking your way through it like you normally would so that your timing will be right when it comes to do the voice over.</p>
<p>When you&#8217;re finished, it&#8217;s time to export the video. In order to keep the quality pristine through the process, we&#8217;ll use the &#8220;Apple Animation&#8221; codec, which when set to &#8220;Best&#8221; in the settings menu is lossless. Export the video to somewhere familiar and close out Snapz Pro X.</p>
<p><strong>Step 2: Lay down the audio</strong><br />
GarageBand 3 introduced a nice new feature known as &#8220;Movie Scoring&#8221;. It allows you to import a video, lay down some audio beneath it and then export it out as a video file. We&#8217;ll use this to add the voiceover to our screencast.</p>
<p>Create a new project in GarageBand and select &#8220;New Movie Score&#8221;. Give it a name and you are presented with a new GB project window. The first track is for the video, so just find the file on your Desktop (or wherever you put it) and drag it on to the top track. Now you need to add a track for your audio, so click Track-&gt;New Track from the top menu. You&#8217;ll want a &#8220;Real Instrument&#8221; track for your voiceover.</p>
<p>Now, here&#8217;s something that caught me up. GB uses its own setting for what input to use, not really caring about the setting you have in the Sound System Preferences pane. So, go to the GarageBand preferences pane and select the Audio/MIDI header. Make sure your input of choice is selected for the Audio Input.</p>
<p>Now you can start to play the video and record audio at will. Once you have a section the way you want, trim up its ends and start on the next one. GB is really great for audio editing like this, so there are lots of things you can do. Once your happy with it, time to export it.</p>
<p><strong>Step 3: Finishing</strong><br />
One disappointing thing about GB is that it doesn&#8217;t allow you to export video using customized settings. So, we&#8217;ll export it as is from GB and use Quicktime Pro to export it to its final state.</p>
<p>Go back to GB&#8217;s preferences and this time select the Export tab. Set both Audio and Movie settings to their highest values (Higher Quality and Full Quality, respectfully). Now go to Share-&gt;Save Podcast to Disk. Again, save it somewhere familiar, you&#8217;ll need it in a second.</p>
<p>Now just open that file in QT Pro, or any other video compressor, and export it with its final settings. I use MPEG-4 set to High, Key Frames and Data Rate Automatic. For Audio, I use AAC 22.050 stereo. I&#8217;d also suggest using Fast Start - Compressed under &#8220;Prepare for Internet Streaming&#8221; if you are using a Quicktime based codec.</p>
<p>That&#8217;t it, and you couldn&#8217;t ask for anything better. You get a better quality product and it actually <em>saves</em> time. Here&#8217;s an example of a screencast I did with this technique: <a href="http://www.slideshowpro.net/mov/screencast/director_preview_2.mov">Director Preview Video 2</a>.</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.bradleyboy.com/2007/05/24/screencasts-for-perfectionists/feed/</wfw:commentRss>
<enclosure url="http://www.slideshowpro.net/mov/screencast/director_preview_2.mov" length="10072898" type="video/quicktime" />
		</item>
		<item>
		<title>Happy Birthday Ma!</title>
		<link>http://blog.bradleyboy.com/2007/05/04/happy-birthday-ma/</link>
		<comments>http://blog.bradleyboy.com/2007/05/04/happy-birthday-ma/#comments</comments>
		<pubDate>Fri, 04 May 2007 05:28:28 +0000</pubDate>
		<dc:creator>bradleyboy</dc:creator>
		
		<category><![CDATA[Uncategorized]]></category>

		<guid isPermaLink="false">http://blog.bradleyboy.com/2007/05/04/happy-birthday-ma/</guid>
		<description><![CDATA[Finally, something to break the blog silence around here. Today begins the week of Daily birthdays, starting with Mom.
Love you and Happy Birthday!
]]></description>
			<content:encoded><![CDATA[<p>Finally, something to break the blog silence around here. Today begins the week of Daily birthdays, starting with Mom.</p>
<p>Love you and Happy Birthday!</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.bradleyboy.com/2007/05/04/happy-birthday-ma/feed/</wfw:commentRss>
		</item>
		<item>
		<title>Under the wire</title>
		<link>http://blog.bradleyboy.com/2007/03/15/under-the-wire/</link>
		<comments>http://blog.bradleyboy.com/2007/03/15/under-the-wire/#comments</comments>
		<pubDate>Thu, 15 Mar 2007 14:56:39 +0000</pubDate>
		<dc:creator>bradleyboy</dc:creator>
		
		<category><![CDATA[Uncategorized]]></category>

		<guid isPermaLink="false">http://blog.bradleyboy.com/2007/03/15/under-the-wire/</guid>
		<description><![CDATA[In just about a half hour, Madness will ensue. I&#8217;ve just made the final touches to my bracket and printed it out. By mistake, I printed it on Nora&#8217;s pink paper. Could be a good luck charm, time will tell&#8230;
On to the picks:
The left side of the bracket (Midwest and West regions) will hold true, [...]]]></description>
			<content:encoded><![CDATA[<p>In just about a half hour, Madness will ensue. I&#8217;ve just made the final touches to my bracket and printed it out. By mistake, I printed it on Nora&#8217;s pink paper. Could be a good luck charm, time will tell&#8230;</p>
<p>On to the picks:</p>
<p>The left side of the bracket (Midwest and West regions) will hold true, with Kansas and Florida arriving in Atlanta. Some big upsets loom in these regions, most notably the Duke-VCU matchup. When the pairings were announced Sunday, VCU&#8217;s fan based increased 1000-fold instantly.</p>
<p>The right side of the bracket (East and South regions) will be a Big 12 statement. Fourth seed Texas and third seed Texas A&#38;M will both take out the number one&#8217;s in their bracket on the way to Hotlanta. Upset specials? Creighton over Nevada and Texas Tech over Boston College.</p>
<p>In Atlanta, Florida and Kansas will play a tight one, with the Jayhawks coming out on top (like they did in this year&#8217;s earlier matchup of the two teams). Texas and Texas A&#38;M will also be a dandy, with Texas pulling through and setting up a rematch of two of the best games of the year: Kansas vs. Texas.</p>
<p>Kansas 88 - Texas 85. Rock Chalk!</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.bradleyboy.com/2007/03/15/under-the-wire/feed/</wfw:commentRss>
		</item>
	<feedburner:awareness xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0">http://api.feedburner.com/awareness/1.0/GetFeedData?uri=bradleyboy</feedburner:awareness></channel>
</rss>
