<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" media="screen" href="/~d/styles/rss2full.xsl"?><?xml-stylesheet type="text/css" media="screen" href="http://feeds.feedburner.com/~d/styles/itemcontent.css"?><rss xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:sy="http://purl.org/rss/1.0/modules/syndication/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/" xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0" version="2.0">

<channel>
	<title>Directed Edge News</title>
	
	<link>http://blog.directededge.com</link>
	<description />
	<lastBuildDate>Sat, 13 Apr 2013 03:07:38 +0000</lastBuildDate>
	<language>en-US</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.5.1</generator>
		<atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="self" type="application/rss+xml" href="http://feeds.feedburner.com/directededge" /><feedburner:info uri="directededge" /><atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="hub" href="http://pubsubhubbub.appspot.com/" /><item>
		<title>Terror in recommendations-ville</title>
		<link>http://feedproxy.google.com/~r/directededge/~3/3iSqnXLzBGM/</link>
		<comments>http://blog.directededge.com/2012/10/29/terror-in-recommendations-ville/#comments</comments>
		<pubDate>Mon, 29 Oct 2012 21:01:33 +0000</pubDate>
		<dc:creator>Scott Wheeler</dc:creator>
				<category><![CDATA[Uncategorized]]></category>

		<guid isPermaLink="false">http://blog.directededge.com/?p=785</guid>
		<description><![CDATA[Every startup seems to have one of those technical meltdown stories. Up until today, our worst had been back in 2009 when we were still in beta, and it was more embarassing than critical. (Lesson learned then: never deploy just before sleeping.) Since then we&#8217;ve done pretty well. On average we have about 4-8 hours [...]]]></description>
				<content:encoded><![CDATA[<p>Every startup seems to have one of those technical meltdown stories.  Up until today, our worst had been back in 2009 when we were still in beta, and it was more embarassing than critical.  (Lesson learned then:  never deploy just before sleeping.)</p>
<p>Since then we&#8217;ve done pretty well.  On average we have about 4-8 hours of downtime per year.  So today was especially nasty.</p>
<h3 style="padding-bottom: 1em;">The first sign of trouble</h3>
<p>Around 11:01 p.m. GMT I get a mail about an unexpected reboot.  One of our other guys had caught an issue and was looking into it, but I hadn&#8217;t noticed the SMS he&#8217;d sent me saying such was coming up.</p>
<p>We hop on Skype and start going through what&#8217;s going on.  Our web services process was alternating between crashing and hanging every few minutes.  Usually that means some corrupted indexes in one of our databases.  We get that once or twice a year.  It&#8217;s an annoyance (and let me tell you about the fun of trying to track down a bug that happens twice a year), but we have a script that can rebuild the indexes, depending on the size of the database, in somewhere between 10 seconds and 10 minutes.</p>
<h3 style="padding-bottom: 1em;">A Heisenbug</h3>
<p>The trick is figuring out which database is causing the problem.  Aside from our enterprise customers, which are usually on their own hardware, we run a bunch of our small and medium sized databases off of the same hardware.  Our web services setup is heavily multithreaded, so there are usually some 40-ish threads that are writing to the logs in parallel; figuring out which request was the one triggering the problem can sometimes be a bit tricky.</p>
<p>But we&#8217;ve been there before too.  We have a switch we can use to flip on function tracing which usuall makes it clear pretty quickly &#8212; or at least has in every instance up until now.</p>
<p>This time it didn&#8217;t.  We kept watching our logs, trying to catch the offending item and guessing the wrong one.  We have a script to pull down databases from the production to our development machines, where we check the indexes for corruption.  For about an hour we kept missing.</p>
<p><strong>We hadn&#8217;t been offline for a full hour in more than a year</strong>, so we were getting antsy at that point.  We&#8217;d thought about this situation before too though &#8212; what if we couldn&#8217;t find the offending database?</p>
<h3 style="padding-bottom: 1em;">A not-so-super super-tool</h3>
<p>The last time we had a similar issue (which was sorted out in under 15 minutes), I wrote a small program to go through and check every single database on a system.  It was, as we soon will learn, not so aptly called, &#8220;IntegrityChecker&#8221;.  We&#8217;d never run it in production, but it had been tested on dev machines.  Basically it would disable an account for a few seconds, try to get a file lock on its files, and then run the integrity checker that we usually use offline to test such things.</p>
<p>Except that for some reason it was given a file lock when the OS should have rejected it.  So, now we have mmap&#8217;ed files that are being written from two process.  Not good.  In fact, very, very bad.</p>
<p>Unfortunately, we didn&#8217;t notice this immediately.  In fact, things looked great &#8212; it identified the database with a corrupt index, we rebuilt it, waited 15 minutes to make sure everything was kosher, and headed off to snooze.  It was about 4:00 a.m. local time.</p>
<h3 style="padding-bottom: 1em;">This isn&#8217;t supposed to happen</h3>
<p><strong>We have two redundant monitoring systems</strong> that live in a separate network and check all of our servers once per minute.  Both of them send out a barage of SMSes if the servers stop responding to requests.  But most of the API requests that were coming in were actually being handled &#8212; probably 80% or so &#8212; and our monitoring systems retry a second time if there&#8217;s a failure just so that we don&#8217;t get notified every time there&#8217;s a minor network glitch somewhere.  So we weren&#8217;t notified that our servers were still sickly.</p>
<h3 style="padding-bottom: 1em;">The score</h3>
<p>Now, if you&#8217;re keeping score, here are the points so far of this perfect storm:</p>
<ul>
<li>Relatively minor issue of database index corruption</li>
<li>IntegrityChecker that&#8217;s actually corrupting databases (and not just their indexes)</li>
<li>Monitoring not catching the problems</li>
</ul>
<h3 style="padding-bottom: 1em;">Digging deeper</h3>
<p>9:30 a.m. rolls around and we&#8217;re up and trying to see if there&#8217;s anything we can piece together about the issues from the night before.  Pretty soon one of the other guys that&#8217;s working with me on the problem notices that the server process actually kept crashing all night, albeit at a somewhat reduced frequency (every 10 minutes or so).</p>
<p>Since the IntegrityChecker had worked so well the night before (as we still thought), I gave it another go.  It turned up this time six corrupt databases.  We&#8217;d never seen that.  I rebuilt them, and again, in perfect storm fashion, ran it again.  Two more.  Something was obviously very wrong.</p>
<p>A little while later the database process is back to restarting every minute.  Panic ensues.</p>
<h3 style="padding-bottom: 1em;">Our monitoring turns on itself</h3>
<p>See, we not only have out-of-band monitoring, <strong>we also have monit running to restart things on our machines if something goes wrong</strong>.</p>
<p>We didn&#8217;t notice for about an hour that the errors in the monit log weren&#8217;t the usual ones.  They were 404 &#8212; &#8220;not found&#8221; &#8212; errors.  That was weird.  It turned out the account that we use for status checking was corrupt.  We fixed that and were back down to crashes every 10 minutes or so, but still had no idea what could possibly be causing the problems.</p>
<p>A couple of heavily caffinated hours of shooting in the dark passed.  Eventually we started to suspect the IntegrityChecker.  And we noticed that a few accounts (about a dozen &#8212; the ones that had the misfortune of writing while the IntegrityChecker was checking them) had been effectively nulled out.  We started getting a sense of how widespread the damage was.</p>
<h3 style="padding-bottom: 1em;">The low-down</h3>
<p>There was a lot that came together to make this as bad as it was.  To recap, we started off with a relatively minor issue, which was made into a major issue by a tool that was built specifically for dealing with the exact situation we were facing, and those large issues were not caught by our generally quite good monitoring, which eventually even became part of the problem itself.</p>
<h3 style="padding-bottom: 1em;">The last resort:  the backups</h3>
<p>Fortunately we have backups, so we started going through accounts manually, offline this time, checking almost every database to try to find corruption.  We found a few more.</p>
<p>At that point we started restoring the broken databases to their last known good state &#8212; about 17 hours before all of the mess started.  We&#8217;ve done that now and things look pretty stable.  It&#8217;s now been an 90 minutes since we had a crash or hang and all accounts are back online.  In some cases we&#8217;re trying to merge data that came in later in the day where we can get to it, but that&#8217;s a bit spotty.</p>
<h3 style="padding-bottom: 1em;">An apology</h3>
<p><strong>This is the first time in three and a half years of being in production that we lost data and we feel terrible about it</strong>.  We&#8217;ll be doing some work on our internal systems to help us monitor, diagnose and fix these issues in the future.  Thanks so much for your patience.</p>
<p>Edit: One additional note: if you&#8217;re one of our customers that uses our Shopify app and your data seems to be out of date, it&#8217;ll automatically be updated the next time that we pull your shop&#8217;s data in with our importer.  There was no data loss for Shopify customers.</p>
<img src="http://feeds.feedburner.com/~r/directededge/~4/3iSqnXLzBGM" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://blog.directededge.com/2012/10/29/terror-in-recommendations-ville/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		<feedburner:origLink>http://blog.directededge.com/2012/10/29/terror-in-recommendations-ville/</feedburner:origLink></item>
		<item>
		<title>Bindings update that removes extra server round-trips</title>
		<link>http://feedproxy.google.com/~r/directededge/~3/uALyP3qRx7c/</link>
		<comments>http://blog.directededge.com/2012/09/27/bindings-update-that-removes-extra-server-round-trips/#comments</comments>
		<pubDate>Fri, 28 Sep 2012 06:21:55 +0000</pubDate>
		<dc:creator>Scott Wheeler</dc:creator>
				<category><![CDATA[Uncategorized]]></category>

		<guid isPermaLink="false">http://blog.directededge.com/?p=780</guid>
		<description><![CDATA[So, we noticed recently that our Java and Python bindings were doing an extra round-trip to our servers for every request. We&#8217;ve just done updates to each of them that removes this. Per the HTTP spec, when a request uses HTTP BASIC authentication, as our API does, the client is supposed to first send a [...]]]></description>
				<content:encoded><![CDATA[<p>So, we noticed recently that our Java and Python bindings were doing an extra round-trip to our servers for every request.  We&#8217;ve just done <a href="https://github.com/directededge/directed-edge-bindings">updates</a> to each of them that removes this.</p>
<p>Per the HTTP spec, when a request uses HTTP BASIC authentication, as our API does, the client is supposed to first send a request without credentials.  When it receives a 401 error, then it should retry with the credentials supplied and repeat that process for each and every request.</p>
<p>While that makes sense for interactive use from a browser, for use in a web services API, it makes much more sense to avoid the extra round-trip and send the credentials already in the first request, thus significantly speeding up request times.</p>
<p>You can <a href="https://github.com/directededge/directed-edge-bindings">get the updated versions of our bindings on Github</a>.</p>
<img src="http://feeds.feedburner.com/~r/directededge/~4/uALyP3qRx7c" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://blog.directededge.com/2012/09/27/bindings-update-that-removes-extra-server-round-trips/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		<feedburner:origLink>http://blog.directededge.com/2012/09/27/bindings-update-that-removes-extra-server-round-trips/</feedburner:origLink></item>
		<item>
		<title>Shopify app updates, new Ruby bindings beta, new web services features coming down the pipe</title>
		<link>http://feedproxy.google.com/~r/directededge/~3/o-umsfxcbfw/</link>
		<comments>http://blog.directededge.com/2012/09/18/shopify-app-updates-new-ruby-bindings-beta-new-web-services-features-coming-down-the-pipe/#comments</comments>
		<pubDate>Wed, 19 Sep 2012 03:12:11 +0000</pubDate>
		<dc:creator>Scott Wheeler</dc:creator>
				<category><![CDATA[Uncategorized]]></category>

		<guid isPermaLink="false">http://blog.directededge.com/?p=763</guid>
		<description><![CDATA[Shopify app updates So, it&#8217;s been a gazillion years since we posted updates here, but there have been a number of things shaking out of the woodwork. First, we just did the biggest update to our Shopify app since we first launched it a couple years back.  It features a new Bootstrap-ified configuration interface and [...]]]></description>
				<content:encoded><![CDATA[<h4 style="padding-bottom: 1em">Shopify app updates</h4>
<p>So, it&#8217;s been a gazillion years since we posted updates here, but there have been a number of things shaking out of the woodwork.</p>
<p>First, we just did the biggest update to <a href="http://apps.shopify.com/directed-edge-product-recommender">our Shopify app</a> since we first launched it a couple years back.  It features a new <a href="http://twitter.github.com/bootstrap/">Bootstrap</a>-ified configuration interface and a whole bunch of new recommendations types.</p>
<p><img class="aligncenter size-full wp-image-764" title="Product Settings" src="http://blog.directededge.com/wp-content/uploads/2012/09/product-settings-blog.png" alt="" width="700" height="590" /></p>
<p><img class="aligncenter size-full wp-image-765" title="Recommendations Types" src="http://blog.directededge.com/wp-content/uploads/2012/09/recommendations-blog.png" alt="" width="700" height="603" /></p>
<h4 style="padding-bottom: 1em">New Ruby Bindings in Beta</strong></h4>
<p>Whilst working on our Shopify app, which itself is a Rails app, we got frustrated with the current state of our Ruby bindings.</p>
<p>You see, our Ruby bindings were written back in the dark days before we actually used Ruby at all inside of the company.  Truth be told, the original version was written by yours truly a couple hours after I started learning Ruby.</p>
<p>These days, along with Java and C++ (which our lower-level, performance critical bits are written in), we write quite a bit of Ruby as as such, our tastes have become more refined.</p>
<p>Here are a couple of quick examples:</p>
<p><strong>Old:</strong></p>

<div class="wp_syntax"><table><tr><td class="code"><pre class="ruby" style="font-family:monospace;"><span style="color:#CC0066; font-weight:bold;">require</span> <span style="color:#996600;">'rubygems'</span>
<span style="color:#CC0066; font-weight:bold;">require</span> <span style="color:#996600;">'directed_edge'</span>
&nbsp;
DE_USER = ENV<span style="color:#006600; font-weight:bold;">&#91;</span><span style="color:#996600;">'DIRECTEDEDGE_TEST_DB'</span><span style="color:#006600; font-weight:bold;">&#93;</span>
DE_PASS = ENV<span style="color:#006600; font-weight:bold;">&#91;</span><span style="color:#996600;">'DIRECTEDEDGE_TEST_PASS'</span><span style="color:#006600; font-weight:bold;">&#93;</span>
&nbsp;
<span style="color:#008000; font-style:italic;"># Import some data</span>
&nbsp;
DE_FILE = <span style="color:#996600;">'temp.xml'</span>
&nbsp;
exporter = <span style="color:#6666ff; font-weight:bold;">DirectedEdge::Exporter</span>.<span style="color:#9900CC;">new</span><span style="color:#006600; font-weight:bold;">&#40;</span>DE_FILE<span style="color:#006600; font-weight:bold;">&#41;</span>
&nbsp;
<span style="color:#006600; font-weight:bold;">&#40;</span><span style="color:#006666;">1</span>..<span style="color:#006666;">10</span><span style="color:#006600; font-weight:bold;">&#41;</span>.<span style="color:#9900CC;">each</span> <span style="color:#9966CC; font-weight:bold;">do</span> <span style="color:#006600; font-weight:bold;">|</span>i<span style="color:#006600; font-weight:bold;">|</span>
  item = <span style="color:#6666ff; font-weight:bold;">DirectedEdge::Item</span>.<span style="color:#9900CC;">new</span><span style="color:#006600; font-weight:bold;">&#40;</span>exporter.<span style="color:#9900CC;">database</span>, <span style="color:#996600;">&quot;item_#{i}&quot;</span><span style="color:#006600; font-weight:bold;">&#41;</span>
  item.<span style="color:#9900CC;">add_tag</span><span style="color:#006600; font-weight:bold;">&#40;</span><span style="color:#996600;">'item'</span><span style="color:#006600; font-weight:bold;">&#41;</span>
  item.<span style="color:#9900CC;">link_to</span><span style="color:#006600; font-weight:bold;">&#40;</span><span style="color:#996600;">&quot;item_#{rand(10) + 1}&quot;</span><span style="color:#006600; font-weight:bold;">&#41;</span>
  exporter.<span style="color:#9900CC;">export</span><span style="color:#006600; font-weight:bold;">&#40;</span>item<span style="color:#006600; font-weight:bold;">&#41;</span>
<span style="color:#9966CC; font-weight:bold;">end</span>
&nbsp;
exporter.<span style="color:#9900CC;">finish</span>
&nbsp;
database = <span style="color:#6666ff; font-weight:bold;">DirectedEdge::Database</span>.<span style="color:#9900CC;">new</span><span style="color:#006600; font-weight:bold;">&#40;</span>DE_USER, DE_PASS<span style="color:#006600; font-weight:bold;">&#41;</span>
database.<span style="color:#9900CC;">import</span><span style="color:#006600; font-weight:bold;">&#40;</span>DE_FILE<span style="color:#006600; font-weight:bold;">&#41;</span>
&nbsp;
<span style="color:#008000; font-style:italic;"># Get recommendations</span>
&nbsp;
item = <span style="color:#6666ff; font-weight:bold;">DirectedEdge::Item</span>.<span style="color:#9900CC;">new</span><span style="color:#006600; font-weight:bold;">&#40;</span>database, <span style="color:#996600;">'item_1'</span><span style="color:#006600; font-weight:bold;">&#41;</span>
item.<span style="color:#9900CC;">related</span><span style="color:#006600; font-weight:bold;">&#40;</span><span style="color:#006600; font-weight:bold;">&#91;</span> <span style="color:#996600;">'item'</span> <span style="color:#006600; font-weight:bold;">&#93;</span><span style="color:#006600; font-weight:bold;">&#41;</span>.<span style="color:#9900CC;">each</span> <span style="color:#006600; font-weight:bold;">&#123;</span> <span style="color:#006600; font-weight:bold;">|</span>i<span style="color:#006600; font-weight:bold;">|</span> <span style="color:#CC0066; font-weight:bold;">puts</span> i <span style="color:#006600; font-weight:bold;">&#125;</span>
&nbsp;
<span style="color:#008000; font-style:italic;"># Update item</span>
&nbsp;
item.<span style="color:#9900CC;">add_tag</span><span style="color:#006600; font-weight:bold;">&#40;</span><span style="color:#996600;">'foo'</span><span style="color:#006600; font-weight:bold;">&#41;</span>
item.<span style="color:#9900CC;">save</span></pre></td></tr></table></div>

<p><strong>New:</strong></p>

<div class="wp_syntax"><table><tr><td class="code"><pre class="ruby" style="font-family:monospace;"><span style="color:#CC0066; font-weight:bold;">require</span> <span style="color:#996600;">'rubygems'</span>
<span style="color:#CC0066; font-weight:bold;">require</span> <span style="color:#996600;">'directededge'</span>
&nbsp;
DE_USER = ENV<span style="color:#006600; font-weight:bold;">&#91;</span><span style="color:#996600;">'DIRECTEDEDGE_TEST_DB'</span><span style="color:#006600; font-weight:bold;">&#93;</span>
DE_PASS = ENV<span style="color:#006600; font-weight:bold;">&#91;</span><span style="color:#996600;">'DIRECTEDEDGE_TEST_PASS'</span><span style="color:#006600; font-weight:bold;">&#93;</span>
&nbsp;
database = <span style="color:#6666ff; font-weight:bold;">DirectedEdge::Database</span>.<span style="color:#9900CC;">new</span><span style="color:#006600; font-weight:bold;">&#40;</span>DE_USER, DE_PASS<span style="color:#006600; font-weight:bold;">&#41;</span>
&nbsp;
<span style="color:#6666ff; font-weight:bold;">DirectedEdge::UpdateJob</span>.<span style="color:#9900CC;">run</span><span style="color:#006600; font-weight:bold;">&#40;</span>database, <span style="color:#ff3333; font-weight:bold;">:replace</span><span style="color:#006600; font-weight:bold;">&#41;</span> <span style="color:#9966CC; font-weight:bold;">do</span> <span style="color:#006600; font-weight:bold;">|</span>update<span style="color:#006600; font-weight:bold;">|</span>
  <span style="color:#006600; font-weight:bold;">&#40;</span><span style="color:#006666;">1</span>..<span style="color:#006666;">10</span><span style="color:#006600; font-weight:bold;">&#41;</span>.<span style="color:#9900CC;">each</span> <span style="color:#9966CC; font-weight:bold;">do</span> <span style="color:#006600; font-weight:bold;">|</span>i<span style="color:#006600; font-weight:bold;">|</span>
    update.<span style="color:#9900CC;">item</span><span style="color:#006600; font-weight:bold;">&#40;</span><span style="color:#996600;">&quot;item_#{i}&quot;</span><span style="color:#006600; font-weight:bold;">&#41;</span> <span style="color:#9966CC; font-weight:bold;">do</span> <span style="color:#006600; font-weight:bold;">|</span>item<span style="color:#006600; font-weight:bold;">|</span>
      item.<span style="color:#9900CC;">tags</span>.<span style="color:#9900CC;">add</span><span style="color:#006600; font-weight:bold;">&#40;</span><span style="color:#996600;">'item'</span><span style="color:#006600; font-weight:bold;">&#41;</span>
      item.<span style="color:#9900CC;">links</span>.<span style="color:#9900CC;">add</span><span style="color:#006600; font-weight:bold;">&#40;</span><span style="color:#996600;">&quot;item_#{rand(10) + 1}&quot;</span><span style="color:#006600; font-weight:bold;">&#41;</span>
    <span style="color:#9966CC; font-weight:bold;">end</span>
  <span style="color:#9966CC; font-weight:bold;">end</span>
<span style="color:#9966CC; font-weight:bold;">end</span>
&nbsp;
<span style="color:#008000; font-style:italic;"># Get recommendations</span>
&nbsp;
item = <span style="color:#6666ff; font-weight:bold;">DirectedEdge::Item</span>.<span style="color:#9900CC;">new</span><span style="color:#006600; font-weight:bold;">&#40;</span>database, <span style="color:#996600;">'item_1'</span><span style="color:#006600; font-weight:bold;">&#41;</span>
item.<span style="color:#9900CC;">related</span><span style="color:#006600; font-weight:bold;">&#40;</span><span style="color:#ff3333; font-weight:bold;">:tags</span> <span style="color:#006600; font-weight:bold;">=&gt;</span> <span style="color:#ff3333; font-weight:bold;">:item</span><span style="color:#006600; font-weight:bold;">&#41;</span>.<span style="color:#9900CC;">each</span> <span style="color:#006600; font-weight:bold;">&#123;</span> <span style="color:#006600; font-weight:bold;">|</span>i<span style="color:#006600; font-weight:bold;">|</span> <span style="color:#CC0066; font-weight:bold;">puts</span> i <span style="color:#006600; font-weight:bold;">&#125;</span>
&nbsp;
<span style="color:#008000; font-style:italic;"># Update item</span>
&nbsp;
item.<span style="color:#9900CC;">tags</span>.<span style="color:#9900CC;">add</span><span style="color:#006600; font-weight:bold;">&#40;</span><span style="color:#996600;">'foo'</span><span style="color:#006600; font-weight:bold;">&#41;</span>
item.<span style="color:#9900CC;">save</span></pre></td></tr></table></div>

<p>The changes are most obvious in the importer above, but there are a lot of little subtle improvements in the API&#8217;s semantics.</p>
<p>If you&#8217;re up for beta testing the new bindings, <a href="mailto:info@directededge.com">drop us a line</a>.  The documentation is sorely lacking at present (ahem, meaning non-existent), but that&#8217;s the main thing that we know of that&#8217;s missing at the moment:  the new bindings are already in use by our Shopify app and the <a href="https://github.com/directededge/directed-edge-bindings/tree/v1/Ruby">code is up on Github</a>.</p>
<h4 style="padding-bottom: 1em">Web services updates on the way</h4>
<p>In a similar vein, we&#8217;ve got some stuff that we added to the web services for the Shopify app that still have some rough corners that we want to smooth out before pushing them out to the unwashed masses, but we&#8217;ll soon have explicit API support for tracking product view histories and showing recommendations based on them.  It&#8217;s been possible to do such in a hackey way in the past, but the new stuff adds some special math-fu for time-clustered recommendations and storing high-traffic queues of updates.  More on that soon.</p>
<img src="http://feeds.feedburner.com/~r/directededge/~4/o-umsfxcbfw" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://blog.directededge.com/2012/09/18/shopify-app-updates-new-ruby-bindings-beta-new-web-services-features-coming-down-the-pipe/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		<feedburner:origLink>http://blog.directededge.com/2012/09/18/shopify-app-updates-new-ruby-bindings-beta-new-web-services-features-coming-down-the-pipe/</feedburner:origLink></item>
		<item>
		<title>The easiest way to add recommendations to your Rails app, announcing acts_as_edgy</title>
		<link>http://feedproxy.google.com/~r/directededge/~3/m2WtXcg0uPw/</link>
		<comments>http://blog.directededge.com/2011/03/15/the-easiest-way-to-add-recommendations-to-your-rails-app-announcing-acts_as_edgy/#comments</comments>
		<pubDate>Tue, 15 Mar 2011 18:17:44 +0000</pubDate>
		<dc:creator>Scott Wheeler</dc:creator>
				<category><![CDATA[Uncategorized]]></category>

		<guid isPermaLink="false">http://blog.directededge.com/?p=702</guid>
		<description><![CDATA[So, there are two things I want to talk about. The first is how our new beta hotness, acts_as_edgy, just made it super easy to add Directed Edge recommendations to Rails 2 apps. How easy? One line easy. Recommendations the Easy Way Let&#8217;s suppose for example that you&#8217;ve got a model called User and another [...]]]></description>
				<content:encoded><![CDATA[<p><img src="http://blog.directededge.com/wp-content/uploads/2011/03/rails-plus-directededge.png" alt="" title="Rails plus Directed Edge" width="250" height="111"/></p>
<p>So, there are two things I want to talk about.  The first is how our new beta hotness, <tt>acts_as_edgy</tt>, just made it super easy to add Directed Edge recommendations to Rails 2 apps.  How easy?  One line easy.</p>
<h3 style="margin-bottom: 1em">Recommendations the Easy Way</h3>
<p>Let&#8217;s suppose for example that you&#8217;ve got a model called User and another called Product.  And let&#8217;s also suppose that you support &#8220;likes&#8221; in your app, so you&#8217;ve got a model called Like too that just maps from a user ID to a product ID.  Now, you want to show people personalized recommendations when they log in and related products when they&#8217;re viewing product pages.  Nothing too exotic there.</p>
<p>So, here&#8217;s what you add to your app in that case:</p>

<div class="wp_syntax"><table><tr><td class="code"><pre class="ruby" style="font-family:monospace;"><span style="color:#9966CC; font-weight:bold;">class</span> User <span style="color:#006600; font-weight:bold;">&lt;</span> <span style="color:#6666ff; font-weight:bold;">ActiveRecord::Base</span>
  acts_as_edgy<span style="color:#006600; font-weight:bold;">&#40;</span><span style="color:#ff3333; font-weight:bold;">:like</span>, Like, Product<span style="color:#006600; font-weight:bold;">&#41;</span>
  ...
<span style="color:#9966CC; font-weight:bold;">end</span></pre></td></tr></table></div>

<p>The <tt>acts_as_edgy</tt> line is the whole shebang.  That&#8217;s it.  After that&#8217;s in there you&#8217;ve got rake tasks that let you do <tt>rake edgy:export</tt> and it&#8217;ll push your data over to our servers.  You can also have it automatically update your data with our web service on saves to your model.  Did I mention it&#8217;s easy?</p>
<p>Okay, okay.  So, yeah, I skipped the part about where you actually do something with the recommendations.  But that&#8217;s not rocket surgery either.  Now that you&#8217;ve got that in your model you can do:</p>

<div class="wp_syntax"><table><tr><td class="code"><pre class="ruby" style="font-family:monospace;">User.<span style="color:#9900CC;">first</span>.<span style="color:#9900CC;">edgy_recommended</span> <span style="color:#008000; font-style:italic;"># returns a list of recommended products based on likes</span>
Product.<span style="color:#9900CC;">first</span>.<span style="color:#9900CC;">edgy_related</span>  <span style="color:#008000; font-style:italic;"># returns a list of related products based on likes</span></pre></td></tr></table></div>

<p>You can do much more complicated stuff too.  Our Black Magic figures out the route between models that you provide a list of.  One of the apps that we&#8217;ve been testing with is Spree.  Let&#8217;s say that we want to do product recommendations based on sales.  In Spree that means that we have to map a User to a Product, but there are a bunch of intermediaries:  Orders belong to a User, LineItems belong to an Order, LineItems also belong to a Variant and Variants belong to a Product.  Whew.  What&#8217;s that look like in <tt>acts_as_edgy</tt> nomenclature?</p>

<div class="wp_syntax"><table><tr><td class="code"><pre class="ruby" style="font-family:monospace;"><span style="color:#9966CC; font-weight:bold;">class</span> User <span style="color:#006600; font-weight:bold;">&lt;</span> <span style="color:#6666ff; font-weight:bold;">ActiveRecord::Base</span>
  acts_as_edgy<span style="color:#006600; font-weight:bold;">&#40;</span><span style="color:#ff3333; font-weight:bold;">:purchase</span>, Order, LineItem, Variant, Product<span style="color:#006600; font-weight:bold;">&#41;</span>
  <span style="color:#008000; font-style:italic;"># ...</span>
<span style="color:#9966CC; font-weight:bold;">end</span></pre></td></tr></table></div>

<p>You just list the models that it has to pass through on its way to its target and it figures out (or, well, tries to, this assumes you&#8217;ve used nice consistent column names, which you of course did, didn&#8217;t you?) how to get there.</p>
<p>And then, once again, we can just query for related and recommended stuff like it&#8217;s nobody&#8217;s business.</p>
<p>You also have access to the regular stuff that our API supports.  So if you had both likes and purchases in the same application, that&#8217;s where that handle sitting up there right at the front of the <tt>acts_as_edgy</tt> line comes in handy.  You can chose how you want to weight those, e.g.:</p>

<div class="wp_syntax"><table><tr><td class="code"><pre class="ruby" style="font-family:monospace;">Product.<span style="color:#9900CC;">first</span>.<span style="color:#9900CC;">edgy_related</span><span style="color:#006600; font-weight:bold;">&#40;</span><span style="color:#ff3333; font-weight:bold;">:like_weight</span> <span style="color:#006600; font-weight:bold;">=&gt;</span> <span style="color:#006666;">0.1</span>, <span style="color:#ff3333; font-weight:bold;">:purchase_weight</span> <span style="color:#006600; font-weight:bold;">=&gt;</span> <span style="color:#006666;">0.9</span><span style="color:#006600; font-weight:bold;">&#41;</span></pre></td></tr></table></div>

<p>And there you&#8217;ve got recommendations based on some mix and match weights that seem appropriate to you.</p>
<p>I wanted to get the cool stuff out first, but there are naturally a couple of set up steps that have to happen beforehand:</p>
<p><strong>THIS IS THE PART YOU ACTUALLY HAVE TO READ</strong></p>
<ol>
<li><strong><a href="http://www.directededge.com/signup.html">Sign up for a Directed Edge account</a></strong></li>
<li>Install the <tt><a href="http://rubygems.org/gems/directed-edge">directed-edge</a></tt> and <tt><a href="http://rubygems.org/gems/will_paginate">will_paginate</a></tt> gems</li>
<li>Install the <a href="https://github.com/directededge/acts_as_edgy">plugin</a></li>
<li>Run <tt>rake edgy:configure</tt> to enter your login info</li>
</ol>
<p>There&#8217;s more on the nuts and bolts of that over on the <a href="https://github.com/directededge/acts_as_edgy">Github page</a>, and just let us know if you get stuck.  But here&#8217;s a real world example.  Wait.  I&#8217;ve just realized I lied to you.  There are <em>three</em> things I want to tell you about.</p>
<p><img src="http://blog.directededge.com/wp-content/uploads/2011/03/spree.png" alt="" title="Spree Logo" width="176" height="82" class="alignright size-full wp-image-719" /><br />
<h3 style="margin-bottom: 1em">We have a Spree plugin.</h3>
<p>Yes, yes, we do.  Hell, we even have a full <em><a href="http://developer.directededge.com/article/Getting_started_with_Spree">tutorial</a></em> on working with Directed Edge and Spree!  It only works with Spree 0.11 at present since we haven&#8217;t ported this baby to Rails 3 yet.  Mostly we needed something real to test this thing on, and Spree seemed like a nice way to test with data models that weren&#8217;t tied to our own assumptions.</p>
<p>You can get at <a href="https://github.com/directededge/acts_as_edgy_spree">the full plugin on Github</a>.</p>
<p>And here are the guts &#8212; the example I mentioned above:</p>
<ol>
<li>An <a href="https://github.com/directededge/acts_as_edgy_spree/blob/master/config/initializers/edgy_spree.rb">initializer</a> to add the line above to the User model</li>
<li>A <a href="https://github.com/directededge/acts_as_edgy_spree/blob/master/app/helpers/edgy_helper.rb">helper</a> to figure out if we&#8217;re doing personalized recommendations, related products or basket recommendations</li>
<li>A <a href="https://github.com/directededge/acts_as_edgy_spree/blob/master/app/views/shared/_edgy_related_table.html.erb">partial template</a> to handle showing the results</li>
</ol>
<p>That&#8217;s pretty lean for all of the glue and display code for adding recommendations (and instant updating) to a full-blown e-commerce thingereedoo.</p>
<h3 style="margin-bottom: 1em">The nerdy bits.</h3>
<p>There are a few neat technical things that are happening behind the scenes to make all of this stuff easy from a user&#8217;s perspective.</p>
<p><strong>SQL Generator</strong></p>
<p>One of them is a fancy custom SQL generator that builds optimal queries for all of this stuff.  Entire Rails models get exported with one query.  The generated SQL can get hella ugly, but it offloads most of the dirty work to the database rather than having to do it all in Ruby code.</p>
<p>The above Spree example (the User to Product mapping) generates this SQL monstrosity:</p>

<div class="wp_syntax"><table><tr><td class="code"><pre class="sql" style="font-family:monospace;"><span style="color: #993333; font-weight: bold;">SELECT</span> users<span style="color: #66cc66;">.</span>id <span style="color: #993333; font-weight: bold;">AS</span> from_id<span style="color: #66cc66;">,</span> <span style="color: #ff0000;">'user'</span> <span style="color: #993333; font-weight: bold;">AS</span> from_type<span style="color: #66cc66;">,</span> variants<span style="color: #66cc66;">.</span>product_id <span style="color: #993333; font-weight: bold;">AS</span> to_id<span style="color: #66cc66;">,</span>
<span style="color: #993333; font-weight: bold;">CASE</span> <span style="color: #993333; font-weight: bold;">WHEN</span> variants<span style="color: #66cc66;">.</span>product_id <span style="color: #993333; font-weight: bold;">IS</span> <span style="color: #993333; font-weight: bold;">NOT</span> <span style="color: #993333; font-weight: bold;">NULL</span> <span style="color: #993333; font-weight: bold;">THEN</span> <span style="color: #ff0000;">'product'</span> <span style="color: #993333; font-weight: bold;">END</span> <span style="color: #993333; font-weight: bold;">AS</span> to_type<span style="color: #66cc66;">,</span>
<span style="color: #993333; font-weight: bold;">CASE</span> <span style="color: #993333; font-weight: bold;">WHEN</span> variants<span style="color: #66cc66;">.</span>product_id <span style="color: #993333; font-weight: bold;">IS</span> <span style="color: #993333; font-weight: bold;">NOT</span> <span style="color: #993333; font-weight: bold;">NULL</span> <span style="color: #993333; font-weight: bold;">THEN</span> <span style="color: #ff0000;">'purchase'</span> <span style="color: #993333; font-weight: bold;">END</span> <span style="color: #993333; font-weight: bold;">AS</span> link_type <span style="color: #993333; font-weight: bold;">FROM</span> users
<span style="color: #993333; font-weight: bold;">LEFT</span> <span style="color: #993333; font-weight: bold;">OUTER</span> <span style="color: #993333; font-weight: bold;">JOIN</span> orders <span style="color: #993333; font-weight: bold;">ON</span> users<span style="color: #66cc66;">.</span>id <span style="color: #66cc66;">=</span> orders<span style="color: #66cc66;">.</span>user_id
<span style="color: #993333; font-weight: bold;">LEFT</span> <span style="color: #993333; font-weight: bold;">OUTER</span> <span style="color: #993333; font-weight: bold;">JOIN</span> line_items <span style="color: #993333; font-weight: bold;">ON</span> orders<span style="color: #66cc66;">.</span>id <span style="color: #66cc66;">=</span> line_items<span style="color: #66cc66;">.</span>order_id
<span style="color: #993333; font-weight: bold;">LEFT</span> <span style="color: #993333; font-weight: bold;">OUTER</span> <span style="color: #993333; font-weight: bold;">JOIN</span> variants <span style="color: #993333; font-weight: bold;">ON</span> line_items<span style="color: #66cc66;">.</span>variant_id <span style="color: #66cc66;">=</span> variants<span style="color: #66cc66;">.</span>id
<span style="color: #993333; font-weight: bold;">WHERE</span> users<span style="color: #66cc66;">.</span>id <span style="color: #993333; font-weight: bold;">IS</span> <span style="color: #993333; font-weight: bold;">NOT</span> <span style="color: #993333; font-weight: bold;">NULL</span> <span style="color: #993333; font-weight: bold;">ORDER</span> <span style="color: #993333; font-weight: bold;">BY</span> from_id;</pre></td></tr></table></div>

<p>We build up a thing we call a &#8220;connection&#8221; which is a number of &#8220;bridges&#8221;.  A bridge is just a route between two models, including the foreign keys and so a connection is a full path from one model to another one via a chain of foreign keys.  In the simple case this is detected based on the model&#8217;s name and built in ActiveRecord methods for reporting foreign keys, but you can also specify a bridge manually for foreign keys that are created that don&#8217;t match the typical nomenclature.  That isn&#8217;t documented at the moment, but shout if that&#8217;s something that you need.</p>
<p><strong>Triggers</strong></p>
<p>Another neat thing is the automatic installation of model triggers.  So when it&#8217;s building that connection mentioned before, our system knows which models trigger updates which need to be sent over to Directed Edge to keep your data in sync.</p>
<p>So if <tt>acts_as_edgy</tt> is set up to automagically send updates (a config parameter that can be called in the config blog that gets written when you call rake edgy:configure) then as soon as a model changes anywhere along that path, we get the goods.  And this triggers a different code path that leads to our SQL generator just pulling out the stuff that needs to be updated in a single query.</p>
<p><b>Future:  Asynchronous Web-service Calls</b></p>
<p>And since those updates are hitting a remote web service, it&#8217;s ideal if they&#8217;re not blocking execution.  We make liberal use of a class we call Future (conceptually borrowed from <a href="http://doc.qt.nokia.com/4.7-snapshot/qfuture.html">QFuture</a> from Qt) which executes a block of code in a background thread and only forces synchronization when its data is accessed.  Here&#8217;s what it looks like:</p>

<div class="wp_syntax"><table><tr><td class="code"><pre class="ruby" style="font-family:monospace;"><span style="color:#9966CC; font-weight:bold;">class</span> Future
  <span style="color:#9966CC; font-weight:bold;">def</span> initialize<span style="color:#006600; font-weight:bold;">&#40;</span>postprocessor = <span style="color:#0000FF; font-weight:bold;">nil</span>, <span style="color:#006600; font-weight:bold;">&amp;</span>finalize<span style="color:#006600; font-weight:bold;">&#41;</span>
    <span style="color:#9966CC; font-weight:bold;">if</span> ENV<span style="color:#006600; font-weight:bold;">&#91;</span><span style="color:#996600;">'EDGY_SYNCHRONOUS'</span><span style="color:#006600; font-weight:bold;">&#93;</span>
      <span style="color:#0066ff; font-weight:bold;">@data</span> = postprocessor ? postprocessor.<span style="color:#9900CC;">call</span><span style="color:#006600; font-weight:bold;">&#40;</span>finalize.<span style="color:#9900CC;">call</span><span style="color:#006600; font-weight:bold;">&#41;</span> : finalize.<span style="color:#9900CC;">call</span>
      <span style="color:#0000FF; font-weight:bold;">return</span>
    <span style="color:#9966CC; font-weight:bold;">end</span>
    <span style="color:#0066ff; font-weight:bold;">@postprocessor</span> = postprocessor
    <span style="color:#0066ff; font-weight:bold;">@future</span> = <span style="color:#CC00FF; font-weight:bold;">Thread</span>.<span style="color:#9900CC;">new</span> <span style="color:#9966CC; font-weight:bold;">do</span>
      <span style="color:#9966CC; font-weight:bold;">begin</span>
        finalize.<span style="color:#9900CC;">call</span>
      <span style="color:#9966CC; font-weight:bold;">rescue</span> <span style="color:#006600; font-weight:bold;">=&gt;</span> ex
        warn <span style="color:#996600;">&quot;Exception in background thread: #{ex}&quot;</span>
      <span style="color:#9966CC; font-weight:bold;">end</span>
    <span style="color:#9966CC; font-weight:bold;">end</span>
  <span style="color:#9966CC; font-weight:bold;">end</span>
&nbsp;
  <span style="color:#9966CC; font-weight:bold;">def</span> method_missing<span style="color:#006600; font-weight:bold;">&#40;</span>method, <span style="color:#006600; font-weight:bold;">*</span>args, <span style="color:#006600; font-weight:bold;">&amp;</span>block<span style="color:#006600; font-weight:bold;">&#41;</span>
    data.<span style="color:#9900CC;">send</span><span style="color:#006600; font-weight:bold;">&#40;</span>method, <span style="color:#006600; font-weight:bold;">*</span>args, <span style="color:#006600; font-weight:bold;">&amp;</span>block<span style="color:#006600; font-weight:bold;">&#41;</span>
  <span style="color:#9966CC; font-weight:bold;">end</span>
&nbsp;
  <span style="color:#9966CC; font-weight:bold;">def</span> to_s
    data.<span style="color:#9900CC;">to_s</span>
  <span style="color:#9966CC; font-weight:bold;">end</span>
&nbsp;
  private
&nbsp;
  <span style="color:#9966CC; font-weight:bold;">def</span> data
    <span style="color:#0066ff; font-weight:bold;">@data</span> <span style="color:#006600; font-weight:bold;">||</span>= <span style="color:#0066ff; font-weight:bold;">@postprocessor</span> ? <span style="color:#0066ff; font-weight:bold;">@postprocessor</span>.<span style="color:#9900CC;">call</span><span style="color:#006600; font-weight:bold;">&#40;</span>@future.<span style="color:#9900CC;">value</span><span style="color:#006600; font-weight:bold;">&#41;</span> : <span style="color:#0066ff; font-weight:bold;">@future</span>.<span style="color:#9900CC;">value</span>
<span style="color:#9966CC; font-weight:bold;">end</span></pre></td></tr></table></div>

<p>Since we implement <tt>method_missing</tt> on the Future class to first block and then forward the call on, you can use it just like a normal value, e.g.:</p>

<div class="wp_syntax"><table><tr><td class="code"><pre class="ruby" style="font-family:monospace;">foo = Future.<span style="color:#9900CC;">new</span> <span style="color:#006600; font-weight:bold;">&#123;</span> <span style="color:#CC0066; font-weight:bold;">sleep</span> <span style="color:#006666;">1</span>; <span style="color:#006666;">42</span> <span style="color:#006600; font-weight:bold;">&#125;</span>
<span style="color:#CC0066; font-weight:bold;">puts</span> foo <span style="color:#008000; font-style:italic;"># prints 42</span></pre></td></tr></table></div>

<p>Altogether, while weighing in at a slim 382 lines of code the plugin is fairly light, but it&#8217;s pretty dense code with quite a bit of interesting stuff going on.</p>
<p>It&#8217;s still quite beta, so we expect there will be a handful of kinks to iron out and there are features we&#8217;d still like to add, but hopefully we&#8217;ll be doing that iteratively as you wonderful folks start building things with it and telling us what you need.</p>
<img src="http://feeds.feedburner.com/~r/directededge/~4/m2WtXcg0uPw" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://blog.directededge.com/2011/03/15/the-easiest-way-to-add-recommendations-to-your-rails-app-announcing-acts_as_edgy/feed/</wfw:commentRss>
		<slash:comments>9</slash:comments>
		<feedburner:origLink>http://blog.directededge.com/2011/03/15/the-easiest-way-to-add-recommendations-to-your-rails-app-announcing-acts_as_edgy/</feedburner:origLink></item>
		<item>
		<title>Google Spam Heresy: The AdSense Paradox</title>
		<link>http://feedproxy.google.com/~r/directededge/~3/3kZEU85OISs/</link>
		<comments>http://blog.directededge.com/2011/01/06/google-spam-heresy-the-adsense-paradox/#comments</comments>
		<pubDate>Thu, 06 Jan 2011 08:38:32 +0000</pubDate>
		<dc:creator>Scott Wheeler</dc:creator>
				<category><![CDATA[Uncategorized]]></category>

		<guid isPermaLink="false">http://blog.directededge.com/?p=696</guid>
		<description><![CDATA[There&#8217;s been much ado about the problem of spam and Google of late. Being something of a search weenie, as my eyelids were feeling heavy today I found myself mulling over the problem, &#8220;How would one detect Google spamming?&#8221; The answer turns out to be surprisingly easy. Who has an incentive to spam Google? People [...]]]></description>
				<content:encoded><![CDATA[<p>There&#8217;s been <a href="http://www.marco.org/2617546197">much</a> <a href="http://www.codinghorror.com/blog/2011/01/trouble-in-the-house-of-google.html">ado</a> about the problem of spam and Google of late. Being something of a search weenie, as my eyelids were feeling heavy today I found myself mulling over the problem, &#8220;How would one detect Google spamming?&#8221;</p>
<p>The answer turns out to be surprisingly easy. Who has an incentive to spam Google? People living from advertising. Who owns the largest online display ad network? Google.</p>
<p>Unlike with email, where there are heuristics at work to guess the intentions of the sender based on the content, Google has that data right in front of them.</p>
<p><strong>So, here&#8217;s the heresy: the spamminess of a web site is inversely proportional to its ad click-through.</strong></p>
<p>Think about it &#8212; in a typical internet search, a navigation path terminating at that page is the best result. If they click on an ad, it probably means you missed serving up the right page in the first place. As a corollary, the pages best optimized to pull you in via a search term and send you back out via a related ad are among the worst results.</p>
<p>So if you&#8217;re Google which value do you optimize for? More ad clicks or better search results? I&#8217;m a big enough Google fan that I believe that they&#8217;d mostly want to optimize for good search results since that&#8217;s what made them the company that they are. But what do you do if there&#8217;s an inverse correlation between the two? Bite the hand that feeds you?</p>
<p>Thinking about things this way in my opinion makes the issue even more interesting, because it seems to hint at something systemic &#8212; i.e. that there there might be something deeper problematic in financing search through display advertising.</p>
<p>Obviously this is a massive oversimplification of the problem of spam, but the paradox intrigued me.</p>
<img src="http://feeds.feedburner.com/~r/directededge/~4/3kZEU85OISs" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://blog.directededge.com/2011/01/06/google-spam-heresy-the-adsense-paradox/feed/</wfw:commentRss>
		<slash:comments>6</slash:comments>
		<feedburner:origLink>http://blog.directededge.com/2011/01/06/google-spam-heresy-the-adsense-paradox/</feedburner:origLink></item>
		<item>
		<title>What’s happening?</title>
		<link>http://feedproxy.google.com/~r/directededge/~3/toaL2S4K2kc/</link>
		<comments>http://blog.directededge.com/2010/11/29/whats-happening/#comments</comments>
		<pubDate>Tue, 30 Nov 2010 05:55:55 +0000</pubDate>
		<dc:creator>Scott Wheeler</dc:creator>
				<category><![CDATA[Uncategorized]]></category>

		<guid isPermaLink="false">http://blog.directededge.com/?p=687</guid>
		<description><![CDATA[So, the blog has been a bit silent of late, mostly because there&#8217;s been a lot of incremental stuff that&#8217;s been happening. We shout that stuff out over at Twitter and Facebook, but we&#8217;ll pull some of the last couple-o-months together here. But while I still have you attention: We need Rails beta testers. If [...]]]></description>
				<content:encoded><![CDATA[<p><img class="aligncenter size-full wp-image-688" title="What's happening?" src="http://blog.directededge.com/wp-content/uploads/2010/11/whats-happening.jpg" alt="" width="400" height="273" /></p>
<p>So, the blog has been a bit silent of late, mostly because there&#8217;s been a lot of incremental stuff that&#8217;s been happening. We shout that stuff out over at <a href="http://twitter.com/directededge">Twitter</a> and <a href="http://www.facebook.com/directededge">Facebook</a>, but we&#8217;ll pull some of the last couple-o-months together here. But while I still have you attention:</p>
<p><strong>We need Rails beta testers. If you&#8217;ve been thinking about adding recommendations to your Rails app, <a href="mailto:info@directededge.com">drop us a line now</a>.</strong></p>
<p>So, with that out of the way, a few of the things that have gone down in Directed Edge-land of late:</p>
<ul>
<li>We&#8217;ve been ramping up the features in our Shopify app based on things that we&#8217;ve been hearing from customers. The Shopify folks covered our new automagic bundling feature <a href="http://blog.shopify.com/2010/11/29/app-roundup">here</a>.</li>
<li>New version of our Ruby gem, 0.2.1 <a href="http://rubygems.org/gems/directed-edge">out</a>, and a couple of features added to our <a href="https://github.com/directededge/directed-edge-bindings/tree/master/Java/">Java bindings</a> so that they approach parity with the other bindings.</li>
<li>Wener Vogels, CTO of Amazon, has been spreading the good news about Directed Edge and we caught up with him when he was presenting about AWS in Berlin.</li>
<li>Yours truly will be doing a big tour out through New York, Austin and San Francisco / Silicon Valley in December and January. Holler if you want to meet and talk about recommendations.</li>
</ul>
<div id="attachment_691" class="wp-caption aligncenter" style="width: 610px"><img class="size-full wp-image-691" title="Werner Vogels - Directed Edge" src="http://blog.directededge.com/wp-content/uploads/2010/11/werner-vogels-directed-edge.jpeg" alt="" width="600" height="337" /><p class="wp-caption-text">Werner Vogels presenting Directed Edge as &quot;one of the building blocks of Cloud Computing&quot;</p></div>
<p>While a lot of the stuff in the last few months has been focused on internal tools and run-of-the-mill growing pains stuff, we&#8217;re getting pretty close to a series of announcements about new stuff coming down the pipe. (For some reason those always seem to come in batches.) We&#8217;re hoping the first of those will be ready in the next week or so.</p>
<img src="http://feeds.feedburner.com/~r/directededge/~4/toaL2S4K2kc" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://blog.directededge.com/2010/11/29/whats-happening/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		<feedburner:origLink>http://blog.directededge.com/2010/11/29/whats-happening/</feedburner:origLink></item>
		<item>
		<title>Directed Edge delivering 33% of revenue for The Market Quarter</title>
		<link>http://feedproxy.google.com/~r/directededge/~3/gD3da07kMCM/</link>
		<comments>http://blog.directededge.com/2010/06/16/directed-edge-delivering-33-of-revenue-for-the-market-quarte/#comments</comments>
		<pubDate>Wed, 16 Jun 2010 20:44:10 +0000</pubDate>
		<dc:creator>Scott Wheeler</dc:creator>
				<category><![CDATA[Uncategorized]]></category>

		<guid isPermaLink="false">http://blog.directededge.com/?p=681</guid>
		<description><![CDATA[Jonathan Briggs, who runs The Market Quarter wrote a few months back on getting up and going with our Shopify app for recommendations. He reported back today after analyzing the first few months of data: What astounded me was how many of my customers (not just visitors) clicked on recommendations. Indeed the ExpressRex referrer is [...]]]></description>
				<content:encoded><![CDATA[<p><img class="aligncenter size-full wp-image-682" title="marketquarter" src="http://blog.directededge.com/wp-content/uploads/2010/06/marketquarter.png" alt="" width="710" height="438" /><a href="http://www.jonathanbriggs.com/">Jonathan Briggs</a>, who runs <a href="http://www.marketquarter.com/">The Market Quarter</a> wrote a few months back on <a href="http://www.jonathanbriggs.com/experimenting-with-shopify-product-recommendation-engine-app,803,AR.html">getting up and going with our Shopify app</a> for recommendations. He reported back today after analyzing the first few months of data:</p>
<blockquote><p>What astounded me was how many of my customers (not just visitors) clicked on recommendations. Indeed the ExpressRex referrer is responsible for a full third (33%) of my revenue in the last quarter and has a 36% conversion rate.</p></blockquote>
<p>You can read the <a href="http://www.jonathanbriggs.com/ecommerce/33-of-revenue-expressrex-recommendation-engine-recommended,814,AR.html">full post</a> in his blog.</p>
<img src="http://feeds.feedburner.com/~r/directededge/~4/gD3da07kMCM" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://blog.directededge.com/2010/06/16/directed-edge-delivering-33-of-revenue-for-the-market-quarte/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		<feedburner:origLink>http://blog.directededge.com/2010/06/16/directed-edge-delivering-33-of-revenue-for-the-market-quarte/</feedburner:origLink></item>
		<item>
		<title>Shopify: Help Your Customers Discover Products and Grow Your Average Order Size</title>
		<link>http://feedproxy.google.com/~r/directededge/~3/k1Hl3je1rcM/</link>
		<comments>http://blog.directededge.com/2010/06/07/shopify-help-your-customers-discover-products-and-grow-your-average-order-size/#comments</comments>
		<pubDate>Mon, 07 Jun 2010 22:24:29 +0000</pubDate>
		<dc:creator>Scott Wheeler</dc:creator>
				<category><![CDATA[Uncategorized]]></category>

		<guid isPermaLink="false">http://blog.directededge.com/?p=678</guid>
		<description><![CDATA[From over at the Shopify blog: Eric Houtkooper of www.PupLife.com helps customers find and discover other products they may want. He has increased his customers’ average order size by recommending products as they browse the store and go through checkout. He suggests you do the same&#8230; More there.]]></description>
				<content:encoded><![CDATA[<p>From over at the Shopify blog:</p>
<blockquote><p>Eric Houtkooper of <a href="http://www.puplife.com/">www.PupLife.com</a> helps customers find and discover other products they may want. He has increased his customers’ average order size by recommending products as they browse the store and go through checkout. He suggests you do the same&#8230;</p></blockquote>
<p>More <a href="http://blog.shopify.com/2010/6/7/help-your-customers-discover-products-and-grow-your-average-order-size">there</a>.</p>
<img src="http://feeds.feedburner.com/~r/directededge/~4/k1Hl3je1rcM" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://blog.directededge.com/2010/06/07/shopify-help-your-customers-discover-products-and-grow-your-average-order-size/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		<feedburner:origLink>http://blog.directededge.com/2010/06/07/shopify-help-your-customers-discover-products-and-grow-your-average-order-size/</feedburner:origLink></item>
		<item>
		<title>Using taps without running a taps server</title>
		<link>http://feedproxy.google.com/~r/directededge/~3/FeRHIAwK1K8/</link>
		<comments>http://blog.directededge.com/2010/06/05/using-taps-without-running-a-taps-server/#comments</comments>
		<pubDate>Sun, 06 Jun 2010 04:50:18 +0000</pubDate>
		<dc:creator>Scott Wheeler</dc:creator>
				<category><![CDATA[Uncategorized]]></category>

		<guid isPermaLink="false">http://blog.directededge.com/?p=674</guid>
		<description><![CDATA[So, the Ruby world has a nifty thinger for syncing up databases over the interwebs. It&#8217;s called Taps, from the superheroes over at Heroku. It&#8217;s great &#8212; you just run a little Sinatra-based server and then give the database URLs and it handles all of the plumbing. But, you see, I was none-too-keen on having [...]]]></description>
				<content:encoded><![CDATA[<p>So, the Ruby world has a nifty thinger for syncing up databases over the interwebs. It&#8217;s called <a href="http://adam.heroku.com/past/2009/2/11/taps_for_easy_database_transfers/">Taps</a>, from the superheroes over at <a href="http://heroku.com/">Heroku</a>. It&#8217;s great &#8212; you just run a little Sinatra-based server and then give the database URLs and it handles all of the plumbing.</p>
<p>But, you see, I was none-too-keen on having another long-running Ruby process, not to mention an open port with production database data lumbering around on it, so I thought I&#8217;d let you guys in on a little hack we produced internally to let you get all of the fun of taps, but without the taps server.</p>
<p>Basically it starts up the taps server on the remote server, tunnels the transfer over SSH, then sends a ctrl-c to the server to kill it&#8217;s done. It&#8217;s pull-only very intentionally &#8212; I want to push from a development database to a production database about like I want a hole in my head.</p>

<div class="wp_syntax"><table><tr><td class="code"><pre class="ruby" style="font-family:monospace;"><span style="color:#008000; font-style:italic;">#!/usr/bin/env ruby</span>
&nbsp;
<span style="color:#CC0066; font-weight:bold;">require</span> <span style="color:#996600;">'rubygems'</span>
<span style="color:#CC0066; font-weight:bold;">require</span> <span style="color:#996600;">'active_support/secure_random'</span>
<span style="color:#CC0066; font-weight:bold;">require</span> <span style="color:#996600;">'net/ssh'</span>
&nbsp;
SSH_USER = <span style="color:#996600;">'[sshuser]'</span>
SSH_HOST = <span style="color:#996600;">'[dbhost]'</span>
&nbsp;
LOCAL_DB = <span style="color:#996600;">'mysql://[dbuser]:[dbpass]@localhost/[dbname]'</span>
REMOTE_DB = <span style="color:#996600;">'mysql://[dbuser]:[dbpass]@localhost/[dbname]'</span>
&nbsp;
TAPS_USER = <span style="color:#6666ff; font-weight:bold;">ActiveSupport::SecureRandom</span>.<span style="color:#9900CC;">hex</span><span style="color:#006600; font-weight:bold;">&#40;</span><span style="color:#006666;">16</span><span style="color:#006600; font-weight:bold;">&#41;</span>
TAPS_PASS = <span style="color:#6666ff; font-weight:bold;">ActiveSupport::SecureRandom</span>.<span style="color:#9900CC;">hex</span><span style="color:#006600; font-weight:bold;">&#40;</span><span style="color:#006666;">16</span><span style="color:#006600; font-weight:bold;">&#41;</span>
&nbsp;
URL = <span style="color:#996600;">&quot;http://#{TAPS_USER}:#{TAPS_PASS}@localhost:5000&quot;</span>
&nbsp;
<span style="color:#6666ff; font-weight:bold;">Net::SSH</span>.<span style="color:#9900CC;">start</span><span style="color:#006600; font-weight:bold;">&#40;</span>SSH_HOST, SSH_USER, <span style="color:#ff3333; font-weight:bold;">:compression</span> <span style="color:#006600; font-weight:bold;">=&gt;</span> <span style="color:#0000FF; font-weight:bold;">true</span><span style="color:#006600; font-weight:bold;">&#41;</span> <span style="color:#9966CC; font-weight:bold;">do</span> <span style="color:#006600; font-weight:bold;">|</span>ssh<span style="color:#006600; font-weight:bold;">|</span>
  ssh.<span style="color:#9900CC;">forward</span>.<span style="color:#9900CC;">local</span><span style="color:#006600; font-weight:bold;">&#40;</span><span style="color:#006666;">5000</span>, <span style="color:#996600;">'localhost'</span>, <span style="color:#006666;">5000</span><span style="color:#006600; font-weight:bold;">&#41;</span>
&nbsp;
  ready = <span style="color:#0000FF; font-weight:bold;">false</span>
&nbsp;
  channel = ssh.<span style="color:#9900CC;">open_channel</span> <span style="color:#9966CC; font-weight:bold;">do</span> <span style="color:#006600; font-weight:bold;">|</span>c<span style="color:#006600; font-weight:bold;">|</span>
    c.<span style="color:#9900CC;">request_pty</span>
    c.<span style="color:#9900CC;">on_data</span> <span style="color:#006600; font-weight:bold;">&#123;</span> <span style="color:#006600; font-weight:bold;">|</span>c, data<span style="color:#006600; font-weight:bold;">|</span> ready = <span style="color:#0000FF; font-weight:bold;">true</span> <span style="color:#9966CC; font-weight:bold;">if</span> data =~ <span style="color:#006600; font-weight:bold;">/</span>port=<span style="color:#006666;">5000</span><span style="color:#006600; font-weight:bold;">/</span> <span style="color:#006600; font-weight:bold;">&#125;</span>
    c.<span style="color:#CC0066; font-weight:bold;">exec</span><span style="color:#006600; font-weight:bold;">&#40;</span><span style="color:#996600;">&quot;taps server #{REMOTE_DB} #{TAPS_USER} #{TAPS_PASS}&quot;</span><span style="color:#006600; font-weight:bold;">&#41;</span>
  <span style="color:#9966CC; font-weight:bold;">end</span>
&nbsp;
  finished = <span style="color:#0000FF; font-weight:bold;">false</span>
&nbsp;
  <span style="color:#CC00FF; font-weight:bold;">Thread</span>.<span style="color:#9900CC;">new</span> <span style="color:#9966CC; font-weight:bold;">do</span>
    <span style="color:#CC0066; font-weight:bold;">sleep</span> <span style="color:#006666;">0.1</span> <span style="color:#9966CC; font-weight:bold;">until</span> ready
    <span style="color:#CC0066; font-weight:bold;">system</span> <span style="color:#996600;">&quot;taps pull #{LOCAL_DB} #{URL}&quot;</span>
    finished = <span style="color:#0000FF; font-weight:bold;">true</span>
  <span style="color:#9966CC; font-weight:bold;">end</span>
&nbsp;
  ssh.<span style="color:#CC0066; font-weight:bold;">loop</span><span style="color:#006600; font-weight:bold;">&#40;</span><span style="color:#006666;">0.1</span><span style="color:#006600; font-weight:bold;">&#41;</span> <span style="color:#9966CC; font-weight:bold;">do</span>
    channel.<span style="color:#9900CC;">send_data</span><span style="color:#006600; font-weight:bold;">&#40;</span><span style="color:#6666ff; font-weight:bold;">Net::SSH::Connection::Term::VINTR</span><span style="color:#006600; font-weight:bold;">&#41;</span> <span style="color:#9966CC; font-weight:bold;">if</span> finished
    !finished
  <span style="color:#9966CC; font-weight:bold;">end</span>
<span style="color:#9966CC; font-weight:bold;">end</span></pre></td></tr></table></div>

<p>Substitute in the right values in the constants up at the top and you&#8217;ve got a nifty way to securely use taps without leaving a server running.</p>
<img src="http://feeds.feedburner.com/~r/directededge/~4/FeRHIAwK1K8" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://blog.directededge.com/2010/06/05/using-taps-without-running-a-taps-server/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		<feedburner:origLink>http://blog.directededge.com/2010/06/05/using-taps-without-running-a-taps-server/</feedburner:origLink></item>
		<item>
		<title>What programming languages do our customers use?</title>
		<link>http://feedproxy.google.com/~r/directededge/~3/s5nLfQ57PqE/</link>
		<comments>http://blog.directededge.com/2010/05/30/what-programming-languages-do-our-customers-use/#comments</comments>
		<pubDate>Sun, 30 May 2010 08:12:28 +0000</pubDate>
		<dc:creator>Scott Wheeler</dc:creator>
				<category><![CDATA[Uncategorized]]></category>

		<guid isPermaLink="false">http://blog.directededge.com/?p=660</guid>
		<description><![CDATA[No huge shockers, but a few places where it&#8217;s interesting to compare perceptions to reality: PHP, for better or worse, still dominates web development. Python&#8217;s much closer to Ruby in the usage we see, despite making less noise. Scala, despite being the new hotness has very little actual uptake. Perl is all but out of [...]]]></description>
				<content:encoded><![CDATA[<p><img class="aligncenter size-full wp-image-665" title="programming-languages" src="http://blog.directededge.com/wp-content/uploads/2010/05/programming-languages.png" alt="" width="580" height="619" /></p>
<p>No huge shockers, but a few places where it&#8217;s interesting to compare perceptions to reality:</p>
<ul>
<li>PHP, for better or worse, still dominates web development.</li>
<li>Python&#8217;s much closer to Ruby in the usage we see, despite making less noise.</li>
<li>Scala, despite being the new hotness has very little actual uptake.</li>
<li>Perl is all but out of the game (though still more than Scala)</li>
</ul>
<img src="http://feeds.feedburner.com/~r/directededge/~4/s5nLfQ57PqE" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://blog.directededge.com/2010/05/30/what-programming-languages-do-our-customers-use/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		<feedburner:origLink>http://blog.directededge.com/2010/05/30/what-programming-languages-do-our-customers-use/</feedburner:origLink></item>
	</channel>
</rss><!-- Dynamic page generated in 0.341 seconds. --><!-- Cached page generated by WP-Super-Cache on 2013-05-15 22:25:21 --><!-- Compression = gzip -->
