<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/css" href="/stylesheets/rss.css"?>
<rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/">
  <channel>
    <title>Nick Sieger: RailsConf 2007: Chris Wanstrath: Kickin' Ass with Cache-fu</title>
    <link>http://blog.nicksieger.com/articles/2007/05/19/railsconf-2007-chris-wanstrath-kickin-ass-with-cache-fu</link>
    <language>en-us</language>
    <ttl>40</ttl>
    <description></description>
    <item>
      <title>"RailsConf 2007: Chris Wanstrath: Kickin' Ass with Cache-fu" by Chris</title>
      <description>&lt;p&gt;Awesome notes!  Great job, man&amp;#46;&lt;/p&gt;</description>
      <pubDate>Mon, 21 May 2007 22:30:36 +0000</pubDate>
      <guid isPermaLink="false">urn:uuid:d3904d11-c577-42d4-9fe5-7cd910e90efc</guid>
      <link>http://blog.nicksieger.com/articles/2007/05/19/railsconf-2007-chris-wanstrath-kickin-ass-with-cache-fu#comment-252</link>
    </item>
    <item>
      <title>"RailsConf 2007: Chris Wanstrath: Kickin' Ass with Cache-fu" by Ravenii</title>
      <description>&lt;p&gt;Thanks Nick, I tried to trackback but I may have failed! here is a part I wrote;
&amp;#8220;Once I found that Nick was blogging, and found that I can catch up with presentations, that I have missed!, I gave up even thinking about writing&amp;#46; He did write a lot of very good material, I spent a lot of time reading his blog already!&amp;#8221;&lt;/p&gt;</description>
      <pubDate>Sun, 20 May 2007 05:05:35 +0000</pubDate>
      <guid isPermaLink="false">urn:uuid:9f3b2ec5-684a-45cb-9c62-6b001b93ce96</guid>
      <link>http://blog.nicksieger.com/articles/2007/05/19/railsconf-2007-chris-wanstrath-kickin-ass-with-cache-fu#comment-249</link>
    </item>
    <item>
      <title>RailsConf 2007: Chris Wanstrath: Kickin' Ass with Cache-fu</title>
      <description>&lt;p&gt;Chris is here to talk about games, since he used to work for Gamespot&amp;#46;  He coded PHP, which is like training wheels without the bike&amp;#46;  He had to sit in a glass cube and help keep the site running during E3 last year&amp;#46;  There were 100 gajillion teenage boys during their lunch break hitting refresh, and it all blew up&amp;#46;  Couldn&amp;#8217;t even gzip the responses, because the servers heated up to much&amp;#46;  They served 50M pages in a day, without downtime&amp;#46;  They did it with Memcache&amp;#46;&lt;/p&gt;

&lt;p&gt;Memcache is a distributed hash &amp;#45;&amp;#45; multiple daemons running on different servers&amp;#46;  Developed by Livejournal for their infrastructure, you just put up the servers, and they just work&amp;#46;&lt;/p&gt;

&lt;p&gt;Should you use Memcache?  No&amp;#46;  &lt;a href="http://c2.com/xp/YouArentGonnaNeedIt.html"&gt;YAGNI&lt;/a&gt;, UYRDNI (unless you really do need it)&amp;#46;&lt;/p&gt;

&lt;h2&gt;Rails and Memcache&lt;/h2&gt;

&lt;p&gt;Fragments, Actions, Sessions, Objects, cache it all&amp;#46;  You can use:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;memcache-client&lt;/code&gt; (by Robot&amp;#45;coop guys/Eric Hodel)&amp;#46;  Marshal&amp;#46;unload is 40 times faster than Object&amp;#46;new/loading from the database&amp;#46;&lt;/li&gt;
&lt;li&gt;CachedModel &amp;#45;&amp;#45; integration with ActiveRecord&lt;/li&gt;
&lt;li&gt;Fragment Cache Store&lt;/li&gt;
&lt;li&gt;Memcache session store&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&amp;#46;&amp;#46;&amp;#46;or&amp;#46;&amp;#46;&amp;#46;&lt;/p&gt;

&lt;h2&gt;&lt;code&gt;cache_fu&lt;/code&gt;&lt;/h2&gt;

&lt;p&gt;Or, &lt;code&gt;acts_as_cached&lt;/code&gt;&amp;#46;  It knows about all the aforementioned objects, with a single YAML config file (&lt;code&gt;config/memcached.yml&lt;/code&gt;)&amp;#46;  Word to the wise:  don&amp;#8217;t use names in your server config file&amp;#46;  Use IPs, avoid BIND and connections to the servers with every connection&amp;#46;  Don&amp;#8217;t let DNS outages bring down your servers&amp;#46;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;get_cache&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;expire_cache&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is all you need &amp;#45;&amp;#45; if you&amp;#8217;re using &lt;code&gt;set_cache&lt;/code&gt;, you probably don&amp;#8217;t understand how the plugin works&amp;#46;  Expire cache on the &amp;#8220;after save&amp;#8221; hook, which allows you to cache ID misses as well&amp;#46;&lt;/p&gt;

&lt;div class="typocode"&gt;&lt;pre&gt;&lt;code class="typocode_ruby "&gt;&lt;span class="keyword"&gt;class &lt;/span&gt;&lt;span class="class"&gt;Presentation&lt;/span&gt; &lt;span class="punct"&gt;&amp;lt;&lt;/span&gt; &lt;span class="constant"&gt;ActiveRecord&lt;/span&gt;&lt;span class="punct"&gt;::&lt;/span&gt;&lt;span class="constant"&gt;Base&lt;/span&gt;
  &lt;span class="ident"&gt;acts_as_cached&lt;/span&gt;
  &lt;span class="ident"&gt;after_save&lt;/span&gt; &lt;span class="symbol"&gt;:expire_cache&lt;/span&gt;
&lt;span class="keyword"&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Example: only cache published items&lt;/p&gt;

&lt;div class="typocode"&gt;&lt;pre&gt;&lt;code class="typocode_ruby "&gt;&lt;span class="keyword"&gt;class &lt;/span&gt;&lt;span class="class"&gt;Presentation&lt;/span&gt; &lt;span class="punct"&gt;&amp;lt;&lt;/span&gt; &lt;span class="constant"&gt;ActiveRecord&lt;/span&gt;&lt;span class="punct"&gt;::&lt;/span&gt;&lt;span class="constant"&gt;Base&lt;/span&gt;
  &lt;span class="ident"&gt;acts_as_cached&lt;/span&gt; &lt;span class="symbol"&gt;:conditions&lt;/span&gt; &lt;span class="punct"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="punct"&gt;'&lt;/span&gt;&lt;span class="string"&gt;published = 1&lt;/span&gt;&lt;span class="punct"&gt;'&lt;/span&gt;
&lt;span class="keyword"&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Cached&amp;#45;scoped&amp;#45;finders (if somebody thinks of a good name, let Chris know)&amp;#46;  The idea is to move custom finder logic to a method on your model, and then wrap a cache&amp;#45;scoping thingy around it&amp;#46;  &lt;code&gt;cache_fu&lt;/code&gt; ties this up nicely by giving you a &lt;code&gt;cached&lt;/code&gt; method on AR::Base&amp;#46;&lt;/p&gt;

&lt;div class="typocode"&gt;&lt;pre&gt;&lt;code class="typocode_ruby "&gt;&lt;span class="keyword"&gt;class &lt;/span&gt;&lt;span class="class"&gt;Topic&lt;/span&gt; &lt;span class="punct"&gt;&amp;lt;&lt;/span&gt; &lt;span class="constant"&gt;ActiveRecord&lt;/span&gt;&lt;span class="punct"&gt;::&lt;/span&gt;&lt;span class="constant"&gt;Base&lt;/span&gt;
  &lt;span class="keyword"&gt;def &lt;/span&gt;&lt;span class="method"&gt;self.weekly_popular&lt;/span&gt;
    &lt;span class="constant"&gt;Topic&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;find&lt;/span&gt; &lt;span class="symbol"&gt;:all&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="punct"&gt;...&lt;/span&gt;
  &lt;span class="keyword"&gt;end&lt;/span&gt;
&lt;span class="keyword"&gt;end&lt;/span&gt;

&lt;span class="constant"&gt;Topic&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;cached&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="symbol"&gt;:weekly_popular&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Adding date to cache key with &lt;code&gt;alias_method_chain&lt;/code&gt;:&lt;/p&gt;

&lt;div class="typocode"&gt;&lt;pre&gt;&lt;code class="typocode_ruby "&gt;&lt;span class="keyword"&gt;def &lt;/span&gt;&lt;span class="method"&gt;self.cache_key_with_date&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="ident"&gt;id&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt;
  &lt;span class="punct"&gt;...&lt;/span&gt;
&lt;span class="keyword"&gt;end&lt;/span&gt;

&lt;span class="keyword"&gt;class &lt;/span&gt;&lt;span class="punct"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="constant"&gt;self&lt;/span&gt;
  &lt;span class="ident"&gt;alias_method_chain&lt;/span&gt; &lt;span class="symbol"&gt;:cache_key&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="symbol"&gt;:date&lt;/span&gt;
&lt;span class="keyword"&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Cached loads by ID: &lt;code&gt;Topic.find(1, 2, 3)&lt;/code&gt; moves to &lt;code&gt;Topic.get_cache(1, 2, 3)&lt;/code&gt;, which can parallelize calls to memcached and bring them back as they&amp;#8217;re ready&amp;#46;&lt;/p&gt;

&lt;div class="typocode"&gt;&lt;pre&gt;&lt;code class="typocode_ruby "&gt;&lt;span class="ident"&gt;user_ids&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="attribute"&gt;@topic&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;posts&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;map&lt;/span&gt;&lt;span class="punct"&gt;(&amp;amp;&lt;/span&gt;&lt;span class="symbol"&gt;:user_id&lt;/span&gt;&lt;span class="punct"&gt;).&lt;/span&gt;&lt;span class="ident"&gt;uniq&lt;/span&gt;
&lt;span class="attribute"&gt;@users&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="constant"&gt;User&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;get_cache&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="ident"&gt;user_ids&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;You can also cache associations, so that you&amp;#8217;re navigating associations via Memcache&amp;#46;&lt;/p&gt;

&lt;p&gt;Cache overrides&lt;/p&gt;

&lt;div class="typocode"&gt;&lt;pre&gt;&lt;code class="typocode_ruby "&gt;&lt;span class="keyword"&gt;class &lt;/span&gt;&lt;span class="class"&gt;ApplicationController&lt;/span&gt; &lt;span class="punct"&gt;&amp;lt;&lt;/span&gt; &lt;span class="constant"&gt;ActionController&lt;/span&gt;&lt;span class="punct"&gt;::&lt;/span&gt;&lt;span class="constant"&gt;Base&lt;/span&gt;
  &lt;span class="ident"&gt;before_filter&lt;/span&gt; &lt;span class="symbol"&gt;:set_cache_override&lt;/span&gt;
  &lt;span class="keyword"&gt;def &lt;/span&gt;&lt;span class="method"&gt;set_cache_override&lt;/span&gt;
    &lt;span class="constant"&gt;ActsAsCached&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;skip_cache_gets&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="punct"&gt;!!&lt;/span&gt;&lt;span class="ident"&gt;params&lt;/span&gt;&lt;span class="punct"&gt;[&lt;/span&gt;&lt;span class="symbol"&gt;:skip_cache&lt;/span&gt;&lt;span class="punct"&gt;]&lt;/span&gt;
  &lt;span class="keyword"&gt;end&lt;/span&gt;
&lt;span class="keyword"&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;&lt;code&gt;reset_cache&lt;/code&gt;: Slow, uncached operations can sometimes queue up and wedge a site&amp;#46;  Instead, issue cache resets on completion of a request, rather than expiring beforehand&amp;#46;  That way, requests that continue to pile up will still use the cached copy until the rebuild is complete&amp;#46;&lt;/p&gt;

&lt;div class="typocode"&gt;&lt;pre&gt;&lt;code class="typocode_ruby "&gt;&lt;span class="keyword"&gt;class &lt;/span&gt;&lt;span class="class"&gt;Presentation&lt;/span&gt; &lt;span class="punct"&gt;&amp;lt;&lt;/span&gt; &lt;span class="constant"&gt;ActiveRecord&lt;/span&gt;&lt;span class="punct"&gt;::&lt;/span&gt;&lt;span class="constant"&gt;Base&lt;/span&gt;
  &lt;span class="ident"&gt;after_save&lt;/span&gt; &lt;span class="symbol"&gt;:reset_cache&lt;/span&gt;
&lt;span class="keyword"&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Versioning: a way to expire cache on new code releases&lt;/p&gt;

&lt;div class="typocode"&gt;&lt;pre&gt;&lt;code class="typocode_ruby "&gt;&lt;span class="keyword"&gt;class &lt;/span&gt;&lt;span class="class"&gt;Presentation&lt;/span&gt; &lt;span class="punct"&gt;&amp;lt;&lt;/span&gt; &lt;span class="constant"&gt;ActiveRecord&lt;/span&gt;&lt;span class="punct"&gt;::&lt;/span&gt;&lt;span class="constant"&gt;Base&lt;/span&gt;
  &lt;span class="ident"&gt;acts_as_cached&lt;/span&gt; &lt;span class="symbol"&gt;:version&lt;/span&gt; &lt;span class="punct"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="number"&gt;1&lt;/span&gt;
&lt;span class="keyword"&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Deployment: Chris recommends using Monit to ensure your Memcache servers are up&amp;#46;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;libketama&lt;/code&gt;: consistent hashing that gives you the ability to redeploy Memcache servers without invalidating all the keys&amp;#46;&lt;/p&gt;

&lt;p&gt;Q: Page caching?  A:  Nginx with native Memcache page caching, but outside of Rails domains&amp;#46;&lt;/p&gt;

&lt;p&gt;Lots of other questions, but dude, Chris talks too fast!&lt;/p&gt;</description>
      <pubDate>Sat, 19 May 2007 20:34:00 +0000</pubDate>
      <guid isPermaLink="false">urn:uuid:40171862-80d2-460f-9ad6-ead9fe29fd91</guid>
      <author>Nick Sieger</author>
      <link>http://blog.nicksieger.com/articles/2007/05/19/railsconf-2007-chris-wanstrath-kickin-ass-with-cache-fu</link>
      <category>railsconf</category>
      <category>railsconf2007</category>
      <trackback:ping>http://blog.nicksieger.com/articles/trackback/247</trackback:ping>
    </item>
  </channel>
</rss>
