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

<channel>
	<title>Drew Gillson</title>
	<atom:link href="http://www.drewgillson.com/blog/feed/" rel="self" type="application/rss+xml" />
	<link>http://www.drewgillson.com/blog</link>
	<description>e-commerce with Magento &#124; LiveOutThere.com startup life</description>
	<lastBuildDate>Sat, 03 Mar 2012 04:25:05 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.3.1</generator>
		<item>
		<title>Export data from SQL Server as CSV</title>
		<link>http://www.drewgillson.com/blog/export-data-sql-server-csv/</link>
		<comments>http://www.drewgillson.com/blog/export-data-sql-server-csv/#comments</comments>
		<pubDate>Sat, 03 Mar 2012 04:06:45 +0000</pubDate>
		<dc:creator>Drew</dc:creator>
				<category><![CDATA[SQL Server]]></category>

		<guid isPermaLink="false">http://www.drewgillson.com/blog/?p=152</guid>
		<description><![CDATA[I do this all the time when moving data from MSSQL to Magento, and figured I would share the code. All you need to call is call this stored procedure and pass the name of the object you want to]]></description>
			<content:encoded><![CDATA[<p>I do this all the time when moving data from MSSQL to Magento, and figured I would share the code. All you need to call is call this stored procedure and pass the name of the object you want to export, and the destination file name, like this:</p>
<blockquote><p>EXEC dbo.sproc_exportObjectToCSV &#8216;view_Customers&#8217;, &#8216;C:\Data\MSSQL\customers.csv&#8217; GO</p></blockquote>
<p>Here&#8217;s the SQL script:</p>
<div id="gist-1964274" class="gist">

        <div class="gist-file">
          <div class="gist-data gist-syntax">
              <div class="highlight"><pre><div class='line' id='LC1'><span class="cm">/****** Object:  StoredProcedure [dbo].[sproc_exportObjectToCSV]    Script Date: 03/02/2012 21:00:39 ******/</span></div><div class='line' id='LC2'><span class="k">SET</span> <span class="n">ANSI_NULLS</span> <span class="k">ON</span></div><div class='line' id='LC3'><span class="k">GO</span></div><div class='line' id='LC4'><br/></div><div class='line' id='LC5'><span class="k">SET</span> <span class="n">QUOTED_IDENTIFIER</span> <span class="k">ON</span></div><div class='line' id='LC6'><span class="k">GO</span></div><div class='line' id='LC7'><br/></div><div class='line' id='LC8'><span class="n">sp_configure</span> <span class="s1">&#39;Ad Hoc Distributed Queries&#39;</span><span class="p">,</span> <span class="mi">1</span><span class="p">;</span></div><div class='line' id='LC9'><span class="n">RECONFIGURE</span><span class="p">;</span></div><div class='line' id='LC10'><span class="k">GO</span></div><div class='line' id='LC11'><br/></div><div class='line' id='LC12'><span class="k">CREATE</span> <span class="k">VIEW</span> <span class="n">view_Columns</span> <span class="k">AS</span> <span class="k">SELECT</span> <span class="s1">&#39;&#39;</span> <span class="k">AS</span> <span class="k">COLUMN_NAME</span></div><div class='line' id='LC13'><span class="k">GO</span></div><div class='line' id='LC14'><br/></div><div class='line' id='LC15'><span class="k">CREATE</span> <span class="k">PROCEDURE</span> <span class="p">[</span><span class="n">dbo</span><span class="p">].[</span><span class="n">sproc_exportObjectToCSV</span><span class="p">]</span></div><div class='line' id='LC16'>	<span class="o">@</span><span class="k">table_name</span> <span class="nb">varchar</span><span class="p">(</span><span class="mi">255</span><span class="p">),</span></div><div class='line' id='LC17'>	<span class="o">@</span><span class="n">file_name</span> <span class="nb">varchar</span><span class="p">(</span><span class="mi">1024</span><span class="p">)</span></div><div class='line' id='LC18'><span class="k">AS</span> </div><div class='line' id='LC19'><span class="k">BEGIN</span></div><div class='line' id='LC20'>	<span class="k">DECLARE</span> <span class="o">@</span><span class="k">column_name</span> <span class="nb">varchar</span><span class="p">(</span><span class="mi">255</span><span class="p">),</span> <span class="o">@</span><span class="n">sql_column_names</span> <span class="nb">varchar</span><span class="p">(</span><span class="mi">4000</span><span class="p">),</span> <span class="o">@</span><span class="n">sql_columns</span> <span class="nb">varchar</span><span class="p">(</span><span class="mi">4000</span><span class="p">),</span> <span class="o">@</span><span class="k">sql</span> <span class="nb">varchar</span><span class="p">(</span><span class="mi">4000</span><span class="p">),</span> <span class="o">@</span><span class="n">db_name</span> <span class="nb">varchar</span><span class="p">(</span><span class="mi">255</span><span class="p">)</span></div><div class='line' id='LC21'><br/></div><div class='line' id='LC22'>	<span class="k">SET</span> <span class="o">@</span><span class="n">db_name</span> <span class="o">=</span> <span class="n">DB_NAME</span><span class="p">()</span></div><div class='line' id='LC23'>	<span class="k">SET</span> <span class="o">@</span><span class="n">sql_column_names</span> <span class="o">=</span> <span class="s1">&#39;SELECT &#39;</span></div><div class='line' id='LC24'>	<span class="k">SET</span> <span class="o">@</span><span class="n">sql_columns</span> <span class="o">=</span> <span class="s1">&#39;SELECT &#39;</span></div><div class='line' id='LC25'>	<span class="k">EXEC</span><span class="p">(</span><span class="s1">&#39;ALTER VIEW view_Columns AS SELECT COLUMN_NAME FROM OPENROWSET (&#39;&#39;SQLOLEDB&#39;&#39;,&#39;&#39;Server=(local);TRUSTED_CONNECTION=YES;&#39;&#39;,&#39;&#39;set fmtonly off exec &#39;</span> <span class="o">+</span> <span class="o">@</span><span class="n">db_name</span> <span class="o">+</span> <span class="s1">&#39;.dbo.sp_columns &#39;&#39;&#39;&#39;&#39;</span> <span class="o">+</span> <span class="o">@</span><span class="k">table_name</span> <span class="o">+</span> <span class="s1">&#39;&#39;&#39;&#39;&#39;&#39;&#39;) AS tbl&#39;</span><span class="p">)</span></div><div class='line' id='LC26'>	<span class="k">DECLARE</span> <span class="n">c1</span> <span class="k">CURSOR</span> <span class="k">FOR</span> <span class="k">SELECT</span> <span class="k">COLUMN_NAME</span> <span class="k">FROM</span> <span class="n">view_Columns</span></div><div class='line' id='LC27'>	<span class="k">OPEN</span> <span class="n">c1</span></div><div class='line' id='LC28'><br/></div><div class='line' id='LC29'>	<span class="k">FETCH</span> <span class="k">NEXT</span> <span class="k">FROM</span> <span class="n">c1</span> <span class="k">INTO</span> <span class="o">@</span><span class="k">column_name</span></div><div class='line' id='LC30'>	<span class="n">WHILE</span> <span class="o">@@</span><span class="n">FETCH_STATUS</span> <span class="o">=</span> <span class="mi">0</span> <span class="k">BEGIN</span></div><div class='line' id='LC31'><br/></div><div class='line' id='LC32'>		<span class="k">SET</span> <span class="o">@</span><span class="n">sql_column_names</span> <span class="o">=</span> <span class="o">@</span><span class="n">sql_column_names</span> <span class="o">+</span> <span class="s1">&#39;&#39;&#39;\&quot;&#39;</span> <span class="o">+</span> <span class="o">@</span><span class="k">column_name</span> <span class="o">+</span> <span class="s1">&#39;\&quot;&#39;&#39; AS &#39;</span> <span class="o">+</span> <span class="o">@</span><span class="k">column_name</span> <span class="o">+</span> <span class="s1">&#39;, &#39;</span></div><div class='line' id='LC33'>		<span class="k">SET</span> <span class="o">@</span><span class="n">sql_columns</span> <span class="o">=</span> <span class="o">@</span><span class="n">sql_columns</span> <span class="o">+</span> <span class="s1">&#39;&#39;&#39;\&quot;&#39;&#39; + CAST(REPLACE(&#39;</span> <span class="o">+</span> <span class="o">@</span><span class="k">column_name</span> <span class="o">+</span> <span class="s1">&#39;,&#39;&#39;\&quot;&#39;&#39;,&#39;&#39;\&quot;\&quot;&#39;&#39;) AS varchar(4000)) + &#39;&#39;\&quot;&#39;&#39;, &#39;</span></div><div class='line' id='LC34'><br/></div><div class='line' id='LC35'>		<span class="k">FETCH</span> <span class="k">NEXT</span> <span class="k">FROM</span> <span class="n">c1</span> <span class="k">INTO</span> <span class="o">@</span><span class="k">column_name</span></div><div class='line' id='LC36'>	<span class="k">END</span></div><div class='line' id='LC37'><br/></div><div class='line' id='LC38'>	<span class="k">CLOSE</span> <span class="n">c1</span></div><div class='line' id='LC39'>	<span class="k">DEALLOCATE</span> <span class="n">c1</span></div><div class='line' id='LC40'><br/></div><div class='line' id='LC41'>	<span class="k">SET</span> <span class="o">@</span><span class="n">sql_column_names</span> <span class="o">=</span> <span class="k">LEFT</span><span class="p">(</span><span class="o">@</span><span class="n">sql_column_names</span><span class="p">,</span><span class="n">LEN</span><span class="p">(</span><span class="o">@</span><span class="n">sql_column_names</span><span class="p">)</span><span class="o">-</span><span class="mi">1</span><span class="p">)</span></div><div class='line' id='LC42'>	<span class="k">SET</span> <span class="o">@</span><span class="n">sql_columns</span> <span class="o">=</span> <span class="k">LEFT</span><span class="p">(</span><span class="o">@</span><span class="n">sql_columns</span><span class="p">,</span><span class="n">LEN</span><span class="p">(</span><span class="o">@</span><span class="n">sql_columns</span><span class="p">)</span><span class="o">-</span><span class="mi">1</span><span class="p">)</span> <span class="o">+</span> <span class="s1">&#39; FROM &#39;</span> <span class="o">+</span> <span class="o">@</span><span class="n">db_name</span> <span class="o">+</span> <span class="s1">&#39;.dbo.&#39;</span> <span class="o">+</span> <span class="o">@</span><span class="k">table_name</span></div><div class='line' id='LC43'>	<span class="k">SET</span> <span class="o">@</span><span class="k">sql</span> <span class="o">=</span> <span class="s1">&#39;bcp &quot;&#39;</span> <span class="o">+</span> <span class="o">@</span><span class="n">sql_column_names</span> <span class="o">+</span> <span class="s1">&#39; UNION ALL &#39;</span> <span class="o">+</span> <span class="o">@</span><span class="n">sql_columns</span> <span class="o">+</span> <span class="s1">&#39;&quot; queryout &quot;&#39;</span> <span class="o">+</span> <span class="o">@</span><span class="n">file_name</span> <span class="o">+</span> <span class="s1">&#39;&quot; -w -t , -T -S &#39;</span> <span class="o">+</span> <span class="o">@@</span><span class="n">servername</span></div><div class='line' id='LC44'>	<span class="k">EXEC</span> <span class="n">master</span><span class="p">..</span><span class="n">xp_cmdshell</span> <span class="o">@</span><span class="k">sql</span></div><div class='line' id='LC45'><span class="k">END</span></div><div class='line' id='LC46'><br/></div><div class='line' id='LC47'><span class="k">GO</span></div></pre></div>
          </div>

          <div class="gist-meta">
            <a href="https://gist.github.com/raw/1964274/12e7132fb9609755b12ab255c0307807e6befa14/sproc_exportObjectToCSV.sql" style="float:right;">view raw</a>
            <a href="https://gist.github.com/1964274#file_sproc_export_object_to_csv.sql" style="float:right;margin-right:10px;color:#666">sproc_exportObjectToCSV.sql</a>
            <a href="https://gist.github.com/1964274">This Gist</a> brought to you by <a href="http://github.com">GitHub</a>.
          </div>
        </div>
</div>

<p>This stored procedure will automatically write out column headers, wrap your columns in double-quotes, and escape double-quotes in your columns. In other words, this outputs a *valid* CSV, which BCP does not.</p>
<p>The magic of this is the dynamically generated view, which we use to grab the column names for the view or table, and then iterate through them (using a cursor &#8212; not many good uses for cursors, but this is one of them) to build our column headers.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.drewgillson.com/blog/export-data-sql-server-csv/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Sow Seeds, Elements of Effective Community Building</title>
		<link>http://www.drewgillson.com/blog/sow-seeds-elements-effective-community-building/</link>
		<comments>http://www.drewgillson.com/blog/sow-seeds-elements-effective-community-building/#comments</comments>
		<pubDate>Thu, 12 Jan 2012 07:33:43 +0000</pubDate>
		<dc:creator>Drew</dc:creator>
				<category><![CDATA[Excerpts]]></category>

		<guid isPermaLink="false">http://www.drewgillson.com/blog/?p=144</guid>
		<description><![CDATA[Sow seeds. Recruit members and offer incentives for genuine participation in return for their advocacy. Most healthy online communities have passionate stewards leading the way. Lurkers are the majority, and most of them will never participate beyond creating an account]]></description>
			<content:encoded><![CDATA[<p>Sow seeds.</p>
<p><em>Recruit members and offer incentives for genuine participation in return for their advocacy.<span id="more-144"></span><strong> </strong></em></p>
<p>Most healthy online communities have passionate stewards leading the way. Lurkers are the majority, and most of them will never participate beyond creating an account and occasionally logging in. Increase the chances of retaining a visitor and encourage them to participate in a meaningful way by drawing their attention to something new, interesting, and immediately relevant. The trend online is increasingly attention-deficit, and the newest social aggregators like Pinterest are a smoking gun.</p>
<p>You can&#8217;t create this content yourself. You can&#8217;t create 1/10 of it yourself. Have your core members create it for you. Or <span style="text-decoration: underline;">curate</span> it for you. Run contests. Exchange pictures, video footage, and other content for merchandise. Set up guest blogging arrangements to draw both search engines and new visitors to your community. Reward your best participants in a meaningful, valuable way. What is it really worth?</p>
<blockquote><p>Excerpt pulled from a series of thoughts written in 2009 for one of my clients</p></blockquote>
<p><a href="http://www.drewgillson.com/blog/wp-content/uploads/2011/12/signature.jpg"><img title="Drew Gillson" src="http://www.drewgillson.com/blog/wp-content/uploads/2011/12/signature.jpg" alt="" width="86" height="100" /></a></p>
]]></content:encoded>
			<wfw:commentRss>http://www.drewgillson.com/blog/sow-seeds-elements-effective-community-building/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Focus, Elements of Effective Community Building</title>
		<link>http://www.drewgillson.com/blog/focus-elements-effective-community-building/</link>
		<comments>http://www.drewgillson.com/blog/focus-elements-effective-community-building/#comments</comments>
		<pubDate>Sat, 07 Jan 2012 01:24:55 +0000</pubDate>
		<dc:creator>Drew</dc:creator>
				<category><![CDATA[Excerpts]]></category>

		<guid isPermaLink="false">http://www.drewgillson.com/blog/?p=139</guid>
		<description><![CDATA[Focus. You can&#8217;t cater to everyone. Choose one audience and win them over first. The success of three of the largest social networks can be traced to a focused attempt to corral an audience and turn them into a powerful]]></description>
			<content:encoded><![CDATA[<div>
<p>Focus.</p>
<p><em>You can&#8217;t cater to everyone. Choose one audience and win them over first.<span id="more-139"></span></em></p>
<p>The success of three of the largest social networks can be traced to a focused attempt to corral an audience and turn them into a powerful core group of early adopters. In Facebook’s case, this core group was university students, followed by savvy technical professionals at software companies and consulting firms. Facebook first focused their marketing at university students who would appreciate the “exclusivity” of a social network designed specifically for their needs, and then moved on to attract technical professionals at companies like Accenture and Microsoft who were comfortable online and, by the very nature of their jobs, dealt with multitudes of equally savvy contacts.</p>
<p>LinkedIn, a professional social network with an initial focus on independent professional marketing and advertising consultants, formed a critical mass of members which radiated outwards to form the 35,000,000 strong community it is today. As with Facebook, the strategic insight was to hitch the vehicle to a group of members (independent consultants) who interact with a lot of people on a regular basis, like bees pollinating flowers. Today, LinkedIn welcomes members of all professions.</p>
<p>MySpace made a well-considered strategic decision to embrace independent musicians and bands, and has since dominated the category. By providing the tools to host and play music, provide event listings, and communicate with fans, MySpace is now the de- facto place for an independent band to use to communicate with their fans.</p>
<p>The importance of picking a target audience and delivering a specific message and feature-set that resonates with that audience cannot be overstated. Personalized, one- on-one communication forms the basis for effective marketing. Listen and respond to your audience’s needs, develop functionality specifically for them, shepherd them, and watch these people turn into raving fans.</p>
<blockquote><p>Excerpt pulled from a series of thoughts written in 2009 for one of my clients</p></blockquote>
<p><a href="http://www.drewgillson.com/blog/wp-content/uploads/2011/12/signature.jpg"><img title="Drew Gillson" src="http://www.drewgillson.com/blog/wp-content/uploads/2011/12/signature.jpg" alt="" width="86" height="100" /></a></p>
</div>
]]></content:encoded>
			<wfw:commentRss>http://www.drewgillson.com/blog/focus-elements-effective-community-building/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Customer service or inside sales?</title>
		<link>http://www.drewgillson.com/blog/customer-service-sales/</link>
		<comments>http://www.drewgillson.com/blog/customer-service-sales/#comments</comments>
		<pubDate>Fri, 06 Jan 2012 07:49:04 +0000</pubDate>
		<dc:creator>Drew</dc:creator>
				<category><![CDATA[Magento]]></category>
		<category><![CDATA[Marketing]]></category>

		<guid isPermaLink="false">http://www.drewgillson.com/blog/?p=127</guid>
		<description><![CDATA[Tonight I&#8217;ve been thinking a lot about our customers at LiveOutThere.com. Unfortunately Magento does not provide any standard tools to begin to understand who they are, what they want, and ultimately, how to sell more product to them. I wanted]]></description>
			<content:encoded><![CDATA[<p>Tonight I&#8217;ve been thinking a lot about our customers at LiveOutThere.com. Unfortunately Magento does not provide any standard tools to begin to understand who they are, what they want, and ultimately, how to sell more product to them. I wanted to do a quick analysis to answer some basic questions and provoke discussion among our team, so I put together a little report. This SQL query (use it on your Magento 1.6.1 database and update the LIKE statements to suit your own brands) tells me some very useful things:</p>
<div id="gist-1569465" class="gist">

        <div class="gist-file">
          <div class="gist-data gist-syntax">
              <div class="highlight"><pre><div class='line' id='LC1'><span class="c1">-- assuming Magento 1.6.1 data model</span></div><div class='line' id='LC2'><span class="k">SELECT</span> <span class="k">DISTINCT</span> <span class="n">customer_email</span><span class="p">,</span></div><div class='line' id='LC3'>	<span class="k">COUNT</span><span class="p">(</span><span class="o">*</span><span class="p">)</span> <span class="k">AS</span> <span class="n">purchases</span><span class="p">,</span></div><div class='line' id='LC4'>	<span class="p">(</span><span class="k">SELECT</span> <span class="k">LEFT</span><span class="p">(</span><span class="n">created_at</span><span class="p">,</span><span class="mi">11</span><span class="p">)</span> <span class="k">FROM</span> <span class="o">`</span><span class="n">sales_flat_order</span><span class="o">`</span> <span class="k">WHERE</span> <span class="n">customer_email</span> <span class="o">=</span> <span class="n">a</span><span class="p">.</span><span class="n">customer_email</span> <span class="k">ORDER</span> <span class="k">BY</span> <span class="n">created_at</span> <span class="k">DESC</span> <span class="k">LIMIT</span> <span class="mi">0</span><span class="p">,</span><span class="mi">1</span><span class="p">)</span> <span class="k">AS</span> <span class="n">last_purchase</span><span class="p">,</span></div><div class='line' id='LC5'>	<span class="p">(</span><span class="k">SELECT</span> <span class="k">COUNT</span><span class="p">(</span><span class="o">*</span><span class="p">)</span> <span class="k">FROM</span> <span class="n">sales_flat_order_item</span> <span class="k">WHERE</span> <span class="n">product_type</span> <span class="o">=</span> <span class="s1">&#39;simple&#39;</span> <span class="k">AND</span> <span class="n">sku</span> <span class="k">LIKE</span> <span class="s1">&#39;%IB%&#39;</span> <span class="k">AND</span> <span class="n">order_id</span> <span class="k">IN</span> <span class="p">(</span><span class="k">SELECT</span> <span class="n">entity_id</span> <span class="k">FROM</span> <span class="o">`</span><span class="n">sales_flat_order</span><span class="o">`</span> <span class="k">WHERE</span> <span class="n">customer_email</span> <span class="o">=</span> <span class="n">a</span><span class="p">.</span><span class="n">customer_email</span><span class="p">))</span> <span class="k">AS</span> <span class="n">icebreaker</span><span class="p">,</span></div><div class='line' id='LC6'>	<span class="p">(</span><span class="k">SELECT</span> <span class="k">COUNT</span><span class="p">(</span><span class="o">*</span><span class="p">)</span> <span class="k">FROM</span> <span class="n">sales_flat_order_item</span> <span class="k">WHERE</span> <span class="n">product_type</span> <span class="o">=</span> <span class="s1">&#39;simple&#39;</span> <span class="k">AND</span> <span class="n">sku</span> <span class="k">LIKE</span> <span class="s1">&#39;%TNF%&#39;</span> <span class="k">AND</span> <span class="n">order_id</span> <span class="k">IN</span> <span class="p">(</span><span class="k">SELECT</span> <span class="n">entity_id</span> <span class="k">FROM</span> <span class="o">`</span><span class="n">sales_flat_order</span><span class="o">`</span> <span class="k">WHERE</span> <span class="n">customer_email</span> <span class="o">=</span> <span class="n">a</span><span class="p">.</span><span class="n">customer_email</span><span class="p">))</span> <span class="k">AS</span> <span class="n">the_north_face</span><span class="p">,</span></div><div class='line' id='LC7'>	<span class="p">(</span><span class="k">SELECT</span> <span class="k">COUNT</span><span class="p">(</span><span class="o">*</span><span class="p">)</span> <span class="k">FROM</span> <span class="n">sales_flat_order_item</span> <span class="k">WHERE</span> <span class="n">product_type</span> <span class="o">=</span> <span class="s1">&#39;simple&#39;</span> <span class="k">AND</span> <span class="n">sku</span> <span class="k">LIKE</span> <span class="s1">&#39;%MHW%&#39;</span> <span class="k">AND</span> <span class="n">order_id</span> <span class="k">IN</span> <span class="p">(</span><span class="k">SELECT</span> <span class="n">entity_id</span> <span class="k">FROM</span> <span class="o">`</span><span class="n">sales_flat_order</span><span class="o">`</span> <span class="k">WHERE</span> <span class="n">customer_email</span> <span class="o">=</span> <span class="n">a</span><span class="p">.</span><span class="n">customer_email</span><span class="p">))</span> <span class="k">AS</span> <span class="n">mountain_hardwear</span><span class="p">,</span></div><div class='line' id='LC8'>	<span class="p">(</span><span class="k">SELECT</span> <span class="k">COUNT</span><span class="p">(</span><span class="o">*</span><span class="p">)</span> <span class="k">FROM</span> <span class="n">sales_flat_order_item</span> <span class="k">WHERE</span> <span class="n">product_type</span> <span class="o">=</span> <span class="s1">&#39;simple&#39;</span> <span class="k">AND</span> <span class="n">sku</span> <span class="k">LIKE</span> <span class="s1">&#39;%MAR%&#39;</span> <span class="k">AND</span> <span class="n">order_id</span> <span class="k">IN</span> <span class="p">(</span><span class="k">SELECT</span> <span class="n">entity_id</span> <span class="k">FROM</span> <span class="o">`</span><span class="n">sales_flat_order</span><span class="o">`</span> <span class="k">WHERE</span> <span class="n">customer_email</span> <span class="o">=</span> <span class="n">a</span><span class="p">.</span><span class="n">customer_email</span><span class="p">))</span> <span class="k">AS</span> <span class="n">marmot</span><span class="p">,</span></div><div class='line' id='LC9'>	<span class="p">(</span><span class="k">SELECT</span> <span class="k">COUNT</span><span class="p">(</span><span class="o">*</span><span class="p">)</span> <span class="k">FROM</span> <span class="n">sales_flat_order_item</span> <span class="k">WHERE</span> <span class="n">product_type</span> <span class="o">=</span> <span class="s1">&#39;simple&#39;</span> <span class="k">AND</span> <span class="n">sku</span> <span class="k">LIKE</span> <span class="s1">&#39;%LOLE%&#39;</span> <span class="k">AND</span> <span class="n">order_id</span> <span class="k">IN</span> <span class="p">(</span><span class="k">SELECT</span> <span class="n">entity_id</span> <span class="k">FROM</span> <span class="o">`</span><span class="n">sales_flat_order</span><span class="o">`</span> <span class="k">WHERE</span> <span class="n">customer_email</span> <span class="o">=</span> <span class="n">a</span><span class="p">.</span><span class="n">customer_email</span><span class="p">))</span> <span class="k">AS</span> <span class="n">lole</span><span class="p">,</span></div><div class='line' id='LC10'>	<span class="p">(</span><span class="k">SELECT</span> <span class="k">COUNT</span><span class="p">(</span><span class="o">*</span><span class="p">)</span> <span class="k">FROM</span> <span class="n">sales_flat_order_item</span> <span class="k">WHERE</span> <span class="n">product_type</span> <span class="o">=</span> <span class="s1">&#39;simple&#39;</span> <span class="k">AND</span> <span class="n">sku</span> <span class="k">LIKE</span> <span class="s1">&#39;%SW%&#39;</span> <span class="k">AND</span> <span class="n">order_id</span> <span class="k">IN</span> <span class="p">(</span><span class="k">SELECT</span> <span class="n">entity_id</span> <span class="k">FROM</span> <span class="o">`</span><span class="n">sales_flat_order</span><span class="o">`</span> <span class="k">WHERE</span> <span class="n">customer_email</span> <span class="o">=</span> <span class="n">a</span><span class="p">.</span><span class="n">customer_email</span><span class="p">))</span> <span class="k">AS</span> <span class="n">smartwool</span><span class="p">,</span></div><div class='line' id='LC11'>	<span class="p">(</span><span class="k">SELECT</span> <span class="k">COUNT</span><span class="p">(</span><span class="o">*</span><span class="p">)</span> <span class="k">FROM</span> <span class="n">sales_flat_order_item</span> <span class="k">WHERE</span> <span class="n">product_type</span> <span class="o">=</span> <span class="s1">&#39;simple&#39;</span> <span class="k">AND</span> <span class="n">sku</span> <span class="k">LIKE</span> <span class="s1">&#39;%OSP%&#39;</span> <span class="k">AND</span> <span class="n">order_id</span> <span class="k">IN</span> <span class="p">(</span><span class="k">SELECT</span> <span class="n">entity_id</span> <span class="k">FROM</span> <span class="o">`</span><span class="n">sales_flat_order</span><span class="o">`</span> <span class="k">WHERE</span> <span class="n">customer_email</span> <span class="o">=</span> <span class="n">a</span><span class="p">.</span><span class="n">customer_email</span><span class="p">))</span> <span class="k">AS</span> <span class="n">osprey</span><span class="p">,</span></div><div class='line' id='LC12'>	<span class="p">(</span><span class="k">SELECT</span> <span class="k">COUNT</span><span class="p">(</span><span class="o">*</span><span class="p">)</span> <span class="k">FROM</span> <span class="n">sales_flat_order_item</span> <span class="k">WHERE</span> <span class="n">product_type</span> <span class="o">=</span> <span class="s1">&#39;simple&#39;</span> <span class="k">AND</span> <span class="n">sku</span> <span class="k">LIKE</span> <span class="s1">&#39;%SOR%&#39;</span> <span class="k">AND</span> <span class="n">order_id</span> <span class="k">IN</span> <span class="p">(</span><span class="k">SELECT</span> <span class="n">entity_id</span> <span class="k">FROM</span> <span class="o">`</span><span class="n">sales_flat_order</span><span class="o">`</span> <span class="k">WHERE</span> <span class="n">customer_email</span> <span class="o">=</span> <span class="n">a</span><span class="p">.</span><span class="n">customer_email</span><span class="p">))</span> <span class="k">AS</span> <span class="n">sorel</span><span class="p">,</span></div><div class='line' id='LC13'>	<span class="p">(</span><span class="k">SELECT</span> <span class="k">COUNT</span><span class="p">(</span><span class="o">*</span><span class="p">)</span> <span class="k">FROM</span> <span class="n">sales_flat_order_item</span> <span class="k">WHERE</span> <span class="n">product_type</span> <span class="o">=</span> <span class="s1">&#39;simple&#39;</span> <span class="k">AND</span> <span class="n">sku</span> <span class="k">LIKE</span> <span class="s1">&#39;%ARC%&#39;</span> <span class="k">AND</span> <span class="n">order_id</span> <span class="k">IN</span> <span class="p">(</span><span class="k">SELECT</span> <span class="n">entity_id</span> <span class="k">FROM</span> <span class="o">`</span><span class="n">sales_flat_order</span><span class="o">`</span> <span class="k">WHERE</span> <span class="n">customer_email</span> <span class="o">=</span> <span class="n">a</span><span class="p">.</span><span class="n">customer_email</span><span class="p">))</span> <span class="k">AS</span> <span class="n">arcteryx</span><span class="p">,</span></div><div class='line' id='LC14'>	<span class="p">(</span><span class="k">SELECT</span> <span class="k">COUNT</span><span class="p">(</span><span class="o">*</span><span class="p">)</span> <span class="k">FROM</span> <span class="n">sales_flat_order_item</span> <span class="k">WHERE</span> <span class="n">product_type</span> <span class="o">=</span> <span class="s1">&#39;simple&#39;</span> <span class="k">AND</span> <span class="n">sku</span> <span class="k">LIKE</span> <span class="s1">&#39;%COL%&#39;</span> <span class="k">AND</span> <span class="n">order_id</span> <span class="k">IN</span> <span class="p">(</span><span class="k">SELECT</span> <span class="n">entity_id</span> <span class="k">FROM</span> <span class="o">`</span><span class="n">sales_flat_order</span><span class="o">`</span> <span class="k">WHERE</span> <span class="n">customer_email</span> <span class="o">=</span> <span class="n">a</span><span class="p">.</span><span class="n">customer_email</span><span class="p">))</span> <span class="k">AS</span> <span class="n">columbia</span></div><div class='line' id='LC15'><span class="k">FROM</span> <span class="o">`</span><span class="n">sales_flat_order</span><span class="o">`</span> <span class="k">AS</span> <span class="n">a</span></div><div class='line' id='LC16'><span class="k">GROUP</span> <span class="k">BY</span> <span class="n">customer_email</span></div><div class='line' id='LC17'><span class="k">ORDER</span> <span class="k">BY</span> <span class="k">COUNT</span><span class="p">(</span><span class="o">*</span><span class="p">)</span> <span class="k">DESC</span></div></pre></div>
          </div>

          <div class="gist-meta">
            <a href="https://gist.github.com/raw/1569465/ccab93a19e1f3ff0fac25ea2974cd10daa485e40/customers.sql" style="float:right;">view raw</a>
            <a href="https://gist.github.com/1569465#file_customers.sql" style="float:right;margin-right:10px;color:#666">customers.sql</a>
            <a href="https://gist.github.com/1569465">This Gist</a> brought to you by <a href="http://github.com">GitHub</a>.
          </div>
        </div>
</div>

<p>The output of that query tells me how many times a particular email address has purchased from us (this aggregates together guest and logged-in-customer purchases, which is what I want), the date of their last purchase, and the number of items they have purchased from each of the major brands that we carry. The output looks something like this:</p>
<p><a href="http://www.drewgillson.com/blog/wp-content/uploads/2012/01/Screen-Shot-2012-01-06-at-12.21.00-AM.jpg"><img class="size-full wp-image-128  alignnone" title="Purchases by customer by brand" src="http://www.drewgillson.com/blog/wp-content/uploads/2012/01/Screen-Shot-2012-01-06-at-12.21.00-AM.jpg" alt="" width="475" height="107" /></a></p>
<p>Now, it&#8217;s fairly obvious that the customer in row #2 is a <strong>great</strong> customer. They have made 7 purchases, each time buying a product from Arc`teryx, one of our premium brands. This person clearly loves Arc`teryx. Could they make my job as a marketer any more clear?</p>
<p>In most cases, you don&#8217;t see such a clear, strong, signal. I believe when you do, it should be acted on. This kind of business intelligence is immediately valuable. In LiveOutThere.com&#8217;s case, the Spring 2012 line is right around the corner, and I know that I will be able to sell that customer some great kit in a personal, intelligent way.</p>
<p>This brings me around to the title of my post, which questions the similarities between customer service and inside sales roles, and how the lines <em>should </em>blur in a boutique e-commerce operation like LiveOutThere.com. There are some fundamental aspects to customer service &#8211; fulfillment, communication, availability, etc. &#8211; but when you have those under control, I believe you need to build on those fundamentals and have your &#8220;customer service&#8221; team sell back to your customers in a precise, targeted way. And that&#8217;s a sales force, not a customer service department.</p>
<p>I believe we sell some truly great gear &#8212; the best available. Our team knows their stuff and can give you well-founded recommendations based on their personal experience with the product. When we know the most about your buying habits and preferences, we can serve you better than anyone else, and that&#8217;s just the reality. It&#8217;s better for our customers and it&#8217;s better for us.</p>
<p>On a practical level, we use this intimate knowledge of our customers to quickly create segments in our email marketing system. Take a peek:</p>
<p><a href="http://www.drewgillson.com/blog/wp-content/uploads/2012/01/Screen-Shot-2012-01-06-at-12.50.03-AM.jpg"><img class=" wp-image-132 alignnone" title="Screen Shot 2012-01-06 at 12.50.03 AM" src="http://www.drewgillson.com/blog/wp-content/uploads/2012/01/Screen-Shot-2012-01-06-at-12.50.03-AM.jpg" alt="" width="656" height="443" /></a></p>
<p>How do you feel about that? Would you rather receive emails that are increasingly relevant to your needs, interests, and position in the buying cycle, or does it just piss you off?</p>
<p>Marketing has become increasingly micro-targeted over the last 10 years and you will see me talking about this more and more in the context of Magento e-commerce. It&#8217;s a fascinating, contemporary subject and brings up many interesting topics (privacy concerns certainly one of them) but I believe that in another 10 years our customers will absolutely not stand for marketing with a broad brush-stroke.</p>
<p><a href="http://www.drewgillson.com/blog/wp-content/uploads/2011/12/signature.jpg"><img title="Drew Gillson" src="http://www.drewgillson.com/blog/wp-content/uploads/2011/12/signature.jpg" alt="" width="86" height="100" /></a></p>
]]></content:encoded>
			<wfw:commentRss>http://www.drewgillson.com/blog/customer-service-sales/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Where are all the great website conversion optimization tools?</title>
		<link>http://www.drewgillson.com/blog/great-website-conversion-optimization-tools/</link>
		<comments>http://www.drewgillson.com/blog/great-website-conversion-optimization-tools/#comments</comments>
		<pubDate>Wed, 04 Jan 2012 08:38:40 +0000</pubDate>
		<dc:creator>Drew</dc:creator>
				<category><![CDATA[Conversion Optimization]]></category>

		<guid isPermaLink="false">http://www.drewgillson.com/blog/?p=111</guid>
		<description><![CDATA[Every time I have looked for a small website or web application testing framework I have come up short. Google Web Optimizer has been around forever and has limited adoption among web masters. Optimizely is neat and is probably the]]></description>
			<content:encoded><![CDATA[<p>Every time I have looked for a small website or web application testing framework I have come up short. Google Web Optimizer has been around forever and has limited adoption among web masters. <span id="more-111"></span>Optimizely is neat and is probably the most comparable lower-end-of-the-market tool that you would compare GWO with. There are other tools, but for whatever reason a lot of them seem to have just fizzled out and become unmaintained and irrelevant (<a href="http://ejohn.org/blog/genetic-ab-testing-with-javascript/">Genetify</a> being one of the most promising &#8211; I love the idea!)</p>
<blockquote><p>I&#8217;ve been interested in website conversion optimization tools since about 2006 &#8211; why has this area stayed so stagnant in the last 5 years? Are the tools too hard to use? Or is it that web content creators just can&#8217;t fit these tools into their process? Having worked in an agency, I can certainly vouch for the latter&#8230;</p></blockquote>
<p>Of course there are a few high-end systems like Omniture (now Adobe) Test &amp; Target. I haven&#8217;t personally used it &#8212; it&#8217;s a bit too rich for my blood, but I know there are bigger companies who use it to do very neat things. What about the smaller guys? Where are the Lean Startup style testing tools that allow you to rapidly roll out iterative changes to slowly but steadily improve your product? What are the 100,000+ Magento installation owners using?</p>
<p>Bottom line is, none of these tools work particularly well for testing <em>features </em>anyways. GWO can&#8217;t do it all &#8211; what if you wanted to try out a different product comparison tool on your Magento e-commerce store? Good luck. I haven&#8217;t ever tried to implement something like this with Optimizely, and maybe you can, but in the rest of this post, I&#8217;ll share the home-grown solution I use to roll-out changes to LiveOutThere.com and measure the results.</p>
<h2>Start testing features in 5 minutes with this simple jQuery-based tool</h2>
<p>Go and grab <a href="https://raw.github.com/gist/1559006/8246dca2050490b1126957d13810a33f6dd6d100/DG_tester">this gist</a> from Github and include it on your website. It includes jQuery at the top so it works out of the box, but you&#8217;ll probably want to remove that. You&#8217;ll also need a testing &#8220;endpoint&#8221;, which you&#8217;ll set up on line 46, but don&#8217;t worry about that for now.</p>
<p>The gist contains a Javascript object called DG_tester that allows you to set up tests very quickly, just like this:</p>
<div id="gist-1559006" class="gist">

        <div class="gist-file">
          <div class="gist-data gist-syntax">
              <div class="highlight"><pre><div class='line' id='LC1'>&lt;script type=&quot;text/javascript&quot;&gt;</div><div class='line' id='LC2'>jQuery(document).ready(function() {</div><div class='line' id='LC3'>	DG_tester.test(</div><div class='line' id='LC4'>		&#39;red_or_green&#39;,</div><div class='line' id='LC5'>		function() {</div><div class='line' id='LC6'>			jQuery(&#39;#color&#39;).css(&#39;background-color&#39;,&#39;green&#39;);</div><div class='line' id='LC7'>		},</div><div class='line' id='LC8'>		function() {</div><div class='line' id='LC9'>			jQuery(&#39;#color&#39;).css(&#39;background-color&#39;,&#39;red&#39;);</div><div class='line' id='LC10'>		}</div><div class='line' id='LC11'>	);	</div><div class='line' id='LC12'>});</div><div class='line' id='LC13'>&lt;/script&gt;</div><div class='line' id='LC14'><br/></div><div class='line' id='LC15'>&lt;div id=&quot;color&quot;&gt;The background of this div will change color.&lt;/div&gt;</div></pre></div>
          </div>

          <div class="gist-meta">
            <a href="https://gist.github.com/raw/1559006/b6b2219d40e88d276aae47de55379be0e96dae3c/Example" style="float:right;">view raw</a>
            <a href="https://gist.github.com/1559006#file_example" style="float:right;margin-right:10px;color:#666">Example</a>
            <a href="https://gist.github.com/1559006">This Gist</a> brought to you by <a href="http://github.com">GitHub</a>.
          </div>
        </div>
</div>

<p>DG_tester.test is a function that accepts a minimum of three arguments. The first is a string: the name of your test, and the rest are functions. The functions are passed as arguments so DG_tester can call them later &#8211; these functions are the different options you want to try on the page. For instance, swapping copy, swapping colours, or adding or removing entire chunks of functionality from your website.</p>
<p>When your visitor arrives, DG_tester will randomly pick one of your functions &#8211; let&#8217;s call them &#8220;tries&#8221;. Each function is assigned a letter of the alphabet by our script, so the first function following the test name is try A, the second try B, and so on. This choice will persist with the visitor for 7 days; <strong>when they do something you want, you score the try.</strong></p>
<p>When you load the <a href="http://drewgillson.com/blog/wp-content/uploads/2011/12/dg_tester_example.htm#" target="_blank">complete example</a> you will now see a div that will be either red or green.</p>
<blockquote><p>If you keep refreshing, you&#8217;ll see the same colour. Click the &#8216;Delete cookie&#8217; link, try a few more refreshes, and you&#8217;ll get the other colour.</p></blockquote>
<p>Every time a try is shown to a visitor, this little script calls your endpoint to tell it that a try has happened. When your visitor does the thing you want (like adds a product to cart, purchases something, clicks the &#8216;Score&#8217; link in the <a href="http://drewgillson.com/blog/wp-content/uploads/2011/12/dg_tester_example.htm#" target="_blank">example</a>, or simply continues to the next page) you would call DG_tester.score, like this:</p>
<div id="gist-1559006" class="gist">

        <div class="gist-file">
          <div class="gist-data gist-syntax">
              <div class="highlight"><pre><div class='line' id='LC1'>jQuery(&#39;#add-to-cart&#39;).click(function() {</div><div class='line' id='LC2'>	DG_tester.score(&#39;red_or_green&#39;);</div><div class='line' id='LC3'>});</div></pre></div>
          </div>

          <div class="gist-meta">
            <a href="https://gist.github.com/raw/1559006/862078196ff67e5db4c505bea3ede38fb1a51692/Score" style="float:right;">view raw</a>
            <a href="https://gist.github.com/1559006#file_score" style="float:right;margin-right:10px;color:#666">Score</a>
            <a href="https://gist.github.com/1559006">This Gist</a> brought to you by <a href="http://github.com">GitHub</a>.
          </div>
        </div>
</div>

<p>If you are monitoring your network traffic in the browser console, or if you look at the generated source of the example page, you&#8217;ll see that two images have now been created. Of course, both of these resolve to 404 pages, since you don&#8217;t have an endpoint yet.</p>
<p>The purpose of the endpoint is to record scores and tries. This is really pretty simple &#8211; don&#8217;t let anyone else tell you otherwise. Cavemen did it by recording scratch marks onto clay tablets. We use a really fast Node.js backend that uses MongoDB to record data, but you don&#8217;t have to do anything like that. A simple PHP script that grabs the testId out of the query string and writes it to a MySQL table will work just as well. Or you could append to CSV files and interpret the results later with Excel.</p>
<p>At any rate, when you&#8217;ve got a backend rigged up and recording data, let your tests run for a while, divide scores by tries for each try, and pick the try that worked best! My rule of thumb is that you should have about 400 tries before you consider anything statistically significant.</p>
<p>Of course, none of this is probably particularly statistically significant anyways &#8211; but that&#8217;s not the point! You need to be testing. Test, test, test, you&#8217;ll get the hang of it. Then tell me what you&#8217;re testing, because nothing I&#8217;m testing this week seems to make a damn bit of difference. And that&#8217;s a blog post for another day: what kind of tests are likely to generate the most informative results and guide your path? Why, when I test, do most of my tests seem inconclusive at best? That&#8217;s perhaps the real reason why a lot of these testing tools have never taken off&#8230;</p>
<p><a href="http://www.drewgillson.com/blog/wp-content/uploads/2011/12/signature.jpg"><img title="Drew Gillson" src="http://www.drewgillson.com/blog/wp-content/uploads/2011/12/signature.jpg" alt="" width="86" height="100" /></a></p>
]]></content:encoded>
			<wfw:commentRss>http://www.drewgillson.com/blog/great-website-conversion-optimization-tools/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>How to apply Magento coupon codes automatically</title>
		<link>http://www.drewgillson.com/blog/how-to-apply-magento-coupon-codes-automatically/</link>
		<comments>http://www.drewgillson.com/blog/how-to-apply-magento-coupon-codes-automatically/#comments</comments>
		<pubDate>Sat, 31 Dec 2011 06:01:00 +0000</pubDate>
		<dc:creator>Drew</dc:creator>
				<category><![CDATA[Magento]]></category>

		<guid isPermaLink="false">http://www.drewgillson.com/blog/?p=70</guid>
		<description><![CDATA[The Magento price rule system is very complex, and like most things Magento, it can do basically whatever you want it to do once you understand how &#8212; but until then, it&#8217;s just frustrating. One of the things that it]]></description>
			<content:encoded><![CDATA[<p>The Magento price rule system is very complex, and like most things Magento, it can do basically whatever you want it to do once you understand how &#8212; but until then, it&#8217;s just frustrating. <span id="more-70"></span>One of the things that it <em>can&#8217;t</em> do is apply coupon codes automatically. There are many cases where you might want to do this, the simplest being setting up special pricing for a specific traffic channel.</p>
<p>For instance, if you send feeds to shopping price comparison networks, use this little trick to set up a hidden 5% price difference and you can easily position your products ahead of the competition while staying on-price. At first consideration, it would seem that you should be able to set this up using a catalog price rule rather than a shopping cart price rule, but unfortunately (as far as I&#8217;ve been able to tell), you can&#8217;t, at least for not-logged-in visitors.</p>
<p>To get Magento to apply coupon codes automatically, you will need to create a controller that looks for a coupon code in your query string, then forwards the visitor on to the requested product or category page. The URL to make this happen will end up looking like this:</p>
<blockquote><p>http://www.liveoutthere.com/applycoupon/index/?url=buy/the_north_face.html&amp;coupon_code=TNF10</p></blockquote>
<h2>Shouldn&#8217;t this be done with an event observer?</h2>
<p>No. Personally, I see two good reasons for using a controller and redirecting, rather than just appending a query string variable to the destination page URL and catching it with an event observer: future-proofing, and cache-breaking. On the topic of future-proofing, a controller will allow you to hack in additional functionality more easily when you want to set up complicated discount strategies, it will allow you to do things like check for stock prior to delivering someone to an out-of-stock product page; and in my opinion it is easier to understand than diving right into event observers (although you&#8217;ll need one of those too &#8211; give me one more paragraph).</p>
<p>As far as cache-breaking, if you&#8217;re using a full-page cache like TinyBrick&#8217;s LightSpeed, doing things this way will make life easier for you when you have to figure out how to serve special content for discounted products and categories &#8211; because the LightSpeed logic happens before everything else in the Magento system, it is *way* easier to deal with these special cases prior to page load than trying to use event observers and complicated cache hole punches. Trust me on this.</p>
<p>Now, before getting to the meat of this how-to, there are many great <a href="http://www.aschroder.com/2010/01/magento-events-explained-and-a-few-gotchas-avoided/" target="_blank">Magento event observer tutorials</a>, and I suggest you spend some time familiarizing yourself with event observers before proceeding if you haven&#8217;t already. They are certainly one of the most powerful aspects of Magento.</p>
<h2>Creating a Magento extension to apply coupon codes automatically</h2>
<p><a href="http://www.drewgillson.com/blog/wp-content/uploads/2011/12/Screen-Shot-2011-12-30-at-8.59.33-PM.jpg"><img class="alignleft size-full wp-image-71" title="Directory listing" src="http://www.drewgillson.com/blog/wp-content/uploads/2011/12/Screen-Shot-2011-12-30-at-8.59.33-PM.jpg" alt="" width="188" height="112" /></a>The first thing you need is an extension in your /app/code/local directory &#8211; I&#8217;ve named mine ApplyCoupon. The purpose of this post is not to explain Magento extension development, so I&#8217;m just going to give you a few GitHub gists and a screenshot and leave it at that.</p>
<p>First, you need to save the coupon code in session. Because of the way Magento works, we can&#8217;t actually apply a discount to anything unless we have a quote, and we can&#8217;t have a quote until we have a product in the cart&#8230; so this is a bit of a catch-22, and we will need an event observer to resolve it.</p>
<p>Before you worry about that, the class in your IndexController.php needs to contain this method:</p>
<div id="gist-1542684" class="gist">

        <div class="gist-file">
          <div class="gist-data gist-syntax">
              <div class="highlight"><pre><div class='line' id='LC1'><span class="x">public function indexAction() {    </span></div><div class='line' id='LC2'><span class="x">    $coupon_code = $this-&gt;getRequest()-&gt;getParam(&#39;coupon_code&#39;);</span></div><div class='line' id='LC3'><span class="x">    </span></div><div class='line' id='LC4'><span class="x">    if ($coupon_code != &#39;&#39;) {</span></div><div class='line' id='LC5'><span class="x">        Mage::getSingleton(&quot;checkout/session&quot;)-&gt;setData(&quot;coupon_code&quot;,$coupon_code);</span></div><div class='line' id='LC6'><span class="x">        Mage::getSingleton(&#39;checkout/cart&#39;)-&gt;getQuote()-&gt;setCouponCode($coupon_code)-&gt;save();</span></div><div class='line' id='LC7'><span class="x">	Mage::getSingleton(&#39;core/session&#39;)-&gt;addSuccess($this-&gt;__(&#39;Coupon was automatically applied&#39;));</span></div><div class='line' id='LC8'><span class="x">    }</span></div><div class='line' id='LC9'><span class="x">    else {</span></div><div class='line' id='LC10'><span class="x">        Mage::getSingleton(&quot;checkout/session&quot;)-&gt;setData(&quot;coupon_code&quot;,&quot;&quot;);</span></div><div class='line' id='LC11'><span class="x">	$cart = Mage::getSingleton(&#39;checkout/cart&#39;);</span></div><div class='line' id='LC12'><span class="x">	foreach( Mage::getSingleton(&#39;checkout/session&#39;)-&gt;getQuote()-&gt;getItemsCollection() as $item ) {</span></div><div class='line' id='LC13'><span class="x">	    $cart-&gt;removeItem( $item-&gt;getId() );</span></div><div class='line' id='LC14'><span class="x">	}</span></div><div class='line' id='LC15'><span class="x">	$cart-&gt;save();</span></div><div class='line' id='LC16'><span class="x">    }</span></div><div class='line' id='LC17'><br/></div><div class='line' id='LC18'><span class="x">    if ($this-&gt;getRequest()-&gt;getParam(&#39;url&#39;)) {</span></div><div class='line' id='LC19'><span class="x">	//using raw header instead of _redirect because _redirect appends a /</span></div><div class='line' id='LC20'><span class="x">	header(&#39;HTTP/1.1 301 Moved Permanently&#39;);</span></div><div class='line' id='LC21'><span class="x">	$gclid = this-&gt;getRequest()-&gt;getParam(&#39;gclid&#39;);</span></div><div class='line' id='LC22'><span class="x">	$url = $this-&gt;getRequest()-&gt;getParam(&#39;url&#39;);</span></div><div class='line' id='LC23'><span class="x">        header(&#39;Location: /&#39; . $url . &#39;?gclid=&#39; . $gclid);</span></div><div class='line' id='LC24'><span class="x">	die();</span></div><div class='line' id='LC25'><span class="x">    } else {</span></div><div class='line' id='LC26'><span class="x">        $this-&gt;_redirect(&quot;/&quot;);</span></div><div class='line' id='LC27'><span class="x">    }</span></div><div class='line' id='LC28'><span class="x">}</span></div></pre></div>
          </div>

          <div class="gist-meta">
            <a href="https://gist.github.com/raw/1542684/f46296c6def5775a5c040d2f382faeb477738570/applyCouponCodeAction.php" style="float:right;">view raw</a>
            <a href="https://gist.github.com/1542684#file_apply_coupon_code_action.php" style="float:right;margin-right:10px;color:#666">applyCouponCodeAction.php</a>
            <a href="https://gist.github.com/1542684">This Gist</a> brought to you by <a href="http://github.com">GitHub</a>.
          </div>
        </div>
</div>

<p>Note the gclid query string parameter we&#8217;re appending to the URL on line 21 &#8211; this is really important. If you don&#8217;t append it, you will lose your auto-tagged campaign URLs in Google Analytics. Learn from me and don&#8217;t make that mistake!</p>
<p>Now, you need an event observer attached to <em>checkout_cart_product_add_after</em> that fires when your shopper adds something to their cart and applies the discount. The class in your Observer.php file will contain one method, applyCouponEvent, to grab the coupon code out of session and apply it to the current quote:</p>
<div id="gist-1542838" class="gist">

        <div class="gist-file">
          <div class="gist-data gist-syntax">
              <div class="highlight"><pre><div class='line' id='LC1'><span class="x">public function applyCouponEvent($observer){</span></div><div class='line' id='LC2'><span class="x">    $coupon_code = trim(Mage::getSingleton(&quot;checkout/session&quot;)-&gt;getData(&quot;coupon_code&quot;));</span></div><div class='line' id='LC3'><span class="x">    if ($coupon_code != &#39;&#39;){</span></div><div class='line' id='LC4'><span class="x">        Mage::getSingleton(&#39;checkout/cart&#39;)-&gt;getQuote()-&gt;setCouponCode($coupon_code)-&gt;save();</span></div><div class='line' id='LC5'><span class="x">    }</span></div><div class='line' id='LC6'><span class="x">}</span></div></pre></div>
          </div>

          <div class="gist-meta">
            <a href="https://gist.github.com/raw/1542838/3ef8952a4ffc9a582ebf73521eec14f3455bb778/applyCouponEvent.php" style="float:right;">view raw</a>
            <a href="https://gist.github.com/1542838#file_apply_coupon_event.php" style="float:right;margin-right:10px;color:#666">applyCouponEvent.php</a>
            <a href="https://gist.github.com/1542838">This Gist</a> brought to you by <a href="http://github.com">GitHub</a>.
          </div>
        </div>
</div>

<p>And here are the bits you&#8217;ll need in your config.xml:</p>
<div id="gist-1542866" class="gist">

        <div class="gist-file">
          <div class="gist-data gist-syntax">
              <div class="highlight"><pre><div class='line' id='LC1'><span class="nt">&lt;frontend&gt;</span></div><div class='line' id='LC2'>	<span class="nt">&lt;events&gt;</span></div><div class='line' id='LC3'>		<span class="nt">&lt;checkout_cart_product_add_after&gt;</span>  </div><div class='line' id='LC4'>		<span class="nt">&lt;observers&gt;</span>  </div><div class='line' id='LC5'>			<span class="nt">&lt;ApplyCoupon_apply&gt;</span></div><div class='line' id='LC6'>				<span class="nt">&lt;type&gt;</span>singleton<span class="nt">&lt;/type&gt;</span>  </div><div class='line' id='LC7'>				<span class="nt">&lt;class&gt;</span>ApplyCoupon/Observer<span class="nt">&lt;/class&gt;</span>    </div><div class='line' id='LC8'>				<span class="nt">&lt;method&gt;</span>applyCouponEvent<span class="nt">&lt;/method&gt;</span>            </div><div class='line' id='LC9'>			<span class="nt">&lt;/ApplyCoupon_apply&gt;</span>              </div><div class='line' id='LC10'>		<span class="nt">&lt;/observers&gt;</span>  </div><div class='line' id='LC11'>		<span class="nt">&lt;/checkout_cart_product_add_after&gt;</span></div><div class='line' id='LC12'>	<span class="nt">&lt;/events&gt;</span></div><div class='line' id='LC13'>	<span class="nt">&lt;routers&gt;</span></div><div class='line' id='LC14'>		<span class="nt">&lt;applycoupon&gt;</span></div><div class='line' id='LC15'>			<span class="nt">&lt;use&gt;</span>standard<span class="nt">&lt;/use&gt;</span></div><div class='line' id='LC16'>			<span class="nt">&lt;args&gt;</span></div><div class='line' id='LC17'>				<span class="nt">&lt;module&gt;</span>You_ApplyCoupon<span class="nt">&lt;/module&gt;</span></div><div class='line' id='LC18'>				<span class="nt">&lt;frontName&gt;</span>applycoupon<span class="nt">&lt;/frontName&gt;</span></div><div class='line' id='LC19'>			<span class="nt">&lt;/args&gt;</span></div><div class='line' id='LC20'>		<span class="nt">&lt;/applycoupon&gt;</span></div><div class='line' id='LC21'>	<span class="nt">&lt;/routers&gt;</span></div><div class='line' id='LC22'><span class="nt">&lt;/frontend&gt;</span></div></pre></div>
          </div>

          <div class="gist-meta">
            <a href="https://gist.github.com/raw/1542866/077c350fcc981bea864f98b4892361e8b3688a87/config.xml" style="float:right;">view raw</a>
            <a href="https://gist.github.com/1542866#file_config.xml" style="float:right;margin-right:10px;color:#666">config.xml</a>
            <a href="https://gist.github.com/1542866">This Gist</a> brought to you by <a href="http://github.com">GitHub</a>.
          </div>
        </div>
</div>

<p>At this point this will all actually work &#8211; you will be able to load your controller action, add something to your cart, and see the discount applied. Unfortunately, like most things Magento, this won&#8217;t cut it. Your visitors still have no indication that there has been a discount applied until they add a product to cart.</p>
<h2>Just a few more steps&#8230;</h2>
<p>You can add this code to your template/catalog/product/view.phtml and list.phtml template files to get the <strong>discounted price as if the discount code had already been applied</strong>:</p>
<div id="gist-1542893" class="gist">

        <div class="gist-file">
          <div class="gist-data gist-syntax">
              <div class="highlight"><pre><div class='line' id='LC1'>$productPrice = $this-&gt;getPriceHtml( $_product, true );</div><div class='line' id='LC2'>$coupon_code = trim(Mage::getSingleton(&quot;checkout/session&quot;)-&gt;getData(&quot;coupon_code&quot;));</div><div class='line' id='LC3'><br/></div><div class='line' id='LC4'>if ($coupon_code != &#39;&#39;) {</div><div class='line' id='LC5'>	$db = Mage::getSingleton(&#39;core/resource&#39;)-&gt;getConnection(&#39;core_read&#39;);</div><div class='line' id='LC6'>	$sql = &quot;SELECT discount_amount, conditions_serialized</div><div class='line' id='LC7'>		FROM salesrule_coupon AS a INNER JOIN salesrule AS b ON a.rule_id = b.rule_id</div><div class='line' id='LC8'>		WHERE simple_action = &#39;by_percent&#39; AND a.code = ?&quot;;</div><div class='line' id='LC9'>	$sql = $db-&gt;quoteInto($sql, $coupon_code);</div><div class='line' id='LC10'>	$rows = $db-&gt;fetchAll($sql);</div><div class='line' id='LC11'>	if (isset($rows[0])) {</div><div class='line' id='LC12'>		$discount = ($rows[0][&#39;discount_amount&#39;] / 100);</div><div class='line' id='LC13'><br/></div><div class='line' id='LC14'>		/*** This assumes that if there is a shopping cart price rule condition,</div><div class='line' id='LC15'>		  *  it is a condition with only one parameter - that the product belongs</div><div class='line' id='LC16'>		  *  to a particular attribute set.</div><div class='line' id='LC17'>		***/</div><div class='line' id='LC18'>		$conditions = unserialize($rows[0][&#39;conditions_serialized&#39;]);</div><div class='line' id='LC19'>		$conditions = $conditions[&#39;conditions&#39;][0][&#39;conditions&#39;][0];</div><div class='line' id='LC20'>		if (is_array($conditions) <span class="err">&amp;&amp;</span> $conditions[&#39;value&#39;] == $_product-&gt;getAttributeSetId()) {</div><div class='line' id='LC21'>			// someone want to write me a regex for this nasty piece of work?</div><div class='line' id='LC22'>			$discountedPrice = str_replace(&#39; &#39;,&#39;&#39;,strip_tags($productPrice));</div><div class='line' id='LC23'>			$discountedPrice = str_replace(&quot;\n&quot;,&#39;&#39;,str_replace(&#39;$&#39;,&#39;&#39;, $discountedPrice));</div><div class='line' id='LC24'>			$discountedPrice = round($discountedPrice - ($discountedPrice * $discount),2);</div><div class='line' id='LC25'>			$productPrice = &quot;<span class="nt">&lt;div</span> <span class="na">class=</span><span class="s">&#39;price-box&#39;</span><span class="nt">&gt;&lt;span</span> <span class="na">class=</span><span class="s">&#39;regular-price&#39;</span><span class="nt">&gt;</span></div><div class='line' id='LC26'>					 <span class="nt">&lt;span</span> <span class="na">class=</span><span class="s">&#39;price&#39;</span><span class="nt">&gt;</span>$&quot; . $discountedPrice . &quot;<span class="nt">&lt;/span&gt;</span></div><div class='line' id='LC27'>					 <span class="nt">&lt;/span&gt;&lt;/div&gt;</span>&quot;;</div><div class='line' id='LC28'>			$sale_price = true;</div><div class='line' id='LC29'>		}</div><div class='line' id='LC30'>	}</div><div class='line' id='LC31'>}</div></pre></div>
          </div>

          <div class="gist-meta">
            <a href="https://gist.github.com/raw/1542893/d00b5b065c5eae3c422dadaeed0eb7b828926cfd/product.phtml" style="float:right;">view raw</a>
            <a href="https://gist.github.com/1542893#file_product.phtml" style="float:right;margin-right:10px;color:#666">product.phtml</a>
            <a href="https://gist.github.com/1542893">This Gist</a> brought to you by <a href="http://github.com">GitHub</a>.
          </div>
        </div>
</div>

<p>Use the $sale_price flag to turn on sale badges, red-tag creative, etc., and echo $productPrice where you would normally. There are probably a few purists out there who would object to this approach, and I&#8217;d love to hear from you if you can think of a better way to do this. This was the best I could come up with.</p>
<p>The condition check on line 15 will allow you to display the discounted price, only if the product in question belongs to the attribute set specified by the condition in the shopping cart price rule &#8212; even though the coupon code will not apply if you have these conditions set up, you still need the template to be aware of the conditions so that you display the correct pricing. That&#8217;s a mouthful!</p>
<blockquote><p>In LiveOutThere.com&#8217;s case, we add all of our The North Face products to their own attribute set, so we can show 10% discounts on just The North Face products when you come through our paid search campaign.</p></blockquote>
<p>If you are using a full-page cache, you will also need to create a hole punch for the value of $productPrice, but that&#8217;s a topic for another post.</p>
<h2>That&#8217;s it from me. Questions?</h2>
<p>If there is enough interest, I would be happy to write another post about how to approach even more complex shopping cart price rules that involve giveaways and gift cards &#8211; a great example of something we&#8217;ve done at LiveOutThere.com is run a promotion where we gave our shoppers a free $75 gift card (we use Unirgy&#8217;s <a href="http://www.magentocommerce.com/magento-connect/unirgy-giftcert.html" target="_blank">uGiftCert</a>) if they spent $250 or more. This was a big hit with our customers and is a great promotional strategy that you should be using too.</p>
<p><a href="http://www.drewgillson.com/blog/wp-content/uploads/2011/12/signature.jpg"><img title="Drew Gillson" src="http://www.drewgillson.com/blog/wp-content/uploads/2011/12/signature.jpg" alt="" width="86" height="100" /></a></p>
]]></content:encoded>
			<wfw:commentRss>http://www.drewgillson.com/blog/how-to-apply-magento-coupon-codes-automatically/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Putting Magento to the test on Boxing Day</title>
		<link>http://www.drewgillson.com/blog/putting-magento-to-the-test-on-boxing-day/</link>
		<comments>http://www.drewgillson.com/blog/putting-magento-to-the-test-on-boxing-day/#comments</comments>
		<pubDate>Thu, 29 Dec 2011 23:31:41 +0000</pubDate>
		<dc:creator>Drew</dc:creator>
				<category><![CDATA[Magento]]></category>

		<guid isPermaLink="false">http://www.drewgillson.com/blog/?p=37</guid>
		<description><![CDATA[In this post I&#8217;ll write about Magento performance optimization, human solutions to technical problems, and the real-world experience of running an e-commerce business with a tiny, agile team.On Boxing Day we had a sale event at LiveOutThere.com. We placed some]]></description>
			<content:encoded><![CDATA[<p>In this post I&#8217;ll write about Magento performance optimization, human solutions to technical problems, and the real-world experience of running an e-commerce business with a tiny, agile team.<span id="more-37"></span>On Boxing Day we had a sale event at LiveOutThere.com. We placed some great items from this season on sale for 30% to 50% off, and using an additional discount code, our customers could receive up to 55% off if they had been paying attention to our email deployments in the days before. These are great prices and it is no wonder we attracted a little more attention than we bargained for:</p>
<div class="mceTemp">
<dl class="wp-caption alignright" style="width: 310px;">
<dt class="wp-caption-dt"><a href="http://www.drewgillson.com/blog/wp-content/uploads/2011/12/Screen-Shot-2011-12-29-at-2.06.43-PM.jpg"><img class="size-medium wp-image-38 " title="Google Analytics visits" src="http://www.drewgillson.com/blog/wp-content/uploads/2011/12/Screen-Shot-2011-12-29-at-2.06.43-PM-300x120.jpg" alt="" width="300" height="120" /><br />
<span class="wp-caption-dd">Visits to LiveOutThere.com in December</span><br />
</a></dt>
</dl>
</div>
<p>In order to give our customers on the west coast a chance to buy product before everyone on the east coast snapped it up, we decided to put up a holding page and close the doors until 11am MST. I placed our Google Analytics tag on the holding page and watched eagerly as hundreds of concurrent visitors piled in.</p>
<h2>11am &#8211; the sale is on!</h2>
<p>When 11am came, I removed the RewriteRule that forced all of our inbound visitors to the holding page, and boom &#8211; the website instantly choked. The disappointment my partners and I felt as we imagined the frustration these hundreds of eager shoppers must have experienced hung in the air. This was not supposed to happen.</p>
<p>We have two servers &#8211; one front-end web / cache server, and one dedicated database server. We do not run a dedicated reverse proxy like Squid or Varnish, and in this case, I don&#8217;t think it would have made a difference. We already use Amazon CloudFront to host our media assets and CSS. Our CSS and JavaScript are minified, and we try to follow all of the YUI guidelines. It was safe to say that this was not a front-end problem. In anticipation of the traffic increase, I had recently <a title="Using Redis as Magento’s cache backend" href="http://www.drewgillson.com/blog/using-redis-as-magentos-cache-backend/">switched to Redis </a>for our Magento cache backend. We use <a title="Customize TinyBrick’s LightSpeed for use with Redis" href="http://www.drewgillson.com/blog/customize-tinybricks-lightspeed-for-use-with-redis/">Redis in combination with TinyBrick&#8217;s LightSpeed extension</a> to store the full HTML of our pages, which will theoretically reduce the database traffic to near zero if the cache is hot.</p>
<h2>11:15am &#8211; initial diagnosis and damage control</h2>
<p><div class="simplePullQuote"><br />
thread_cache_size = 16<br />
max_connections = 800<br />
query_cache_size = 512M<br />
sort_buffer_size = 4M<br />
join_buffer_size = 3M<br />
open_files_limit = 4096<br />
table_open_cache = 2048<br />
</div>Solving a problem like this on the fly is kind of like replacing a valve in a fully pressurized plumbing system with no master shut-off. Within a few minutes I had established that the issue was certainly heavy load on the database server, but unfortunately, could not do much about it. We had instantly reached max_connections and sent the processor utilization to 100%. With nothing else to do, I added a RewriteRule back to our .htaccess that shunted visitors back to a holding page. Jamie Clarke, LiveOutThere.com&#8217;s president, drafted a quick apology that we published to the holding page while I worked to understand and fix the issue.</p>
<blockquote><p>Communicate early and as often as possible.  Our Facebook page allowed us to continue a conversation with our waiting customers, and ultimately serve them one-on-one.</p></blockquote>
<p>Our dedicated MySQL server has 24GB of RAM and uses the key configuration values shown in the pull-quote above. This was based on the <a title="White Paper: Methods and Best Practices for High Performance eCommerce with Magento Enterprise" href="http://www.magentocommerce.com/knowledge-base/entry/white-paper-methods-and-best-practices-for-high-performance-ecommerce-with-" target="_blank">best practices found at magentocommerce.com</a>. This is certainly not a cheap piece of hardware, and even under heavy load it should have performed adequately &#8211; something wasn&#8217;t adding up. I tried restarting MySQL a few times. No dice. I would get 2 seconds of operation and would immediately be back in the 100% utilization range. I knew I had two different challenges &#8211; first, how to bring the site back up in some sort of minimal viable configuration, and second, what the hell was causing the utilization in the first place? Our Redis cache was hot, and the database server should have been under almost no load.</p>
<h2>11:30am &#8211; minimum viable fix</h2>
<p>I realized that the only option I had would be to use mod_rewrite to do traffic control &#8212; by letting some customers in, but not all of them, I could keep the load on the database server under control and allow at least some of our customers to make purchases. This posed a few problems &#8211; you can&#8217;t let someone in and then kick them back out again, so I knew I eventually had to solve the real issue, as I would eventually begin to swamp the database server anyways. My first instinct was to do this by IP subnets and I broke the fully-allowable range of IP addresses into a few separate chunks. This was an obvious technical solution, but within a few minutes I realized it was going to just make our customers even more mad. I needed a human solution.</p>
<blockquote><p>Learn from Disney. Make the waiting experience a shared, collective experience. Even if it&#8217;s frustrating, it&#8217;s memorable and it&#8217;s an opportunity to let your real strengths shine through &#8211; this is ultimately a story about great customer service, not database server performance.</p></blockquote>
<p>Solution #2 was to engage each customer one-on-one. I instructed Alex, our customer service lead, to collect IP addresses from our most vocal Facebook customers so I could let them in personally. This was a seat-of-the-pants decision that turned out to be a wise one. Where before I had hundreds of customers, none of whom seemed to be able to get into the store (and I guess that&#8217;s statistics for you), now I had hand-picked customers who seemed to be particularly patient, particularly frustrated, or particularly interested in communicating with us while we determined the problem.</p>
<blockquote><p>Allowing our most vocal Facebook fans in first quickly turned the tide of discussion on our Facebook page from frustration to hopeful anticipation.</p></blockquote>
<p>As we slowly began to process sales, a vibe developed on our Facebook page that was all about the exclusivity of access, the anticipation of being allowed into a great sale, and the shared experience of waiting. Customers posted IP address after IP address, and as Alex read them to me I retyped out RewriteCond directives to give each person access. When they received access, they would almost always make a positive comment about it on the wall.</p>
<h2>1:00pm &#8211; the true nature of the problem</h2>
<p>After an hour and a half of coordinating Facebook sales, I had a chance to think about what was going on with the database server &#8211; and had no choice &#8211; as our utilization was creeping higher because by that point I had about 150 people shopping. The obvious next step was to use MySQL&#8217;s SHOW PROCESSLIST command to see exactly what kind of queries were being requested in the first place. At first glance, this showed me a whole bunch of queries that were in SLEEP state. I assumed these queries were waiting for row locks, but some of the times were so high (in the 30+ second range), that I figured just killing them outright would be better than serving a page in 30+ seconds anyways.</p>
<blockquote><p>You can use pt-kill, a utility from <a href="http://www.percona.com/software/percona-toolkit/">Percona&#8217;s advanced MySQL toolkit</a>, to selectively kill queries that match your criteria.</p></blockquote>
<p>Using pt-kill, I set up a process that would scan for SLEEPing queries every 30 seconds and kill them &#8211; I figured this would mean better performance for the queries that <em>were getting served,</em> and at the very least would serve error pages to our customers quicker than letting them grow older watching a spinning cursor. This would also reduce our connections count significantly every 30 seconds, so we weren&#8217;t throwing max_connections exceeded errors. This provided another stop-gap measure that bought me another hour while I could diagnose the real problem.</p>
<h2>2:00pm &#8211; partial resolution and the opening of the floodgates</h2>
<p>After watching SHOW PROCESSLIST carefully I began to pick out particular queries that didn&#8217;t make a lot of sense to me; namely involving customer sessions. This was my first &#8220;aha!&#8221; moment all day.<div class="simplePullQuote"><br />
Why was Magento storing customer session data in the database? That wasn&#8217;t how it should have been configured!</div>We have our /var directory mounted in a tmpfs partition (a virtual disk stored in RAM), and session data should have been written to /var/session on the web server. Sure enough, I looked at our /app/etc/local.xml file and our session_save location directive was set to database (this is how our development environment is configured, so at some point, we must have accidentally copied a development local.xml to live but forgotten to reset this particular directive) I knew this simple change would drastically reduce the load on the database server, but I also knew that if I changed the session_save location, it would cause our in-progress sessions to be wiped out. This meant that customers who had things in their cart would lose them.</p>
<p>I had no choice &#8211; it was either switch the location sessions were being saved to, or eventually choke the MySQL server again. I made the change and refreshed Magento&#8217;s configuration cache.</p>
<p>Within a few minutes the utilization on the database server dropped to a more reasonable level. I began removing RewriteConds cautiously, monitoring MySQL as I saved each copy of .htaccess back up to our web server. Within 10 minutes, I had removed all of the rewrite conditions and every one of our customers was able to shop. Unfortunately as a result of changing the session_save location, we did have a number of angry customers who had items in their cart or had almost completed checkout, and we made every effort to make it right for each one of these customers on a case-by-case basis. In many cases, we captured the order details using our <a href="http://www.olark.com">Olark LiveChat</a> instance and simply placed the order on the customer&#8217;s behalf.</p>
<h2>3:00pm to 1:00am &#8211; seeing it through</h2>
<p>Now that customers had full access to the store, we began receiving orders one after the other. For the remainder of the day, we averaged about 40 orders an hour. At any given time, we had between 100 and 200 active shoppers, and dozens of people checking out simultaneously. We use <a title="Understand your visitors’ behaviour by plugging into the matrix" href="http://www.drewgillson.com/blog/understand-your-visitors-by-plugging-into-the-matrix/">Dg_Matrix</a> to watch shopper behaviour, and the screen was flying. Alex and I stayed on live chat until approximately 1:00am, and processed many one-on-one sales. At one point, Alex had 36 chats going at once. Somehow, she kept it together.</p>
<blockquote><p>At <a href="http://www.liveoutthere.com">LiveOutThere.com</a>, we attempt to offer a full-service approach comparable to an in-store experience over LiveChat. That means helping you determine exactly what <a href="http://www.liveoutthere.com/">winter jackets</a> you should compare, how they should fit, how much you should spend, and what other alternatives might be available to you.</p></blockquote>
<p>We finished the sale day between midnight and 1am, ringing up $100,000 in sales and hopefully delighting more customers than we disappointed. The customer service follow-up has been steady, and we have been quick to offer extensions of our sale prices for anyone who was unable to purchase in the initial few hours of the sale. In conclusion, this experience has certainly shown the way forward for future optimizations, illustrated a clear need to <a href="http://en.wikipedia.org/wiki/Lint_(software)">lint</a> our configuration files, and taught us valuable lessons about damage control and the power of open, transparent communication with customers.</p>
<p><a href="http://www.drewgillson.com/blog/wp-content/uploads/2011/12/signature.jpg"><img title="Drew Gillson" src="http://www.drewgillson.com/blog/wp-content/uploads/2011/12/signature.jpg" alt="" width="86" height="100" /></a></p>
]]></content:encoded>
			<wfw:commentRss>http://www.drewgillson.com/blog/putting-magento-to-the-test-on-boxing-day/feed/</wfw:commentRss>
		<slash:comments>4</slash:comments>
		</item>
		<item>
		<title>Customize TinyBrick&#8217;s LightSpeed for use with Redis</title>
		<link>http://www.drewgillson.com/blog/customize-tinybricks-lightspeed-for-use-with-redis/</link>
		<comments>http://www.drewgillson.com/blog/customize-tinybricks-lightspeed-for-use-with-redis/#comments</comments>
		<pubDate>Wed, 14 Dec 2011 00:41:19 +0000</pubDate>
		<dc:creator>Drew</dc:creator>
				<category><![CDATA[Magento]]></category>
		<category><![CDATA[Redis]]></category>
		<category><![CDATA[cache]]></category>
		<category><![CDATA[lightspeed]]></category>

		<guid isPermaLink="false">http://www.drewgillson.com/blog/?p=11</guid>
		<description><![CDATA[If you use TinyBrick&#8217;s Lightspeed with your Magento installation, and you have switched to Redis, you will need to make a few changes to LightSpeed to support using Redis as the cache store. You will need to modify LightSpeed to]]></description>
			<content:encoded><![CDATA[<p>If you use TinyBrick&#8217;s Lightspeed with your Magento installation, and you have switched to Redis, you will need to make a few changes to LightSpeed to support using Redis as the cache store. You will need to modify LightSpeed to support 1) writing data to Redis, and 2) reading data from Redis. <span id="more-11"></span>We&#8217;ll start with writing data, which is a bit more complicated, and then we&#8217;ll add the necessary changes to lightspeed.php to support reading data from Redis.</p>
<p>To write data to Redis you will need to add a new cache server model to your LightSpeed extension. Create the file /app/code/community/TinyBrick/Lightspeed/Model/Server/Redis.php with the contents of the following gist:</p>
<p style="padding-left: 30px;"><div id="gist-1518557" class="gist">

        <div class="gist-file">
          <div class="gist-data gist-syntax">
              <div class="highlight"><pre><div class='line' id='LC1'>&lt;?php</div><div class='line' id='LC2'>class TinyBrick_LightSpeed_Model_Server_Redis {</div><div class='line' id='LC3'>	protected $_server;</div><div class='line' id='LC4'>	protected $_enabled 		= false;</div><div class='line' id='LC5'><br/></div><div class='line' id='LC6'>	public function getServer() {</div><div class='line' id='LC7'>		if (!$this-&gt;_server) {</div><div class='line' id='LC8'>			require_once($_SERVER[&#39;DOCUMENT_ROOT&#39;] . &#39;/lib/Credis/Client.php&#39;);</div><div class='line' id='LC9'>			require_once($_SERVER[&#39;DOCUMENT_ROOT&#39;] . &#39;/lib/Zend/Cache/Backend.php&#39;);</div><div class='line' id='LC10'>			require_once($_SERVER[&#39;DOCUMENT_ROOT&#39;] . &#39;/lib/Zend/Cache/Backend/Interface.php&#39;);</div><div class='line' id='LC11'>			require_once($_SERVER[&#39;DOCUMENT_ROOT&#39;] . &#39;/lib/Zend/Cache/Backend/ExtendedInterface.php&#39;);</div><div class='line' id='LC12'>			require_once($_SERVER[&#39;DOCUMENT_ROOT&#39;] . &#39;/app/code/core/Zend/Cache/Backend/Redis.php&#39;);</div><div class='line' id='LC13'>			$options = array();</div><div class='line' id='LC14'>			$options[&#39;server&#39;] = (string)Mage::getConfig()-&gt;getNode(&#39;lightspeed/cache/backend_options/server&#39;);</div><div class='line' id='LC15'>			$options[&#39;port&#39;] = (int)Mage::getConfig()-&gt;getNode(&#39;lightspeed/cache/backend_options/port&#39;);</div><div class='line' id='LC16'>			$options[&#39;database&#39;] = (int)Mage::getConfig()-&gt;getNode(&#39;lightspeed/cache/backend_options/database&#39;);</div><div class='line' id='LC17'>			$options[&#39;timeout&#39;] = (int)Mage::getConfig()-&gt;getNode(&#39;lightspeed/cache/backend_options/timeout&#39;);</div><div class='line' id='LC18'>			$options[&#39;force_standalone&#39;] = (int)Mage::getConfig()-&gt;getNode(&#39;lightspeed/cache/backend_options/force_standalone&#39;);</div><div class='line' id='LC19'>			$options[&#39;automatic_cleaning_factor&#39;] = (int)Mage::getConfig()-&gt;getNode(&#39;lightspeed/cache/backend_options/automatic_cleaning_factor&#39;);</div><div class='line' id='LC20'>			$this-&gt;_enabled = true;</div><div class='line' id='LC21'>			$this-&gt;_server = new Zend_Cache_Backend_Redis($options);</div><div class='line' id='LC22'>		}</div><div class='line' id='LC23'>		return $this-&gt;_server;</div><div class='line' id='LC24'>	}</div><div class='line' id='LC25'><br/></div><div class='line' id='LC26'>	public function save($key, $data, $expires=0, array $tags=array()) {</div><div class='line' id='LC27'>		$server = $this-&gt;getServer();</div><div class='line' id='LC28'>		if ($server &amp;&amp; $this-&gt;_enabled) {</div><div class='line' id='LC29'>			$server-&gt;save(serialize($data), $key, $tags, $expires);</div><div class='line' id='LC30'>		}</div><div class='line' id='LC31'>	}</div><div class='line' id='LC32'><br/></div><div class='line' id='LC33'>	public function cleanByTag($tag) {</div><div class='line' id='LC34'>		if ($this-&gt;_server) {</div><div class='line' id='LC35'>			$this-&gt;_server-&gt;_removeByMatchingTags($tag);</div><div class='line' id='LC36'>		}</div><div class='line' id='LC37'>	}</div><div class='line' id='LC38'><br/></div><div class='line' id='LC39'>	public function clean($tags=array()) {</div><div class='line' id='LC40'>		if ($this-&gt;_server) {</div><div class='line' id='LC41'>			if (count($tags) &amp;&amp; !in_array(&#39;LIGHTSPEED&#39;, $tags)) {</div><div class='line' id='LC42'>				$this-&gt;_server-&gt;_removeByMatchingTags($tags);</div><div class='line' id='LC43'>			}</div><div class='line' id='LC44'>			else {</div><div class='line' id='LC45'>				$this-&gt;_server-&gt;clean(Zend_Cache::CLEANING_MODE_ALL, $tags);</div><div class='line' id='LC46'>			}</div><div class='line' id='LC47'>		}</div><div class='line' id='LC48'>	}</div><div class='line' id='LC49'>}</div></pre></div>
          </div>

          <div class="gist-meta">
            <a href="https://gist.github.com/raw/1518557/63d0c097cd106b921198d3233b913d6dc4e8c5ff/cache-server-model" style="float:right;">view raw</a>
            <a href="https://gist.github.com/1518557#file_cache_server_model" style="float:right;margin-right:10px;color:#666">cache-server-model</a>
            <a href="https://gist.github.com/1518557">This Gist</a> brought to you by <a href="http://github.com">GitHub</a>.
          </div>
        </div>
</div>
</p>
<p>Then, you need to modify the TinyBrick_LightSpeed_Model_Server::getServer function in /app/code/community/TinyBrick/Lightspeed/Model/Server.php so that it knows about the new Redis cache server model you just created:</p>
<p style="padding-left: 30px;"><div id="gist-1518560" class="gist">

        <div class="gist-file">
          <div class="gist-data gist-syntax">
              <div class="highlight"><pre><div class='line' id='LC1'>	public function getServer()</div><div class='line' id='LC2'>	{</div><div class='line' id='LC3'>		if(!$this-&gt;_server){</div><div class='line' id='LC4'>			$type = strtolower(Mage::getConfig()-&gt;getNode(&#39;lightspeed/cache/type&#39;));</div><div class='line' id='LC5'>			if ($type == &quot;memcached&quot;){</div><div class='line' id='LC6'>				$this-&gt;_server = Mage::getModel(&quot;lightspeed/server_memcache&quot;);</div><div class='line' id='LC7'>			}elseif ($type == &quot;redis&quot;){</div><div class='line' id='LC8'>				$this-&gt;_server = Mage::getModel(&quot;lightspeed/server_redis&quot;);</div><div class='line' id='LC9'>			}else{</div><div class='line' id='LC10'>				$this-&gt;_server = Mage::getModel(&quot;lightspeed/server_files&quot;);</div><div class='line' id='LC11'>			}</div><div class='line' id='LC12'>		}</div><div class='line' id='LC13'>		return $this-&gt;_server;</div><div class='line' id='LC14'>	}</div></pre></div>
          </div>

          <div class="gist-meta">
            <a href="https://gist.github.com/raw/1518560/5e30a002fcc3f55b31a21f2ddfb9429a1a1e1513/get-server" style="float:right;">view raw</a>
            <a href="https://gist.github.com/1518560#file_get_server" style="float:right;margin-right:10px;color:#666">get-server</a>
            <a href="https://gist.github.com/1518560">This Gist</a> brought to you by <a href="http://github.com">GitHub</a>.
          </div>
        </div>
</div>
</p>
<p>Now, you can modify the LightSpeed configuration section of your local.xml file to use Redis instead of Memcache. Mine looks like this (the following XML should be a child of your &lt;config&gt; tag of local.xml:</p>
<p style="padding-left: 30px;"><div id="gist-1518565" class="gist">

        <div class="gist-file">
          <div class="gist-data gist-syntax">
              <div class="highlight"><pre><div class='line' id='LC1'><span class="nt">&lt;lightspeed&gt;</span></div><div class='line' id='LC2'><br/></div><div class='line' id='LC3'>&nbsp;&nbsp;&nbsp;&nbsp;<span class="nt">&lt;global&gt;</span></div><div class='line' id='LC4'>&nbsp;&nbsp;&nbsp;&nbsp;REDACTED</div><div class='line' id='LC5'>&nbsp;&nbsp;&nbsp;&nbsp;<span class="nt">&lt;/global&gt;</span></div><div class='line' id='LC6'><br/></div><div class='line' id='LC7'>&nbsp;&nbsp;&nbsp;&nbsp;<span class="nt">&lt;session&gt;</span></div><div class='line' id='LC8'>&nbsp;&nbsp;&nbsp;&nbsp;REDACTED</div><div class='line' id='LC9'>&nbsp;&nbsp;&nbsp;&nbsp;<span class="nt">&lt;/session&gt;</span></div><div class='line' id='LC10'><br/></div><div class='line' id='LC11'>&nbsp;&nbsp;&nbsp;&nbsp;<span class="nt">&lt;cache&gt;</span></div><div class='line' id='LC12'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="nt">&lt;type&gt;</span>redis<span class="nt">&lt;/type&gt;</span></div><div class='line' id='LC13'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="nt">&lt;backend&gt;</span>Zend_Cache_Backend_Redis<span class="nt">&lt;/backend&gt;</span></div><div class='line' id='LC14'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="nt">&lt;backend_options&gt;</span></div><div class='line' id='LC15'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="nt">&lt;server&gt;</span>/usr/home/outthere/redis.sock<span class="nt">&lt;/server&gt;</span></div><div class='line' id='LC16'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="nt">&lt;port&gt;</span>6379<span class="nt">&lt;/port&gt;</span></div><div class='line' id='LC17'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="nt">&lt;database&gt;</span>3<span class="nt">&lt;/database&gt;</span></div><div class='line' id='LC18'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="nt">&lt;force_standalone&gt;</span>0<span class="nt">&lt;/force_standalone&gt;</span>  <span class="c">&lt;!-- 0 for phpredis, 1 for standalone PHP (slower) --&gt;</span></div><div class='line' id='LC19'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="nt">&lt;automatic_cleaning_factor&gt;</span>20000<span class="nt">&lt;/automatic_cleaning_factor&gt;</span> <span class="c">&lt;!-- optional, 20000 is the default, 0 disables auto clean --&gt;</span></div><div class='line' id='LC20'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="nt">&lt;/backend_options&gt;</span></div><div class='line' id='LC21'>&nbsp;&nbsp;&nbsp;&nbsp;<span class="nt">&lt;/cache&gt;</span></div><div class='line' id='LC22'><br/></div><div class='line' id='LC23'><span class="nt">&lt;/lightspeed&gt;</span></div></pre></div>
          </div>

          <div class="gist-meta">
            <a href="https://gist.github.com/raw/1518565/27ffe23d9d6bf714eda51901de75035a4e78c95d/local.xml" style="float:right;">view raw</a>
            <a href="https://gist.github.com/1518565#file_local.xml" style="float:right;margin-right:10px;color:#666">local.xml</a>
            <a href="https://gist.github.com/1518565">This Gist</a> brought to you by <a href="http://github.com">GitHub</a>.
          </div>
        </div>
</div>
</p>
<p>Good work. Now LightSpeed will save information into Redis instead of Memcache! You can verify this is occurring by opening the redis-cli command line utility and running the MONITOR command. You will see a lot of activity every time you request a page &#8211; pore through it and verify that LightSpeed is actually saving what you think it should be saving to cache &#8211; primarily, the HTML of your page!</p>
<p>Now, you just need to modify lightspeed.php to serve the information you&#8217;ve stored in Redis. This is a little easier. First, you need to add a case for Redis in the PageCache::loadConfiguration function of lightspeed.php:</p>
<p style="padding-left: 30px;"><div id="gist-1518544" class="gist">

        <div class="gist-file">
          <div class="gist-data gist-syntax">
              <div class="highlight"><pre><div class='line' id='LC1'>case &#39;redis&#39;:</div><div class='line' id='LC2'>try {</div><div class='line' id='LC3'>	require_once(&#39;lib/Credis/Client.php&#39;);</div><div class='line' id='LC4'>	require_once(&#39;lib/Zend/Cache/Backend.php&#39;);</div><div class='line' id='LC5'>	require_once(&#39;lib/Zend/Cache/Backend/Interface.php&#39;);</div><div class='line' id='LC6'>	require_once(&#39;lib/Zend/Cache/Backend/ExtendedInterface.php&#39;);</div><div class='line' id='LC7'>	require_once(&#39;app/code/core/Zend/Cache/Backend/Redis.php&#39;);</div><div class='line' id='LC8'>	self::$cacheEngine = &#39;redis&#39;;</div><div class='line' id='LC9'>	$options = array();</div><div class='line' id='LC10'>	foreach($child2-&gt;backend_options as $el){</div><div class='line' id='LC11'>		$options[&#39;server&#39;] = (string)$el-&gt;server;</div><div class='line' id='LC12'>		$options[&#39;port&#39;] = (int)$el-&gt;port;</div><div class='line' id='LC13'>		$options[&#39;database&#39;] = (int)$el-&gt;database;</div><div class='line' id='LC14'>		$options[&#39;timeout&#39;] = (int)$el-&gt;timeout;</div><div class='line' id='LC15'>		$options[&#39;force_standalone&#39;] = (int)$el-&gt;force_standalone;</div><div class='line' id='LC16'>		$options[&#39;automatic_cleaning_factor&#39;] = (int)$el-&gt;automatic_cleaning_factor;</div><div class='line' id='LC17'>	}</div><div class='line' id='LC18'>	self::$cacheData[&#39;server&#39;] = new Zend_Cache_Backend_Redis($options);</div><div class='line' id='LC19'>}</div><div class='line' id='LC20'>catch (Exception $e) {</div><div class='line' id='LC21'>	die(&#39;Redis can not be used as the backend for Lightspeed. Check local.xml for proper configuration.&#39;);</div><div class='line' id='LC22'>}</div><div class='line' id='LC23'>break;</div></pre></div>
          </div>

          <div class="gist-meta">
            <a href="https://gist.github.com/raw/1518544/ec46bf990966dad2b995c9fe8850f879c72dd3a1/loadConfiguration" style="float:right;">view raw</a>
            <a href="https://gist.github.com/1518544#file_load_configuration" style="float:right;margin-right:10px;color:#666">loadConfiguration</a>
            <a href="https://gist.github.com/1518544">This Gist</a> brought to you by <a href="http://github.com">GitHub</a>.
          </div>
        </div>
</div>
</p>
<p> Then, you&#8217;ll need to add a case for Redis in the PageCache::get function:</p>
<p style="padding-left: 30px;"><div id="gist-1518552" class="gist">

        <div class="gist-file">
          <div class="gist-data gist-syntax">
              <div class="highlight"><pre><div class='line' id='LC1'>case &#39;redis&#39;:</div><div class='line' id='LC2'>return unserialize(self::$cacheData[&#39;server&#39;]-&gt;load($key));</div><div class='line' id='LC3'>break;</div><div class='line' id='LC4'><br/></div></pre></div>
          </div>

          <div class="gist-meta">
            <a href="https://gist.github.com/raw/1518552/2faf95ccd809e801ec65ef6c336109cf9b8c19ab/get" style="float:right;">view raw</a>
            <a href="https://gist.github.com/1518552#file_get" style="float:right;margin-right:10px;color:#666">get</a>
            <a href="https://gist.github.com/1518552">This Gist</a> brought to you by <a href="http://github.com">GitHub</a>.
          </div>
        </div>
</div>
</p>
<p>There you go. That should be enough to get you up and running. LightSpeed is a great extension for Magento, but unfortunately since TinyBrick has been acquired their support has become non-existent. Hopefully this functionality can be incorporated into a future version of LightSpeed. Hello TinyBrick? Anyone there?</p>
<p><a href="http://www.drewgillson.com/blog/wp-content/uploads/2011/12/signature.jpg"><img title="Drew Gillson" src="http://www.drewgillson.com/blog/wp-content/uploads/2011/12/signature.jpg" alt="" width="86" height="100" /></a></p>
]]></content:encoded>
			<wfw:commentRss>http://www.drewgillson.com/blog/customize-tinybricks-lightspeed-for-use-with-redis/feed/</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
		<item>
		<title>LinkedIn Profile</title>
		<link>http://www.drewgillson.com/blog/about-me/</link>
		<comments>http://www.drewgillson.com/blog/about-me/#comments</comments>
		<pubDate>Mon, 12 Dec 2011 01:14:28 +0000</pubDate>
		<dc:creator>Drew</dc:creator>
				<category><![CDATA[Who Am I?]]></category>

		<guid isPermaLink="false">http://www.drewgillson.com/blog/?p=23</guid>
		<description><![CDATA[I&#8217;m an online business consultant with an original, expert perspective. Usually, I work with one or two clients at a time and I consider my projects long-term partnerships. I write code and business plans, and my current passion is LiveOutThere.com.]]></description>
			<content:encoded><![CDATA[<p>I&#8217;m an online business consultant with an original, expert perspective. Usually, I work with one or two clients at a time and I consider my projects long-term partnerships. I write code <em>and</em> business plans, and my current passion is LiveOutThere.com.<span id="more-23"></span></p>
<iframe style="border: none;" src="http://www.drewgillson.com/linkedin.php" width="1000px" height="800px"></iframe>
]]></content:encoded>
			<wfw:commentRss>http://www.drewgillson.com/blog/about-me/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Using Redis as Magento&#8217;s cache backend</title>
		<link>http://www.drewgillson.com/blog/using-redis-as-magentos-cache-backend/</link>
		<comments>http://www.drewgillson.com/blog/using-redis-as-magentos-cache-backend/#comments</comments>
		<pubDate>Mon, 12 Dec 2011 00:14:21 +0000</pubDate>
		<dc:creator>Drew</dc:creator>
				<category><![CDATA[Magento]]></category>
		<category><![CDATA[Redis]]></category>

		<guid isPermaLink="false">http://www.drewgillson.com/blog/?p=5</guid>
		<description><![CDATA[In preparation for the increased traffic we see at LiveOutThere.com during the retail shopping season, I did some research into alternative cache backends for Magento. Long story short, Memcache is no longer the best cache solution. We were doing all]]></description>
			<content:encoded><![CDATA[<p>In preparation for the increased traffic we see at LiveOutThere.com during the retail shopping season, I did some research into alternative cache backends for Magento. Long story short, Memcache is no longer the best cache solution. <span id="more-5"></span>We were doing all the recommended things, but I was still not getting the performance I wanted. We are now using Redis and you should be too!</p>
<p>Before I moved us over to Redis, we were running a single memcached instance on the same server as our web server, and we had mounted Magento&#8217;s SQLite &#8220;slow cache&#8221; in a tmpfs file system in RAM. We used the memcache 2.2.6 extension to talk to memcached (seems this is a point of confusion for some people, as there are two PHP extensions to talk to memcached). My main complaint was that I could not selectively refresh anything in the cache without bringing the whole store to a grinding halt. Memcache does not support cache tags natively, so without getting into the dirty details, what the Magento team has done is use their second &#8220;slow cache&#8221; to store the key names in Memcache for specific tags. This is clever, but not very efficient, and now that there are better caches that actually support cache tags natively, like Redis, it becomes obvious how much this was a square peg in a round hole solution.</p>
<p>Now, we are still running Redis on the same server (we don&#8217;t have a dedicated cache server), but I am seeing much better performance and most importantly, it is easier to manage. Redis has a command line interface that allows me to selectively remove an item from the cache, and if you&#8217;re using a full page cache extension like Lightspeed, this is a god-send.</p>
<p>For instance, if our designer wants to add a new banner to the home page, I can just do this to remove the home page cache key:</p>
<p style="padding-left: 30px;"><div id="gist-1518535" class="gist">

        <div class="gist-file">
          <div class="gist-data gist-syntax">
              <div class="highlight"><pre><div class='line' id='LC1'>DEL &quot;zc:d:www.liveoutthere.com/&quot;</div></pre></div>
          </div>

          <div class="gist-meta">
            <a href="https://gist.github.com/raw/1518535/ad97e7817cc726bc409c723be1845c1851efb72e/redis-cli-delete" style="float:right;">view raw</a>
            <a href="https://gist.github.com/1518535#file_redis_cli_delete" style="float:right;margin-right:10px;color:#666">redis-cli-delete</a>
            <a href="https://gist.github.com/1518535">This Gist</a> brought to you by <a href="http://github.com">GitHub</a>.
          </div>
        </div>
</div>
</p>
<p>This <em>was</em> possible with Memcache if you were willing to write your own custom cache interface, but I&#8217;ve never heard of anyone doing this with Magento out in the wild. That&#8217;s what the Refresh Cache buttons are for (right?) &#8211; yeah sure, go ahead and press it on your busy production website. I would see our MySQL usage peg the processor near 100% for 15 or 20 minutes while the cache was rebuilding, and the site would be so slow it was unusable. Theoretically, the Magento CMS is supposed to remove the cache key when you save your CMS page, but this doesn&#8217;t work when you have an additional cache system like TinyBrick&#8217;s LightSpeed in the mix.</p>
<p style="padding-left: 30px;">Using LightSpeed? You&#8217;ll need to do <a title="Customize TinyBrick’s Lightspeed for use with Redis" href="http://www.drewgillson.com/blog/customize-tinybricks-lightspeed-for-use-with-redis/">a few more things</a> to get Redis working.</p>
<p>So, how do you do this yourself? I followed the instructions at <a title="Flexiblog" href="http://blog.flexishore.com/2011/09/magento-use-redis-as-cache-backend/">Flexiblog</a> and they worked for me. Try it out and let me if it&#8217;s made managing your cache as easy for you as it has for me.</p>
<p><a href="http://www.drewgillson.com/blog/wp-content/uploads/2011/12/signature.jpg"><img title="Drew Gillson" src="http://www.drewgillson.com/blog/wp-content/uploads/2011/12/signature.jpg" alt="" width="86" height="100" /></a></p>
]]></content:encoded>
			<wfw:commentRss>http://www.drewgillson.com/blog/using-redis-as-magentos-cache-backend/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
	</channel>
</rss>

