<?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>Alexey Kovyrin&#039;s Blog &#187; My Projects</title>
	<atom:link href="http://kovyrin.net/category/projects/feed/" rel="self" type="application/rss+xml" />
	<link>http://kovyrin.net</link>
	<description>Yet Another Admin&#039;s blog</description>
	<lastBuildDate>Tue, 17 Aug 2010 15:55:44 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.0</generator>
		<item>
		<title>Fwd: Scalexis Inc is Hiring</title>
		<link>http://kovyrin.net/2010/07/05/scalexis/</link>
		<comments>http://kovyrin.net/2010/07/05/scalexis/#comments</comments>
		<pubDate>Mon, 05 Jul 2010 05:48:53 +0000</pubDate>
		<dc:creator>Alexey Kovyrin</dc:creator>
				<category><![CDATA[My Projects]]></category>

		<guid isPermaLink="false">http://kovyrin.net/?p=488</guid>
		<description><![CDATA[Scalexis Inc, web performance/scalability consulting firm in Toronto, is looking for a full-time consultant. Being a consulting company on the high-demand market of web application scalability consulting and high-performance web applications development, we need an employee that could perform both web application development and web application performance consulting work. The main duty of the employee [...]]]></description>
			<content:encoded><![CDATA[<p><a href="http://scalexis.com">Scalexis Inc</a>, web performance/scalability consulting firm in Toronto, is looking for a full-time consultant. Being a consulting company on the high-demand market of web application scalability consulting and high-performance web applications development, we need an employee that could perform both web application development and web application performance consulting work.<br />
<span id="more-488"></span></p>
<p>The main duty of the employee would be to work directly with the customers&#8217; operations teams to detect, analyze and fix performance problems and plan application scalability in accordance with customers&#8217; needs/requests.</p>
<p>The employee would work in a small team of highly professional consultants helping the company to handle growing demand for web applications performance consulting services. </p>
<p>Position is in Toronto. Telecommuting is not possible.</p>
<p>Being a consulting-related position, it requires an extensive experience with highly-loaded web-applications scalability and, based on our customers&#8217; needs, an expert knowledge of the following technologies: </p>
<ul>
<li>Programming Languages: Ruby, PHP, C/C++, C#, Google Go </li>
<li>Databases: MySQL, Microsoft SQL Server </li>
<li>Operating Systems: MacOS X, Linux, Windows</li>
</ul>
<p>This position requires a Bachelor or Master&#8217;s degree in computer sciences. Fluent oral and written Russian language and an intermediate written English language skills are required.</p>
<p>For more details please contact hr (аt) scalexis.com.</p>
]]></content:encoded>
			<wfw:commentRss>http://kovyrin.net/2010/07/05/scalexis/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Advanced Squid Caching in Scribd: Cache Invalidation Techniques</title>
		<link>http://kovyrin.net/2010/05/29/squid-in-scribd-cache-invalidation/</link>
		<comments>http://kovyrin.net/2010/05/29/squid-in-scribd-cache-invalidation/#comments</comments>
		<pubDate>Sat, 29 May 2010 17:02:17 +0000</pubDate>
		<dc:creator>Alexey Kovyrin</dc:creator>
				<category><![CDATA[Admin-tips]]></category>
		<category><![CDATA[Development]]></category>
		<category><![CDATA[My Projects]]></category>
		<category><![CDATA[Networks]]></category>
		<category><![CDATA[caching]]></category>
		<category><![CDATA[HTCP]]></category>
		<category><![CDATA[invalidation]]></category>
		<category><![CDATA[Nginx]]></category>
		<category><![CDATA[plugin]]></category>
		<category><![CDATA[squid]]></category>

		<guid isPermaLink="false">http://kovyrin.net/?p=322</guid>
		<description><![CDATA[Having a reverse-proxy web cache as one of the major infrastructure elements brings many benefits for large web applications: it reduces your application servers load, reduces average response times on your site, etc. But there is one problem every developer experiences when works with such a cache &#8211; cached content invalidation. It is a complex [...]]]></description>
			<content:encoded><![CDATA[<p>Having a <a href="http://kovyrin.net/2008/10/25/advanced-squid-caching-for-rails-applications-preface/">reverse-proxy</a> web cache as one of the major infrastructure elements brings many benefits for large web applications: it reduces your application servers load, reduces average response times on your site, etc. But there is one problem every developer experiences when works with such a cache &#8211; <em>cached content invalidation</em>.</p>
<p>It is a complex problem that usually consists of two smaller ones: i<em>ndividual cache elements invalidation</em> (you need to keep an eye on your data changes and invalidate cached pages when related data changes) and <em>full cache purges</em> (sometimes your site layout or page templates change and you need to purge all the cached pages to make sure users will get new visual elements of layout changes). In this post I&#8217;d like to look at a few techniques we use at <a href="http://www.scribd.com/">Scribd</a> to solve cache invalidation problems.</p>
<p><span id="more-322"></span></p>
<hr />So, the <strong>first problem &#8211; ongoing cache invalidation when content changes</strong>. This is actually a pretty simple task in squid: you just use <a href="http://www.htcp.org/">HTCP protocol</a> and send CLR requests to your caching farm (we didn&#8217;t find any HTCP protocol implementations so we&#8217;ve implemented <a href="http://github.com/kovyrin/htcp-ruby">our own simple client</a> that supports just one command).</p>
<p>Since we use <a href="http://haproxy.1wt.eu/">haproxy</a> to balance our traffic in the cluster it is hard to predict where should we send a purge request. So we fan those out to all cache servers.</p>
<p>To make sure cache purging won&#8217;t slow the site down, especially considering we need to do more that just a simple cache purge (submit documents to search indexes, etc, etc), we just spool a &#8220;document changed&#8221; request to a queue and then have a set of <a href="http://github.com/kovyrin/loops">asynchronous processes</a> that do all the work in background.</p>
<p>Next, <strong>The Hard Problem &#8211; handling full cache purges w/o killing our backend servers</strong> with 5x-10x traffic (our normal hit ratio is ~90-95%).</p>
<p>We&#8217;ve spent a lot of time thinking about this problem and the first idea we came up with was to have a loop process somewhere that would iterate all documents we have cached and purge them one by one&#8230; but that does not seem to be a practical solution when you have tens of millions documents (and few page versions per document) and obviously the solution would not scale with constantly growing documents corpus.</p>
<p>So we kept brainstorming and finally got one idea that works just perfectly for us: what if we&#8217;d be able to take our traffic and define a function <em>f(t)</em> that would return a percentage of the traffic that should be purged at any moment in time. So we did it &#8211; we&#8217;ve implemented a nginx module that would version our cache by assigning every cached page a revision (<a href="http://kovyrin.net/2009/07/21/advanced-squid-caching-scribd-logged-in-users-complex-urls/">using a custom HTTP-headers + Vary-caching</a>) and would be able to slowly migrate the cache from one revision to another over a pre-defined period of time.</p>
<p>Here is an overview of the requests/responses flow in our web/cache/application cluster:</p>
<p><center><img src="http://img.skitch.com/20100605-dyqee8s35ukg4cxefu3ua5jiit.png" alt="Document Page Caching" /></center></p>
<p>X-Cache-Revision value selection algorithm is the following:</p>
<p><center><img src="http://img.skitch.com/20100605-kpahhhdc92uig9qmtih4rqa6tu.png" alt="Cache Revision Selection" /></center></p>
<p>Having this kind of logic in place allows us to do so called &#8220;slow&#8221; cache purges that could take any time from a few minutes (that still helps to reduce the load spike generated by the hottest content) up to many hours (this is what we normally use) or days (never used this option, but it is definitely possible).</p>
<p>Here is an example 100% cache purge over an 8 hour interval:</p>
<ol>
<li> Daily hit ratio graph:<br />
<a href="http://img.skitch.com/20100529-pkx64g6the9winqcnk6sigiyns.png" rel="shadowbox[post-322];player=img;"><img rel="shadowbox" src="http://img.skitch.com/20100529-pkx64g6the9winqcnk6sigiyns.jpg" width="470" alt="day" /></a>
</li>
<li> Weekly hit ratio graph:<br />
<a  href="http://img.skitch.com/20100529-nk2hyafgtbw1pc1nrkgbec8st3.png" rel="shadowbox[post-322];player=img;"><img rel="shadowbox" src="http://img.skitch.com/20100529-nk2hyafgtbw1pc1nrkgbec8st3.jpg" width="470" alt="week" /></a>
</li>
</ol>
<p>As you can see, during those slow purges our cached pages would be slowly updated without putting too much pressure on the backend. Cache hit ratio would slowly degrade and then slowly get back to its normal levels, but with our normal (6-8 hours) purges hit ratio never gets lower that 65-70% which makes it possible for us to save huge amounts of money on not having 90% spare capacity just for the cache purge load surges (we used to have lots of spare application cluster capacity before introducing this approach).</p>
<p><strong>Update (June 5th, 2010):</strong> Request/response flow and cache revision selection algorithm added.</p>
]]></content:encoded>
			<wfw:commentRss>http://kovyrin.net/2010/05/29/squid-in-scribd-cache-invalidation/feed/</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
		<item>
		<title>DbCharmer &#8211; Rails Can Scale!</title>
		<link>http://kovyrin.net/2010/04/16/dbcharmer-rails-can-scale/</link>
		<comments>http://kovyrin.net/2010/04/16/dbcharmer-rails-can-scale/#comments</comments>
		<pubDate>Fri, 16 Apr 2010 21:33:18 +0000</pubDate>
		<dc:creator>Alexey Kovyrin</dc:creator>
				<category><![CDATA[Databases]]></category>
		<category><![CDATA[Development]]></category>
		<category><![CDATA[My Projects]]></category>
		<category><![CDATA[ActiveRecord]]></category>
		<category><![CDATA[MySQL]]></category>
		<category><![CDATA[rails]]></category>
		<category><![CDATA[Ruby]]></category>
		<category><![CDATA[scalability]]></category>
		<category><![CDATA[sharding]]></category>

		<guid isPermaLink="false">http://kovyrin.net/?p=397</guid>
		<description><![CDATA[Back in November 2009 I was working on a project to port Scribd.com code base to Rails 2.2 and noticed that some old plugins we were using in 2.1 were abandoned by their authors. Some of them were just removed from the code base, but one needed a replacement &#8211; that was an old plugin [...]]]></description>
			<content:encoded><![CDATA[<p>Back in November 2009 I was working on a project to port <a href="http://www.scribd.com">Scribd.com</a> code base to Rails 2.2 and noticed that some old plugins we were using in 2.1 were abandoned by their authors. Some of them were just removed from the code base, but one needed a replacement &#8211; that was an old plugin called acts_as_readonlyable that helped us to distribute our queries among a cluster of MySQL slaves. There were some alternatives but we didn&#8217;t like them for one or another reasons so we&#8217;ve decided to go with creating our own ActiveRecord plugin, that would help us scale our databases out. That&#8217;s the story behind the <a href="http://kovyrin.net/2009/11/03/db-charmer-activerecord-connection-magic-plugin/">first release of DbCharmer</a>.</p>
<p>Today, six months after the first release of <a href="http://rubygems.org/gems/db-charmer">the gem</a> and we&#8217;ve moved it to gemcutter (which is now the official gems hosting) and we&#8217;re already at <a href="http://rubygems.org/gems/db-charmer/versions">version 1.6.11</a>. The gem was downloaded more than 2000 times. There are (at least) 10+ large users that rely on this gem to scale their products out. And (this is the most exciting) we&#8217;ve added tons of new features to the product. </p>
<p>Here are the main features added since the first release:</p>
<ul>
<li>Much better <strong>multi-database migrations</strong> support including default migrations connection changing.</li>
<li>We&#8217;ve added <strong>ActiveRecord associations preload</strong> support that makes it possible to move eager loading queries to the same connection where your finder queries go to.</li>
<li>We&#8217;ve improved <strong>ActiveRecord&#8217;s query logging</strong> feature and now you can see what connections your queries executed on (and yes, all those improvements are colorized <img src='http://kovyrin.net/wp-includes/images/smilies/icon_smile.gif' alt=':-)' class='wp-smiley' /> ).</li>
<li>We&#8217;ve added an ability to temporary<strong> remap any ActiveRecord connections</strong> to any other connections for a block of code (really useful when you need to make sure all your queries would go to some non-default slave and you do not want to mess with all your models).</li>
<li>The most interesting change: we&#8217;ve implemented some basic <strong>sharding functionality in ActiveRecord</strong> which currently is being used in production in our application.</li>
</ul>
<p>As you can see now DbCharmer helps you to do three major scalability tasks in your Rails projects:</p>
<ol>
<li><strong>Master-Slave clusters</strong> to scale out your Rails models reads.</li>
<li><strong>Vertical sharding</strong> by moving some of your models to a separate (maybe even dedicated) servers and still keep using AR associations</li>
<li><strong>Horizontal sharding</strong> by slicing your models data to pieces and placing those pieces into different databases and/or servers.</li>
</ol>
<p> So, If you didn&#8217;t check DbCharmer out yet and you&#8217;re working on some large rails project that is (or going to be) facing scalability problems, go <a href="http://github.com/kovyrin/db-charmer/blob/master/README.rdoc">read the docs</a>, download/install the <a href="http://rubygems.org/gems/db-charmer">gem</a> and prove them that <strong>Rails CAN scale</strong>!</p>
]]></content:encoded>
			<wfw:commentRss>http://kovyrin.net/2010/04/16/dbcharmer-rails-can-scale/feed/</wfw:commentRss>
		<slash:comments>6</slash:comments>
		</item>
		<item>
		<title>DB Charmer &#8211; ActiveRecord Connection Magic Plugin</title>
		<link>http://kovyrin.net/2009/11/03/db-charmer-activerecord-connection-magic-plugin/</link>
		<comments>http://kovyrin.net/2009/11/03/db-charmer-activerecord-connection-magic-plugin/#comments</comments>
		<pubDate>Tue, 03 Nov 2009 15:36:26 +0000</pubDate>
		<dc:creator>Alexey Kovyrin</dc:creator>
				<category><![CDATA[Databases]]></category>
		<category><![CDATA[Development]]></category>
		<category><![CDATA[My Projects]]></category>
		<category><![CDATA[ActiveRecord]]></category>
		<category><![CDATA[MySQL]]></category>
		<category><![CDATA[Ruby]]></category>
		<category><![CDATA[Ruby On Rails]]></category>
		<category><![CDATA[scalability]]></category>
		<category><![CDATA[scribd]]></category>

		<guid isPermaLink="false">http://kovyrin.net/?p=324</guid>
		<description><![CDATA[Today I&#8217;m proud to announce the first public release of our ActiveRecord database connection magic plugin: DbCharmer. DB Charmer &#8211; ActiveRecord Connection Magic Plugin DbCharmer is a simple yet powerful plugin for ActiveRecord that does a few things: Allows you to easily manage AR models&#8217; connections (switch_connection_to method) Allows you to switch AR models&#8217; default [...]]]></description>
			<content:encoded><![CDATA[<p>Today I&#8217;m proud to announce the first public release of <a href="http://www.scribd.com">our</a> ActiveRecord database connection magic plugin: <a href="http://github.com/kovyrin/db-charmer">DbCharmer</a>. </p>
<hr/>
<h2>DB Charmer &#8211; ActiveRecord Connection Magic Plugin</h2>
<p><tt>DbCharmer</tt> is a simple yet powerful plugin for ActiveRecord that does a few things:</p>
<ol>
<li>Allows you to easily manage AR models&#8217; connections (<code class="codecolorer ruby dawn"><span class="ruby">switch_connection_to</span></code> method)
<li>Allows you to switch AR models&#8217; default connections to a separate servers/databases
<li>Allows you to easily choose where your query should go (<code class="codecolorer ruby dawn"><span class="ruby">on_<span style="color:#006600; font-weight:bold;">*</span></span></code> methods family)
<li>Allows you to automatically send read queries to your slaves while masters would handle all the updates.
<li>Adds multiple databases migrations to ActiveRecord
</ol>
<p><span id="more-324"></span></p>
<h2>Installation</h2>
<p>There are two options when approaching db-charmer installation:</p>
<ul>
<li>using the <a href="http://gemcutter.org/gems/db-charmer">gem</a> (recommended)
<li>install as a Rails plugin
</ul>
<p>To install as a <a href="http://gemcutter.org/gems/db-charmer">gem</a>, add this to your environment.rb:</p>
<div class="codecolorer-container ruby twitlight" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br /></div></td><td><div class="ruby codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">config.<span style="color:#9900CC;">gem</span> <span style="color:#996600;">'db-charmer'</span>, <span style="color:#ff3333; font-weight:bold;">:lib</span> <span style="color:#006600; font-weight:bold;">=&gt;</span> <span style="color:#996600;">'db_charmer'</span>, <br />
&nbsp; &nbsp; <span style="color:#ff3333; font-weight:bold;">:source</span> <span style="color:#006600; font-weight:bold;">=&gt;</span> <span style="color:#996600;">'http://gemcutter.org'</span></div></td></tr></tbody></table></div>
<p>And then run the command:</p>
<div class="codecolorer-container bash twitlight" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br /></div></td><td><div class="bash codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap"><span style="color: #c20cb9; font-weight: bold;">sudo</span> rake gems:<span style="color: #c20cb9; font-weight: bold;">install</span></div></td></tr></tbody></table></div>
<p>To install db-charmer as a Rails plugin use this:</p>
<div class="codecolorer-container bash twitlight" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br /></div></td><td><div class="bash codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">script<span style="color: #000000; font-weight: bold;">/</span>plugin <span style="color: #c20cb9; font-weight: bold;">install</span> <span style="color: #c20cb9; font-weight: bold;">git</span>:<span style="color: #000000; font-weight: bold;">//</span>github.com<span style="color: #000000; font-weight: bold;">/</span>kovyrin<span style="color: #000000; font-weight: bold;">/</span>db-charmer.git</div></td></tr></tbody></table></div>
<h2>Easy ActiveRecord Connection Management</h2>
<p>As a part of this plugin we&#8217;ve added <code class="codecolorer ruby dawn"><span class="ruby">switch_connection_to</span></code> method that accepts many different kinds of db connections specifications and uses them on a model. We support:</p>
<ol>
<li>Strings and symbols as the names of connection configuration blocks in database.yml.
<li>ActiveRecord models (we&#8217;d use connection currently set up on a model).
<li>Database connections (<code class="codecolorer ruby dawn"><span class="ruby">Model.<span style="color:#9900CC;">connection</span></span></code>)
<li>Nil values to reset model to default connection.
</ol>
<p>Sample code:</p>
<div class="codecolorer-container ruby twitlight" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br />3<br />4<br />5<br />6<br />7<br /></div></td><td><div class="ruby codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap"><span style="color:#9966CC; font-weight:bold;">class</span> Foo <span style="color:#006600; font-weight:bold;">&lt;</span> <span style="color:#6666ff; font-weight:bold;">ActiveRecord::Model</span>; <span style="color:#9966CC; font-weight:bold;">end</span><br />
<br />
Foo.<span style="color:#9900CC;">switch_connection_to</span><span style="color:#006600; font-weight:bold;">&#40;</span><span style="color:#ff3333; font-weight:bold;">:blah</span><span style="color:#006600; font-weight:bold;">&#41;</span><br />
Foo.<span style="color:#9900CC;">switch_connection_to</span><span style="color:#006600; font-weight:bold;">&#40;</span><span style="color:#996600;">'foo'</span><span style="color:#006600; font-weight:bold;">&#41;</span><br />
Foo.<span style="color:#9900CC;">switch_connection_to</span><span style="color:#006600; font-weight:bold;">&#40;</span>Bar<span style="color:#006600; font-weight:bold;">&#41;</span><br />
Foo.<span style="color:#9900CC;">switch_connection_to</span><span style="color:#006600; font-weight:bold;">&#40;</span>Baz.<span style="color:#9900CC;">connection</span><span style="color:#006600; font-weight:bold;">&#41;</span><br />
Foo.<span style="color:#9900CC;">switch_connection_to</span><span style="color:#006600; font-weight:bold;">&#40;</span><span style="color:#0000FF; font-weight:bold;">nil</span><span style="color:#006600; font-weight:bold;">&#41;</span></div></td></tr></tbody></table></div>
<p>The <code class="codecolorer ruby dawn"><span class="ruby">switch_connection_to</span></code> method has an optional second parameter <code class="codecolorer ruby dawn"><span class="ruby">should_exist</span></code> which is true by default. This parameter is used when the method is called with a string or a symbol connection name and there is no such connection configuration in the database.yml file. If this parameter is <code class="codecolorer ruby dawn"><span class="ruby"><span style="color:#0000FF; font-weight:bold;">true</span></span></code>, an exception would be raised, otherwise, the error would be ignored and no connection change would happen.</p>
<p>This is really useful when in development mode or in a tests you do not want to create many different databases on your local machine and just want to put all your tables in a single database.</p>
<p><b>Warning:</b> All the connection switching calls would switch connection <b>only</b> for those classes the method called on. You can&#8217;t call the <code class="codecolorer ruby dawn"><span class="ruby">switch_connection_to</span></code> method and switch connection for a base class in some hierarchy (for example, you can&#8217;t switch AR::Base connection and see all your models switched to the new connection, use the classic <code class="codecolorer ruby dawn"><span class="ruby">establish_connection</span></code> instead).</p>
<h2>Multiple DB Migrations</h2>
<p>In every application that works with many databases, there is need in a convenient schema migrations mechanism.</p>
<p>All Rails users already have this mechanism &#8211; rails migrations. So in <tt>DbCharmer</tt>, we&#8217;ve made it possible to seamlessly use multiple databases in Rails migrations.</p>
<p>There are two methods available in migrations to operate on more than one database:</p>
<p>1. Global connection change method &#8211; used to switch whole migration to a non-default database.<br />
2. Block-level connection change method &#8211; could be used to do only a part of a migration on a non-default db.</p>
<p>Migration class example (global connection rewrite):</p>
<div class="codecolorer-container ruby twitlight" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br />3<br />4<br />5<br />6<br />7<br />8<br />9<br />10<br />11<br />12<br />13<br />14<br /></div></td><td><div class="ruby codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap"><span style="color:#9966CC; font-weight:bold;">class</span> MultiDbTest <span style="color:#006600; font-weight:bold;">&lt;</span> <span style="color:#6666ff; font-weight:bold;">ActiveRecord::Migration</span><br />
&nbsp; &nbsp;db_magic <span style="color:#ff3333; font-weight:bold;">:connection</span> <span style="color:#006600; font-weight:bold;">=&gt;</span> <span style="color:#ff3333; font-weight:bold;">:second_db</span><br />
<br />
&nbsp; &nbsp;<span style="color:#9966CC; font-weight:bold;">def</span> <span style="color:#0000FF; font-weight:bold;">self</span>.<span style="color:#9900CC;">up</span><br />
&nbsp; &nbsp; &nbsp;create_table <span style="color:#ff3333; font-weight:bold;">:test_table</span>, <span style="color:#ff3333; font-weight:bold;">:force</span> <span style="color:#006600; font-weight:bold;">=&gt;</span> <span style="color:#0000FF; font-weight:bold;">true</span> <span style="color:#9966CC; font-weight:bold;">do</span> <span style="color:#006600; font-weight:bold;">|</span>t<span style="color:#006600; font-weight:bold;">|</span><br />
&nbsp; &nbsp; &nbsp; &nbsp;t.<span style="color:#CC0066; font-weight:bold;">string</span> <span style="color:#ff3333; font-weight:bold;">:test_string</span><br />
&nbsp; &nbsp; &nbsp; &nbsp;t.<span style="color:#9900CC;">timestamps</span><br />
&nbsp; &nbsp; &nbsp;<span style="color:#9966CC; font-weight:bold;">end</span><br />
&nbsp; &nbsp;<span style="color:#9966CC; font-weight:bold;">end</span><br />
<br />
&nbsp; &nbsp;<span style="color:#9966CC; font-weight:bold;">def</span> <span style="color:#0000FF; font-weight:bold;">self</span>.<span style="color:#9900CC;">down</span><br />
&nbsp; &nbsp; &nbsp;drop_table <span style="color:#ff3333; font-weight:bold;">:test_table</span><br />
&nbsp; &nbsp;<span style="color:#9966CC; font-weight:bold;">end</span><br />
&nbsp;<span style="color:#9966CC; font-weight:bold;">end</span></div></td></tr></tbody></table></div>
<p>Migration class example (block-level connection rewrite):</p>
<div class="codecolorer-container ruby twitlight" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br />3<br />4<br />5<br />6<br />7<br />8<br />9<br />10<br />11<br />12<br />13<br />14<br /></div></td><td><div class="ruby codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap"><span style="color:#9966CC; font-weight:bold;">class</span> MultiDbTest <span style="color:#006600; font-weight:bold;">&lt;</span> <span style="color:#6666ff; font-weight:bold;">ActiveRecord::Migration</span><br />
&nbsp; <span style="color:#9966CC; font-weight:bold;">def</span> <span style="color:#0000FF; font-weight:bold;">self</span>.<span style="color:#9900CC;">up</span><br />
&nbsp; &nbsp; on_db <span style="color:#ff3333; font-weight:bold;">:second_db</span> <span style="color:#9966CC; font-weight:bold;">do</span><br />
&nbsp; &nbsp; &nbsp; create_table <span style="color:#ff3333; font-weight:bold;">:test_table</span>, <span style="color:#ff3333; font-weight:bold;">:force</span> <span style="color:#006600; font-weight:bold;">=&gt;</span> <span style="color:#0000FF; font-weight:bold;">true</span> <span style="color:#9966CC; font-weight:bold;">do</span> <span style="color:#006600; font-weight:bold;">|</span>t<span style="color:#006600; font-weight:bold;">|</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; t.<span style="color:#CC0066; font-weight:bold;">string</span> <span style="color:#ff3333; font-weight:bold;">:test_string</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; t.<span style="color:#9900CC;">timestamps</span><br />
&nbsp; &nbsp; &nbsp; <span style="color:#9966CC; font-weight:bold;">end</span><br />
&nbsp; &nbsp; <span style="color:#9966CC; font-weight:bold;">end</span><br />
&nbsp; <span style="color:#9966CC; font-weight:bold;">end</span><br />
<br />
&nbsp; <span style="color:#9966CC; font-weight:bold;">def</span> <span style="color:#0000FF; font-weight:bold;">self</span>.<span style="color:#9900CC;">down</span><br />
&nbsp; &nbsp; on_db <span style="color:#ff3333; font-weight:bold;">:second_db</span> <span style="color:#006600; font-weight:bold;">&#123;</span> drop_table <span style="color:#ff3333; font-weight:bold;">:test_table</span> <span style="color:#006600; font-weight:bold;">&#125;</span><br />
&nbsp; <span style="color:#9966CC; font-weight:bold;">end</span><br />
<span style="color:#9966CC; font-weight:bold;">end</span></div></td></tr></tbody></table></div>
<p>By default in development and test environments you could skip this <code class="codecolorer ruby dawn"><span class="ruby"><span style="color:#ff3333; font-weight:bold;">:second_db</span></span></code> connection from your database.yml files and rails would create the tables in your single database, but in production you&#8217;d specify it and get the table created on a separate server and/or in a separate database.</p>
<p>This behaviour is controlled by the <code class="codecolorer ruby dawn"><span class="ruby">DbCharmer.<span style="color:#9900CC;">migration_connections_should_exist</span></span></code> configuration attribute which could be set from a rails initializer.</p>
<h2>Using Models in Master-Slave Environments</h2>
<p>Master-slave replication is the most popular scale-out technique in a medium-sized and large database-centric applications today. There are some rails plugins out there that help developers to use slave servers in their models but none of them were flexible enough for us to start using them in a huge application we work on.</p>
<p>So, we&#8217;ve been using ActsAsReadonlyable plugin for a long time and have made tons of changes in its code over the time. But since that plugin has been abandoned by its authors, we&#8217;ve decided to collect all of our master-slave code in one plugin and release it for rails 2.2+. <tt>DbCharmer</tt> adds the following features to Rails models:</p>
<h3>Auto-Switching all Reads to the Slave(s)</h3>
<p>When you create a model, you could use <code class="codecolorer ruby dawn"><span class="ruby">db_magic <span style="color:#ff3333; font-weight:bold;">:slave</span> <span style="color:#006600; font-weight:bold;">=&gt;</span> <span style="color:#ff3333; font-weight:bold;">:blah</span></span></code> or <code class="codecolorer ruby dawn"><span class="ruby">db_magic <span style="color:#ff3333; font-weight:bold;">:slaves</span> <span style="color:#006600; font-weight:bold;">=&gt;</span> <span style="color:#006600; font-weight:bold;">&#91;</span> <span style="color:#ff3333; font-weight:bold;">:foo</span>, <span style="color:#ff3333; font-weight:bold;">:bar</span> <span style="color:#006600; font-weight:bold;">&#93;</span></span></code> commands in your model to set up reads redirection mode when all your find/count/exist/etc methods will be reading data from your slave (or a bunch of slaves in a round-robin manner). Here is an example:</p>
<div class="codecolorer-container ruby twitlight" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br />3<br />4<br />5<br />6<br />7<br /></div></td><td><div class="ruby codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap"><span style="color:#9966CC; font-weight:bold;">class</span> Foo <span style="color:#006600; font-weight:bold;">&lt;</span> <span style="color:#6666ff; font-weight:bold;">ActiveRecord::Base</span><br />
&nbsp; db_magic <span style="color:#ff3333; font-weight:bold;">:slave</span> <span style="color:#006600; font-weight:bold;">=&gt;</span> <span style="color:#ff3333; font-weight:bold;">:slave01</span><br />
<span style="color:#9966CC; font-weight:bold;">end</span><br />
<br />
<span style="color:#9966CC; font-weight:bold;">class</span> Bar <span style="color:#006600; font-weight:bold;">&lt;</span> <span style="color:#6666ff; font-weight:bold;">ActiveRecord::Base</span><br />
&nbsp; db_magic <span style="color:#ff3333; font-weight:bold;">:slaves</span> <span style="color:#006600; font-weight:bold;">=&gt;</span> <span style="color:#006600; font-weight:bold;">&#91;</span> <span style="color:#ff3333; font-weight:bold;">:slave01</span>, <span style="color:#ff3333; font-weight:bold;">:slave02</span> <span style="color:#006600; font-weight:bold;">&#93;</span><br />
<span style="color:#9966CC; font-weight:bold;">end</span></div></td></tr></tbody></table></div>
<h3>Default Connection Switching</h3>
<p>If you have more than one master-slave cluster (or simply more than one database) in your database environment, then you might want to change the default database connection of some of your models. You could do that by using <code class="codecolorer ruby dawn"><span class="ruby">db_magic <span style="color:#ff3333; font-weight:bold;">:connection</span> <span style="color:#006600; font-weight:bold;">=&gt;</span> <span style="color:#ff3333; font-weight:bold;">:foo</span></span></code> call from your models. Example:</p>
<div class="codecolorer-container ruby twitlight" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br />3<br /></div></td><td><div class="ruby codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap"><span style="color:#9966CC; font-weight:bold;">class</span> Foo <span style="color:#006600; font-weight:bold;">&lt;</span> <span style="color:#6666ff; font-weight:bold;">ActiveRecord::Base</span><br />
&nbsp; db_magic <span style="color:#ff3333; font-weight:bold;">:connection</span> <span style="color:#006600; font-weight:bold;">=&gt;</span> <span style="color:#ff3333; font-weight:bold;">:foo</span><br />
<span style="color:#9966CC; font-weight:bold;">end</span></div></td></tr></tbody></table></div>
<p>Sample model on a separate master-slave cluster (so, separate main connection + a slave connection):</p>
<div class="codecolorer-container ruby twitlight" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br />3<br /></div></td><td><div class="ruby codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap"><span style="color:#9966CC; font-weight:bold;">class</span> Bar <span style="color:#006600; font-weight:bold;">&lt;</span> <span style="color:#6666ff; font-weight:bold;">ActiveRecord::Base</span><br />
&nbsp; db_magic <span style="color:#ff3333; font-weight:bold;">:connection</span> <span style="color:#006600; font-weight:bold;">=&gt;</span> <span style="color:#ff3333; font-weight:bold;">:bar</span>, <span style="color:#ff3333; font-weight:bold;">:slave</span> <span style="color:#006600; font-weight:bold;">=&gt;</span> <span style="color:#ff3333; font-weight:bold;">:bar_slave</span><br />
<span style="color:#9966CC; font-weight:bold;">end</span></div></td></tr></tbody></table></div>
<h3>Per-Query Connection Management</h3>
<p>Sometimes you have select queries that you know you want to run on the master. This could happen for example when you have just added some data and need to read it back and not sure if it made it all the way to the slave yet or no. For this situation and a few others there is a set of methods we&#8217;ve added to ActiveRecord models:</p>
<p>1) +on_master+ &#8211; this method could be used in two forms: block form and proxy form. In the block form you could force connection switch for a block of code:</p>
<div class="codecolorer-container ruby twitlight" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br />3<br />4<br /></div></td><td><div class="ruby codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">User.<span style="color:#9900CC;">on_master</span> <span style="color:#9966CC; font-weight:bold;">do</span><br />
&nbsp; user = User.<span style="color:#9900CC;">find_by_login</span><span style="color:#006600; font-weight:bold;">&#40;</span><span style="color:#996600;">'foo'</span><span style="color:#006600; font-weight:bold;">&#41;</span><br />
&nbsp; user.<span style="color:#9900CC;">update_attributes</span>!<span style="color:#006600; font-weight:bold;">&#40;</span><span style="color:#ff3333; font-weight:bold;">:activated</span> <span style="color:#006600; font-weight:bold;">=&gt;</span> <span style="color:#0000FF; font-weight:bold;">true</span><span style="color:#006600; font-weight:bold;">&#41;</span><br />
<span style="color:#9966CC; font-weight:bold;">end</span></div></td></tr></tbody></table></div>
<p>In the proxy form this method could be used to force one query to be performed on the master database server:</p>
<div class="codecolorer-container ruby twitlight" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br />3<br /></div></td><td><div class="ruby codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">Comment.<span style="color:#9900CC;">on_master</span>.<span style="color:#9900CC;">last</span><span style="color:#006600; font-weight:bold;">&#40;</span><span style="color:#ff3333; font-weight:bold;">:limit</span> <span style="color:#006600; font-weight:bold;">=&gt;</span> <span style="color:#006666;">5</span><span style="color:#006600; font-weight:bold;">&#41;</span><br />
User.<span style="color:#9900CC;">on_master</span>.<span style="color:#9900CC;">find_by_activation_code</span><span style="color:#006600; font-weight:bold;">&#40;</span>code<span style="color:#006600; font-weight:bold;">&#41;</span><br />
User.<span style="color:#9900CC;">on_master</span>.<span style="color:#9900CC;">exists</span>?<span style="color:#006600; font-weight:bold;">&#40;</span><span style="color:#ff3333; font-weight:bold;">:login</span> <span style="color:#006600; font-weight:bold;">=&gt;</span> login, <span style="color:#ff3333; font-weight:bold;">:password</span> <span style="color:#006600; font-weight:bold;">=&gt;</span> password<span style="color:#006600; font-weight:bold;">&#41;</span></div></td></tr></tbody></table></div>
<p>2) <code class="codecolorer ruby dawn"><span class="ruby">on_slave</span></code> &#8211; this method is used to force a query to be run on a slave even in situations when it&#8217;s been previously forced to use the master. If there is more than one slave, one would be selected randomly. This method has two forms as well: block and proxy.</p>
<p>3) <code class="codecolorer ruby dawn"><span class="ruby">on_db<span style="color:#006600; font-weight:bold;">&#40;</span>connection<span style="color:#006600; font-weight:bold;">&#41;</span></span></code> &#8211; this method is what makes two previous methods possible. It is used to switch a model&#8217;s connection to some db for a short block of code or even for one statement (two forms). It accepts the same range of values as the <code class="codecolorer ruby dawn"><span class="ruby">switch_connection_to</span></code> method does. Example:</p>
<div class="codecolorer-container ruby twitlight" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br /></div></td><td><div class="ruby codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">Comment.<span style="color:#9900CC;">on_db</span><span style="color:#006600; font-weight:bold;">&#40;</span><span style="color:#ff3333; font-weight:bold;">:olap</span><span style="color:#006600; font-weight:bold;">&#41;</span>.<span style="color:#9900CC;">count</span><br />
Post.<span style="color:#9900CC;">on_db</span><span style="color:#006600; font-weight:bold;">&#40;</span><span style="color:#ff3333; font-weight:bold;">:foo</span><span style="color:#006600; font-weight:bold;">&#41;</span>.<span style="color:#9900CC;">find</span><span style="color:#006600; font-weight:bold;">&#40;</span><span style="color:#ff3333; font-weight:bold;">:first</span><span style="color:#006600; font-weight:bold;">&#41;</span></div></td></tr></tbody></table></div>
<h3>Associations Connection Management</h3>
<p>ActiveRecord models can have an associations with each other and since every model has its own database connections, it becomes pretty hard to manage connections in a chained calls like <code class="codecolorer ruby dawn"><span class="ruby">User.<span style="color:#9900CC;">posts</span>.<span style="color:#9900CC;">count</span></span></code>. With a class-only connection switching methods this call would look like the following if we&#8217;d want to count posts on a separate database:</p>
<div class="codecolorer-container ruby twitlight" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br /></div></td><td><div class="ruby codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">Post.<span style="color:#9900CC;">on_db</span><span style="color:#006600; font-weight:bold;">&#40;</span><span style="color:#ff3333; font-weight:bold;">:olap</span><span style="color:#006600; font-weight:bold;">&#41;</span> <span style="color:#006600; font-weight:bold;">&#123;</span> User.<span style="color:#9900CC;">posts</span>.<span style="color:#9900CC;">count</span> <span style="color:#006600; font-weight:bold;">&#125;</span></div></td></tr></tbody></table></div>
<p>Apparently this is not the best way to write the code and we&#8217;ve implemented an <code class="codecolorer ruby dawn"><span class="ruby">on_<span style="color:#006600; font-weight:bold;">*</span></span></code> methods on associations as well so you could do things like this:</p>
<div class="codecolorer-container ruby twitlight" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br /></div></td><td><div class="ruby codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap"><span style="color:#0066ff; font-weight:bold;">@user</span>.<span style="color:#9900CC;">posts</span>.<span style="color:#9900CC;">on_db</span><span style="color:#006600; font-weight:bold;">&#40;</span><span style="color:#ff3333; font-weight:bold;">:olap</span><span style="color:#006600; font-weight:bold;">&#41;</span>.<span style="color:#9900CC;">count</span><br />
<span style="color:#0066ff; font-weight:bold;">@user</span>.<span style="color:#9900CC;">posts</span>.<span style="color:#9900CC;">on_slave</span>.<span style="color:#9900CC;">find</span><span style="color:#006600; font-weight:bold;">&#40;</span><span style="color:#ff3333; font-weight:bold;">:title</span> <span style="color:#006600; font-weight:bold;">=&gt;</span> <span style="color:#996600;">'Hello, world!'</span><span style="color:#006600; font-weight:bold;">&#41;</span></div></td></tr></tbody></table></div>
<p>Notice: Since ActiveRecord associations implemented as proxies for resulting objects/collections, it is possible to use our connection switching methods even without chained methods:</p>
<div class="codecolorer-container ruby twitlight" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br /></div></td><td><div class="ruby codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap"><span style="color:#0066ff; font-weight:bold;">@post</span>.<span style="color:#9900CC;">user</span>.<span style="color:#9900CC;">on_slave</span> <span style="color:#008000; font-style:italic;"># would return post's author</span><br />
<span style="color:#0066ff; font-weight:bold;">@photo</span>.<span style="color:#9900CC;">owner</span>.<span style="color:#9900CC;">on_slave</span> <span style="color:#008000; font-style:italic;"># would return photo's owner</span></div></td></tr></tbody></table></div>
<p>Starting with <tt>DbCharmer</tt> release 1.4 it is possible to use prefix notation for has_many and HABTM associations connection switching:</p>
<div class="codecolorer-container ruby twitlight" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br /></div></td><td><div class="ruby codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap"><span style="color:#0066ff; font-weight:bold;">@user</span>.<span style="color:#9900CC;">on_db</span><span style="color:#006600; font-weight:bold;">&#40;</span><span style="color:#ff3333; font-weight:bold;">:foo</span><span style="color:#006600; font-weight:bold;">&#41;</span>.<span style="color:#9900CC;">posts</span><br />
<span style="color:#0066ff; font-weight:bold;">@user</span>.<span style="color:#9900CC;">on_slave</span>.<span style="color:#9900CC;">posts</span></div></td></tr></tbody></table></div>
<h3>Named Scopes Support</h3>
<p>To make it easier for <tt>DbCharmer</tt> users to use connections switching methods with named scopes, we&#8217;ve added <code class="codecolorer ruby dawn"><span class="ruby">on_<span style="color:#006600; font-weight:bold;">*</span></span></code> methods support on the scopes as well. All the following scope chains would do exactly the same way (the query would be executed on the <code class="codecolorer ruby dawn"><span class="ruby"><span style="color:#ff3333; font-weight:bold;">:foo</span></span></code> database connection):</p>
<div class="codecolorer-container ruby twitlight" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br />3<br />4<br /></div></td><td><div class="ruby codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">Post.<span style="color:#9900CC;">on_db</span><span style="color:#006600; font-weight:bold;">&#40;</span><span style="color:#ff3333; font-weight:bold;">:foo</span><span style="color:#006600; font-weight:bold;">&#41;</span>.<span style="color:#9900CC;">published</span>.<span style="color:#9900CC;">with_comments</span>.<span style="color:#9900CC;">spam_marked</span>.<span style="color:#9900CC;">count</span><br />
Post.<span style="color:#9900CC;">published</span>.<span style="color:#9900CC;">on_db</span><span style="color:#006600; font-weight:bold;">&#40;</span><span style="color:#ff3333; font-weight:bold;">:foo</span><span style="color:#006600; font-weight:bold;">&#41;</span>.<span style="color:#9900CC;">with_comments</span>.<span style="color:#9900CC;">spam_marked</span>.<span style="color:#9900CC;">count</span><br />
Post.<span style="color:#9900CC;">published</span>.<span style="color:#9900CC;">with_comments</span>.<span style="color:#9900CC;">on_db</span><span style="color:#006600; font-weight:bold;">&#40;</span><span style="color:#ff3333; font-weight:bold;">:foo</span><span style="color:#006600; font-weight:bold;">&#41;</span>.<span style="color:#9900CC;">spam_marked</span>.<span style="color:#9900CC;">count</span><br />
Post.<span style="color:#9900CC;">published</span>.<span style="color:#9900CC;">with_comments</span>.<span style="color:#9900CC;">spam_marked</span>.<span style="color:#9900CC;">on_db</span><span style="color:#006600; font-weight:bold;">&#40;</span><span style="color:#ff3333; font-weight:bold;">:foo</span><span style="color:#006600; font-weight:bold;">&#41;</span>.<span style="color:#9900CC;">count</span></div></td></tr></tbody></table></div>
<p>And now, add this feature to our associations support and here is what we could do:</p>
<div class="codecolorer-container ruby twitlight" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br />3<br /></div></td><td><div class="ruby codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap"><span style="color:#0066ff; font-weight:bold;">@user</span>.<span style="color:#9900CC;">on_db</span><span style="color:#006600; font-weight:bold;">&#40;</span><span style="color:#ff3333; font-weight:bold;">:archive</span><span style="color:#006600; font-weight:bold;">&#41;</span>.<span style="color:#9900CC;">posts</span>.<span style="color:#9900CC;">published</span>.<span style="color:#9900CC;">all</span><br />
<span style="color:#0066ff; font-weight:bold;">@user</span>.<span style="color:#9900CC;">posts</span>.<span style="color:#9900CC;">on_db</span><span style="color:#006600; font-weight:bold;">&#40;</span><span style="color:#ff3333; font-weight:bold;">:olap</span><span style="color:#006600; font-weight:bold;">&#41;</span>.<span style="color:#9900CC;">published</span>.<span style="color:#9900CC;">count</span><br />
<span style="color:#0066ff; font-weight:bold;">@user</span>.<span style="color:#9900CC;">posts</span>.<span style="color:#9900CC;">published</span>.<span style="color:#9900CC;">on_db</span><span style="color:#006600; font-weight:bold;">&#40;</span><span style="color:#ff3333; font-weight:bold;">:foo</span><span style="color:#006600; font-weight:bold;">&#41;</span>.<span style="color:#9900CC;">first</span></div></td></tr></tbody></table></div>
<h2>Documentation</h2>
<p>For more information on the plugin internals, please check out the source code. All the plugin&#8217;s code is ~100% covered with a tests that were placed in a separate staging rails project located at <a href="http://github.com/kovyrin/db-charmer-sandbox">github</a>. The project has unit tests for all or at least the most of the parts of plugin&#8217;s code.</p>
<h2>What Ruby and Rails implementations does it work for?</h2>
<p>We have a continuous integration setups for this plugin on MRI 1.8.6 with Rails 2.2 and 2.3. We use the plugin in production on <a href="http://www.scribd.com">Scribd.com</a> with MRI (rubyee) 1.8.6 and Rails 2.2.</p>
<h2>Who are the authors?</h2>
<p>This plugin has been created in Scribd.com for our internal use and then the sources were opened for other people to use. All the code in this package has been developed by <a href="http://kovyrin.net">Alexey Kovyrin</a> for Scribd.com and is released under the MIT license. For more details, see the LICENSE file.</p>
<hr/>
<p>If you have any comments on this project, feel free to contact me here in comments or by email. And, of course, patches are welcome (only when covered with tests).</p>
]]></content:encoded>
			<wfw:commentRss>http://kovyrin.net/2009/11/03/db-charmer-activerecord-connection-magic-plugin/feed/</wfw:commentRss>
		<slash:comments>11</slash:comments>
		</item>
		<item>
		<title>Advanced Squid Caching in Scribd: Logged In Users and Complex URLs Handling</title>
		<link>http://kovyrin.net/2009/07/21/advanced-squid-caching-scribd-logged-in-users-complex-urls/</link>
		<comments>http://kovyrin.net/2009/07/21/advanced-squid-caching-scribd-logged-in-users-complex-urls/#comments</comments>
		<pubDate>Tue, 21 Jul 2009 22:18:57 +0000</pubDate>
		<dc:creator>Alexey Kovyrin</dc:creator>
				<category><![CDATA[Admin-tips]]></category>
		<category><![CDATA[Development]]></category>
		<category><![CDATA[My Projects]]></category>
		<category><![CDATA[cache]]></category>
		<category><![CDATA[caching]]></category>
		<category><![CDATA[Nginx]]></category>
		<category><![CDATA[rewrite]]></category>
		<category><![CDATA[scribd]]></category>
		<category><![CDATA[squid]]></category>

		<guid isPermaLink="false">http://kovyrin.net/?p=251</guid>
		<description><![CDATA[It&#8217;s been a while since I&#8217;ve posted my first post about the way we do document pages caching in Scribd and this approach has definitely proven to be really effective since then. In the second post of this series I&#8217;d like to explain how we handle our complex document URLs and logged in users in [...]]]></description>
			<content:encoded><![CDATA[<p>It&#8217;s been a while since I&#8217;ve posted <a href="http://kovyrin.net/2008/10/25/advanced-squid-caching-for-rails-applications-preface/">my first post about the way we do document pages caching in Scribd</a> and this approach has definitely proven to be really effective since then. In the second post of this series I&#8217;d like to explain how we handle our complex document URLs and logged in users in the caching architecture.</p>
<p>First of all, let&#8217;s take a look at a typical Scribd&#8217;s document URL: <a href="http://www.scribd.com/doc/1/Improved-Statistical-Test">http://www.scribd.com/doc/1/Improved-Statistical-Test</a>. </p>
<p>As we can see, it consists of a document-specific part (<tt><strong>/doc/1</strong></tt>) and a non-unique human-readable slug part (<tt><strong>/Improved-Statistical-Test</strong></tt>). When a user comes to the site with a wrong slug in the document URL, we need to make sure we send the user to the correct URL with a permanent HTTP 301 redirect. So, obviously we can&#8217;t simply send our requests to the squid because it&#8217;d cause few problems:</p>
<ul>
<li>When we change document&#8217;s title, we&#8217;d create a new cached item and would not be able to redirect users from the old URL to the new one</li>
<li>When we change a title, we&#8217;d pollute cache with additional document page copies.</li>
</ul>
<p>One more problem that makes the situation even worse &#8211; we have 3 different kinds of users on the site:</p>
<ol>
<li><strong>Logged in users</strong> &#8211; active web site users that are logged in and should see their name at the top of the page, should see all kinds of customized parts of the page, etc (especially when a page is their own document).</li>
<li><strong>Anonymous users</strong> &#8211; all users that are not logged in and visit the site with a flash-enabled browser</li>
<li><strong>Bots</strong> &#8211; all kinds of crawlers that can&#8217;t read flash content and need to see a plain text document version</li>
</ol>
<p>All three kinds of users should see their own document page versions whether the page is cached or not.</p>
<p><span id="more-251"></span></p>
<p>So, how do we solve these two problems? Here is how.</p>
<p>First of all, to fix the URLs problem we&#8217;ve decided to rewrite the URL before it goes to a squid server. We change URLs to look like this: <tt><strong>http://www.scribd.com/doc/1?__enable_docview_caching=1</strong></tt>. This makes the document URL dependent only on a unique document ID that never change and sends an additional parameter to the backend to signal that the page could potentially be cached. The slug is sent to backend using an HTTP-header (<strong><tt>X-Scribd-Slug</tt></strong>) so that backend could check the slug and return a redirect if needed. </p>
<p>To make sure we won&#8217;t respond with a cached page to a request with an invalid URL (invalid slug basically), we use <strong><tt>Vary: X-Scribd-Slug</tt></strong> HTTP header which is implemented in Squid (only late 2.6 and 2.7) and makes it check specified headers in a request before responding with a cached content. If the header of the cached content is different then the header in the request, squid proxies the resuest to backend where we could handle the cache miss as we want.</p>
<p>Next, to resolve the users problem we&#8217;ve created a small nginx module that looking at a request headers could tell you whether the user is a bot or not and whether he&#8217;s logged in or an anonymous visitor. This module basically exposes a <strong><tt>$scribd_user_id</tt></strong> variable to our configs and we could use the variable to do separate configuration for different kinds of users. </p>
<p>At this point we do not cache document pages for logged in users so we basically have two copies of each page in the cache: flash-enabled document page and an inline document page. We do this separation by changing our cache URLs one more time: we add a scribd_user_id=$scribd_user_id variable (anonymous = 0, bot = -1) to the cache URL: <tt><strong>http://www.scribd.com/doc/1?__enable_docview_caching=1&#038;scribd_user_id=0</strong></tt>.</p>
<p>And last, not not least, we use two really awesome Squid features called <strong><tt>stale-while-revalidate</tt></strong> and <strong><tt>stale-if-error</tt></strong> (AFAIR, they were invented in Yahoo! and then <a href="http://www.mnot.net/blog/2007/12/12/stale">described by their squid admin</a>). </p>
<p>Option <strong><tt>stale-while-revalidate</tt></strong> allow us to quickly serve content from the cache while doing background re-validation requests to the Rails backend. Option <strong><tt>stale-if-error</tt></strong> basically allows us to serve content from the squid cache when Rails backend is down/dead/slow. </p>
<p></p>
<p>All these changes allowed us to handle more traffic with less hardware and what is even more important, they helped us improve user experience with the site: response times dropped 2-3 fold and much less people see our Ouch! pages (HTTP 50x errors when backend is dead or overloaded). Here is an example of one of our servers&#8217; hit ratio and traffic savings daily graphs:</p>
<p><a href="http://kovyrin.net/wp-content/uploads/2009/07/graph_image1.png" rel="shadowbox[post-251];player=img;"><img src="http://kovyrin.net/wp-content/uploads/2009/07/graph_image1-300x125.png" alt="graph_image" title="graph_image" width="300" height="125" class="aligncenter size-medium wp-image-291" /></a></p>
<p><a href="http://kovyrin.net/wp-content/uploads/2009/07/traffic_savings.png" rel="shadowbox[post-251];player=img;"><img src="http://kovyrin.net/wp-content/uploads/2009/07/traffic_savings-300x139.png" alt="traffic_savings" title="traffic_savings" width="300" height="139" class="aligncenter size-medium wp-image-293" /></a></p>
<p>This it with the logged in users and complex URLs handling in Scribd caching architecture. Next post in this series will explain how we do cache invalidation in Scribd. Stay tuned.</p>
]]></content:encoded>
			<wfw:commentRss>http://kovyrin.net/2009/07/21/advanced-squid-caching-scribd-logged-in-users-complex-urls/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>The Blog v.2.0</title>
		<link>http://kovyrin.net/2009/07/20/the-blog-v-2-0/</link>
		<comments>http://kovyrin.net/2009/07/20/the-blog-v-2-0/#comments</comments>
		<pubDate>Mon, 20 Jul 2009 02:19:51 +0000</pubDate>
		<dc:creator>Alexey Kovyrin</dc:creator>
				<category><![CDATA[Blog]]></category>
		<category><![CDATA[My Projects]]></category>
		<category><![CDATA[design]]></category>
		<category><![CDATA[history]]></category>
		<category><![CDATA[site]]></category>

		<guid isPermaLink="false">http://kovyrin.net/?p=243</guid>
		<description><![CDATA[Long time ago, in 2002 I decided to create my own point of presence in the Internet. Back then I&#8217;ve got pretty nice domain (scoundrel.kremenchug.net), hacked up a few pages on php, added a guestbook and that was it. Many years it was almost static and I did a few updates on my resume page [...]]]></description>
			<content:encoded><![CDATA[<p>Long time ago, in 2002 I decided to create my own point of presence in the Internet. Back then I&#8217;ve got pretty nice domain (<a href="http://web.archive.org/web/20041103085421/scoundrel.kremenchug.net/about.php?lang=en">scoundrel.kremenchug.net</a>), hacked up a few pages on php, added a guestbook and that was it. Many years it was almost static and I did a few updates on my resume page few times a year. Later I&#8217;ve switched the site to wordpress to make it easier to manage <a href="http://kovyrin.net/resume/">my resume</a> and <a href="http://kovyrin.net/projects/">stuff</a>&#8230; </p>
<p>And 3 years ago in March 2006 I&#8217;ve decided to start my own blog. I took a <a href="http://www.vanillamist.com/blog/?page_id=64">standard template</a> and started the blog on a separate domain while the domain was on its own domain name&#8230; This spring <a href="http://kovyrina.info">my wife</a> made me a great birthday present &#8211; she&#8217;s created me a <a href="http://kovyrina.info/portfolio/homo-adminus-blog-theme/">custom blog design</a> that has all the stuff I wanted from my own web site for a long time. My friend <a href="http://kpumuk.info">Dima Shteflyuk</a> has helped me with creating a wordpress template from Tanya&#8217;s mockups and here we are &#8211; now I&#8217;ve decided to merge my blog and my web site into a single web entity called <a href="http://kovyrin.net">http://kovyrin.net/</a>. Welcome to my new blog/site/whatever!</p>
]]></content:encoded>
			<wfw:commentRss>http://kovyrin.net/2009/07/20/the-blog-v-2-0/feed/</wfw:commentRss>
		<slash:comments>8</slash:comments>
		</item>
		<item>
		<title>Loops plugin for rails and merb released</title>
		<link>http://kovyrin.net/2009/02/17/loops-plugin-for-rails-and-merb-released/</link>
		<comments>http://kovyrin.net/2009/02/17/loops-plugin-for-rails-and-merb-released/#comments</comments>
		<pubDate>Tue, 17 Feb 2009 03:41:30 +0000</pubDate>
		<dc:creator>Alexey Kovyrin</dc:creator>
				<category><![CDATA[Development]]></category>
		<category><![CDATA[Links]]></category>
		<category><![CDATA[My Projects]]></category>
		<category><![CDATA[github]]></category>
		<category><![CDATA[plugin]]></category>
		<category><![CDATA[project]]></category>
		<category><![CDATA[rails]]></category>
		<category><![CDATA[Ruby]]></category>
		<category><![CDATA[Ruby On Rails]]></category>
		<category><![CDATA[scribd]]></category>

		<guid isPermaLink="false">http://blog.kovyrin.net/?p=186</guid>
		<description><![CDATA[loops is a small and lightweight framework for Ruby on Rails and Merb created to support simple background loops in your application which are usually used to do some background data processing on your servers (queue workers, batch tasks processors, etc). Originally loops plugin was created to make our (Scribd.com) own loops code more organized. [...]]]></description>
			<content:encoded><![CDATA[<p><a href="http://github.com/kovyrin/loops/tree/master">loops</a> is a small and lightweight framework for Ruby on Rails and Merb created to support simple background loops in your application which are usually used to do some background data processing on your servers (queue workers, batch tasks processors, etc). </p>
<p>Originally <a href="http://github.com/kovyrin/loops/tree/master">loops plugin</a> was created to make our (<a href="http://www.scribd.com">Scribd.com</a>) own loops code more organized. We used to have tens of different modules with methods that were called with script/runner and then used with nohup and other not so convenient backgrounding techniques. When you have such a number of loops/workers to run in background it becomes a nightmare to manage them on a regular basis (restarts, code upgrades, status/health checking, etc).</p>
<p>After a short time of writing our loops in more organized ways we were able to generalize most of the loops code so now our loops look like a classes with a single mandatory public method called run. Everything else (spawning many workers, managing them, logging, backgrounding, pid-files management, etc) is handled by the plugin itself. </p>
<p>The major idea behind this small project was to create a deadly simple and yet robust framework to be able to run some tasks in background and do not think about spawning many workers, restarting them when they die, etc. So, if you need to be able to run either one or many copies of your worker or you do not want to think about re-spawning dead workers and do not want to spend megabytes of RAM on separate copies of Ruby interpreter (when you run each copy of your loop as a separate process controlled by monit/god/etc), then I’d recommend you to try this framework — you&#8217;ll like it. </p>
<p>For more information, visit the <a href="http://github.com/kovyrin/loops/tree/master">project site</a> and, of course, read the sources <img src='http://kovyrin.net/wp-includes/images/smilies/icon_smile.gif' alt=':-)' class='wp-smiley' /> </p>
]]></content:encoded>
			<wfw:commentRss>http://kovyrin.net/2009/02/17/loops-plugin-for-rails-and-merb-released/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>ActiveMQ + Ruby Stomp Client: How to process elements one by one</title>
		<link>http://kovyrin.net/2008/10/30/activemq-ruby-stomp-client-how-to-process-elements-one-by-one/</link>
		<comments>http://kovyrin.net/2008/10/30/activemq-ruby-stomp-client-how-to-process-elements-one-by-one/#comments</comments>
		<pubDate>Thu, 30 Oct 2008 04:42:39 +0000</pubDate>
		<dc:creator>Alexey Kovyrin</dc:creator>
				<category><![CDATA[Admin-tips]]></category>
		<category><![CDATA[Databases]]></category>
		<category><![CDATA[Development]]></category>
		<category><![CDATA[My Projects]]></category>
		<category><![CDATA[activemq]]></category>
		<category><![CDATA[database]]></category>
		<category><![CDATA[development]]></category>
		<category><![CDATA[MySQL]]></category>
		<category><![CDATA[performance]]></category>
		<category><![CDATA[queue]]></category>
		<category><![CDATA[Ruby]]></category>
		<category><![CDATA[scalability]]></category>
		<category><![CDATA[scribd]]></category>
		<category><![CDATA[stomp]]></category>

		<guid isPermaLink="false">http://blog.kovyrin.net/?p=135</guid>
		<description><![CDATA[Few months ago I&#8217;ve switched one of our internal projects from doing synchronous database saves of analytics data to an asynchronous processing using starling + a pool of workers. This was the day when I really understood the power of specialized queue servers. I was using database (mostly, MySQL) for this kind of tasks for [...]]]></description>
			<content:encoded><![CDATA[<p>Few months ago I&#8217;ve switched one of <a href="http://www.scribd.com">our</a> internal projects from doing synchronous database saves of analytics data to an asynchronous processing using <a href="http://github.com/starling/starling/tree/master">starling</a> + a pool of workers. This was the day when I really understood the power of specialized queue servers. I was using database (mostly, MySQL) for this kind of tasks for years and sometimes (especially under a highly concurrent load) it worked not so fast&#8230; Few times I worked with some queue servers, but those were either some small tasks or I didn&#8217;t have a time to really get the idea, that specialized queue servers were created just to do these tasks quickly and efficiently.</p>
<p>All this time (few months now) I was using starling noticed really bad thing in how it works: if workers die (really die, or lock on something for a long time, or just start lagging) and queue start growing, the thing could kill your server and you won&#8217;t be able to do something about it &#8211; it just eats all your memory and this is it. Since then I&#8217;ve started looking for a better solution for our queuing, the technology was too cool to give up. I&#8217;ve tried 5 or 6 different popular solutions and all of them sucked&#8230; They ALL had the same problem &#8211; if your queue grows, this is your problem and not queue broker&#8217;s :-/ The last solution I&#8217;ve tested was <a href="http://activemq.apache.org/">ActiveMQ</a> and either I wasn&#8217;t able to push it to its limits or it is really so cool, but looks like it does not have this memory problem. So, we&#8217;ve started using it recently.</p>
<p>In this small post I&#8217;d like to describe a few things that took me pretty long to figure out in <a href="http://github.com/grempe/stomp/tree/master">ruby Stomp client</a>: how to make queues persistent (really!) and how to process elements one by one with clients&#8217; acknowledgments.<br />
<span id="more-135"></span><br />
First, we need to connect to the queue server and this code is the same for clients and servers:</p>
<div class="codecolorer-container ruby twitlight" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br />3<br /></div></td><td><div class="ruby codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">client = <span style="color:#6666ff; font-weight:bold;">Stomp::Client</span>.<span style="color:#CC0066; font-weight:bold;">open</span> <span style="color:#996600;">&quot;stomp://localhost:61613&quot;</span><br />
<span style="color:#9966CC; font-weight:bold;">or</span><br />
client = <span style="color:#6666ff; font-weight:bold;">Stomp::Client</span>.<span style="color:#CC0066; font-weight:bold;">open</span><span style="color:#006600; font-weight:bold;">&#40;</span>login, password, host, port, reliable<span style="color:#006600; font-weight:bold;">&#41;</span></div></td></tr></tbody></table></div>
<p>Second form looks better for me because it allows you to specify if you want client to be reliable (lock on errors and try to reconnect, etc) or just want client to raise an error in case of any problems.</p>
<p>When you&#8217;ve connected to the server, you can push your data to a queue:</p>
<div class="codecolorer-container ruby twitlight" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br /></div></td><td><div class="ruby codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">client.<span style="color:#9900CC;">send</span><span style="color:#006600; font-weight:bold;">&#40;</span><span style="color:#996600;">'/queue/some_queue'</span>, <span style="color:#996600;">&quot;hello world&quot;</span>, headers<span style="color:#006600; font-weight:bold;">&#41;</span></div></td></tr></tbody></table></div>
<p>And this is where you have an ability to specify if you want your data to be persistent (survive server crashes, etc): <tt>headers</tt> is a hash that could have an element <tt>:persistent => true</tt>, which would do the thing. So, your code would looks like this (for example):</p>
<div class="codecolorer-container ruby twitlight" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br /></div></td><td><div class="ruby codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">client.<span style="color:#9900CC;">send</span><span style="color:#006600; font-weight:bold;">&#40;</span><span style="color:#996600;">'/queue/some_queue'</span>, <span style="color:#996600;">&quot;hello world&quot;</span>, <span style="color:#ff3333; font-weight:bold;">:persistent</span> <span style="color:#006600; font-weight:bold;">=&gt;</span> <span style="color:#0000FF; font-weight:bold;">true</span><span style="color:#006600; font-weight:bold;">&#41;</span><br />
client.<span style="color:#9900CC;">close</span> <span style="color:#008000; font-style:italic;"># this is needed only in your push code</span></div></td></tr></tbody></table></div>
<p>Now, when you have your data submitted to the queue, you need to be able to read it and process with some script. This is as simple as the following code:</p>
<div class="codecolorer-container ruby twitlight" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br />3<br />4<br />5<br />6<br /></div></td><td><div class="ruby codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap"><span style="color:#008000; font-style:italic;"># Processing loop</span><br />
client.<span style="color:#9900CC;">subscribe</span><span style="color:#006600; font-weight:bold;">&#40;</span><span style="color:#996600;">'/queue/some_queue'</span>, headers<span style="color:#006600; font-weight:bold;">&#41;</span> <span style="color:#9966CC; font-weight:bold;">do</span> <span style="color:#006600; font-weight:bold;">|</span>msg<span style="color:#006600; font-weight:bold;">|</span><br />
&nbsp; <span style="color:#008000; font-style:italic;"># Process your message here</span><br />
&nbsp; <span style="color:#008000; font-style:italic;"># Your submitted data is in msg.body</span><br />
<span style="color:#9966CC; font-weight:bold;">end</span><br />
client.<span style="color:#9900CC;">join</span> <span style="color:#008000; font-style:italic;"># Wait until listening thread dies</span></div></td></tr></tbody></table></div>
<p>Again, we wanted to receive messages one by one and acknowledge successful processing in our code. This is simple too. You need:</p>
<ul>
<li>Pass <tt>:ack => :client</tt> as an element in your <tt>headers</tt> hash</li>
<li>Call <tt>client.acknowledge(msg)</tt> in the loop if you&#8217;re sure that an element could be removed from the queue</li>
</ul>
<p>This is basically it with the stuff I wanted to explain today. If you&#8217;ve never tried to work with any queue servers, try today and maybe tomorrow you won&#8217;t be able to imagine your systems architecture without such a component <img src='http://kovyrin.net/wp-includes/images/smilies/icon_smile.gif' alt=':-)' class='wp-smiley' /> </p>
]]></content:encoded>
			<wfw:commentRss>http://kovyrin.net/2008/10/30/activemq-ruby-stomp-client-how-to-process-elements-one-by-one/feed/</wfw:commentRss>
		<slash:comments>7</slash:comments>
		</item>
		<item>
		<title>Advanced Squid Caching for Rails Applications: Preface</title>
		<link>http://kovyrin.net/2008/10/25/advanced-squid-caching-for-rails-applications-preface/</link>
		<comments>http://kovyrin.net/2008/10/25/advanced-squid-caching-for-rails-applications-preface/#comments</comments>
		<pubDate>Sat, 25 Oct 2008 21:28:30 +0000</pubDate>
		<dc:creator>Alexey Kovyrin</dc:creator>
				<category><![CDATA[Databases]]></category>
		<category><![CDATA[Development]]></category>
		<category><![CDATA[My Projects]]></category>
		<category><![CDATA[Networks]]></category>
		<category><![CDATA[caching]]></category>
		<category><![CDATA[http]]></category>
		<category><![CDATA[memcache]]></category>
		<category><![CDATA[MySQL]]></category>
		<category><![CDATA[performance]]></category>
		<category><![CDATA[scalability]]></category>
		<category><![CDATA[scribd]]></category>
		<category><![CDATA[squid]]></category>

		<guid isPermaLink="false">http://blog.kovyrin.net/?p=131</guid>
		<description><![CDATA[Since the day one when I joined Scribd, I was thinking about the fact that 90+% of our traffic is going to the document view pages, which is a single action in our documents controller. I was wondering how could we improve this action responsiveness and make our users happier. Few times I was creating [...]]]></description>
			<content:encoded><![CDATA[<p>Since the day one when <a href="http://kovyrin.info/2008/07/23/new-job-scalability-expert-in-scribdcom/">I joined Scribd</a>, I was thinking about the fact that 90+% of our traffic is going to the document view pages, which is a single action in our documents controller. I was wondering how could we improve this action responsiveness and make our users happier.</p>
<p>Few times I was creating a git branches and hacking this action trying to implement some sort of page-level caching to make things faster. But all the time results weren&#8217;t as good as I&#8217;d like them to be. So, branches were sitting there and waiting for a better idea.<br />
<span id="more-131"></span><br />
Few months ago <a href="http://kpumuk.info">my good friend</a> has joined Scribd and we&#8217;ve started thinking on this problem together. As the result of our brainstorming we&#8217;ve managed to figure out what were the problems preventing us from doing efficient caching:</p>
<ul>
<li>First of all, a lots of code in the action is changing the page view if our visitor is a bot (no, not a cloaking, just some minor adjustments of the view).</li>
<li>Second problem was a set of differences in the view for anonymous and logged in users.</li>
<li>And finally, third problem was the fact that the page has a few blocks that change pretty dynamically: document stats pane and comments lists.</li>
</ul>
<p>All these problems when combined were creating a lots of pain when I was trying to cache a whole page. When we&#8217;ve figured them out, we&#8217;ve started thinking on how could we generalize possible combinations of those factors and possible approaches to caching.</p>
<p>There is a well known idea in web applications development: the fastest web app action is an action that does not require any code to be executed on your application server. So, first idea we&#8217;ve tried to think about was some approach that would definitely reduce the number of hits on our app servers. This idea was based on HTTP protocol features related to <strong>Last-Modified</strong> and <strong>E-Tag</strong> headers. But there was a problem &#8211; not so many users go to the same page twice so even if we&#8217;d make the page cacheable, it wouldn&#8217;t help too much. But the idea of full page caching outside of the application was really good and we&#8217;ve started playing with it to figure out how to use it in production.</p>
<p>Long time ago, when Internet was slow and expensive many ISPs and large companies were trying to reduce their traffic w/o hurting users&#8217; experience. Then <a href="http://www.squid-cache.org/">caching proxy servers</a> were born. The idea of those servers was to handle all web requests going from a network (ISP or a company office) and try to cache as much content as possible so when the same or some other user would request a cached page, proxy server would return it really fast. If we&#8217;d implement support for those Last-Modified headers, all proxy servers would be happy to cache our pages. But there was a problem &#8211; no one uses caching proxies in 2008 <img src='http://kovyrin.net/wp-includes/images/smilies/icon_smile.gif' alt=':-)' class='wp-smiley' />  So, we&#8217;ve got an idea &#8211; why can&#8217;t we place such a server in front of our application and make it cache content for all users in the world? (Yes, we knew about a caching reverse proxies before &#8211; I&#8217;m just trying to explain the flow of our thoughts and words when we were brainstorming the problem).</p>
<p>The only problem with this approach would be to differentiate logged in users, anonymous users and bots. Considering the fact that our proxy server could be placed between the app and our web servers (nginx), we&#8217;ve decided to create a nginx module that would translate the same document page URLs to a set of URLs, which would be different for all those 3 kinds of users.</p>
<p>When all those problems with different kinds of users were solved, we&#8217;ve decided to solve the last one &#8211; non-cacheable dynamic stats pane. The solution was pretty simple &#8211; we&#8217;ve added a small ajax call to the page which would update stats on the cached version of our page for all real users while bots will see the same page, but with a bit stale stats pane.</p>
<p>Long story short, the results is really great. Application servers load reduced by 50-70%, database servers  load is reduced by 30-60%, response times dropped down to 150-200 msec from 500-750 msec. As an additional positive effect of the caching we&#8217;ve managed to remove all fragments caches from the application and free more of memcached resources for data caches. Here are a few cacti graphs of our servers load/traffic (the caching was introduced on Oct 9th at night):</p>
<p><strong>Main MySQL command counters:</strong></p>
<div class="thumbnail"><a href="http://skitch.com/kovyrin/2jq2/cacti"><img src="http://img.skitch.com/20081025-kkeqfuqp87w22fgpqwpa99ri4f.preview.jpg" alt="Cacti" /></a><br /><span style="font-family: Lucida Grande, Trebuchet, sans-serif, Helvetica, Arial; font-size: 10px; color: #808080">Uploaded with <a href="http://plasq.com/">plasq</a>&#8216;s <a href="http://skitch.com">Skitch</a>!</span></div>
<p><br/></p>
<p><strong>One of our Application Servers CPU Usage:</strong></p>
<div class="thumbnail"><a href="http://skitch.com/kovyrin/2jqh/cacti"><img src="http://img.skitch.com/20081025-be4571a18h2ijh75idgipsw4u1.preview.jpg" alt="Cacti" /></a><br /><span style="font-family: Lucida Grande, Trebuchet, sans-serif, Helvetica, Arial; font-size: 10px; color: #808080">Uploaded with <a href="http://plasq.com/">plasq</a>&#8216;s <a href="http://skitch.com">Skitch</a>!</span></div>
<p><br/></p>
<p><strong>One of our Application Servers Load Average:</strong></p>
<div class="thumbnail"><a href="http://skitch.com/kovyrin/2jxy/cacti"><img src="http://img.skitch.com/20081025-mdjxgquferw1tdwrqecssbafby.preview.jpg" alt="Cacti" /></a><br /><span style="font-family: Lucida Grande, Trebuchet, sans-serif, Helvetica, Arial; font-size: 10px; color: #808080">Uploaded with <a href="http://plasq.com/">plasq</a>&#8216;s <a href="http://skitch.com">Skitch</a>!</span></div>
<p><br/></p>
<p>Unfortunately there are a lot of things to share related to this caching experience, so I&#8217;ve decided to make a series of posts that would explain all the problems we had and solutions we&#8217;ve found for each of the following parts of the caching system:</p>
<ul>
<li><a href="http://kovyrin.net/2009/07/21/advanced-squid-caching-scribd-logged-in-users-complex-urls/">Logged in Users and Complex URLs Handling</a></li>
<li>Rails code to support Last-Modified headers and how we purge caches</li>
<li>Squid Server setup (configs and hardware)</li>
<li>Nginx module development</li>
</ul>
<p>So, if you&#8217;re interested in details, subscribe to this blog&#8217;s <a href="http://feeds.feedburner.com/Homo-Adminus">RSS feed</a> and in a few days you&#8217;ll see the first article from this series.</p>
]]></content:encoded>
			<wfw:commentRss>http://kovyrin.net/2008/10/25/advanced-squid-caching-for-rails-applications-preface/feed/</wfw:commentRss>
		<slash:comments>10</slash:comments>
		</item>
		<item>
		<title>Bounces-handler Released</title>
		<link>http://kovyrin.net/2008/08/03/bounce-handler-released/</link>
		<comments>http://kovyrin.net/2008/08/03/bounce-handler-released/#comments</comments>
		<pubDate>Sun, 03 Aug 2008 08:46:05 +0000</pubDate>
		<dc:creator>Alexey Kovyrin</dc:creator>
				<category><![CDATA[Development]]></category>
		<category><![CDATA[My Projects]]></category>
		<category><![CDATA[Networks]]></category>
		<category><![CDATA[email]]></category>
		<category><![CDATA[rails]]></category>
		<category><![CDATA[release]]></category>
		<category><![CDATA[Ruby]]></category>
		<category><![CDATA[Ruby On Rails]]></category>
		<category><![CDATA[scribd]]></category>
		<category><![CDATA[spam]]></category>

		<guid isPermaLink="false">http://blog.kovyrin.net/?p=130</guid>
		<description><![CDATA[Today I&#8217;ve managed to finish initial version of our bounces-handler package we use for mailing-related stuff in Scribd. Bounces-handler package is a simple set of scripts to automatically process email bounces and ISP‘s feedback loops emails, maintain your mailing blacklists and a Rails plugin to use those blacklists in your RoR applications. This piece of [...]]]></description>
			<content:encoded><![CDATA[<p>Today I&#8217;ve managed to finish <a href="http://github.com/kovyrin/bounces-handler/tree/master">initial version of our bounces-handler package</a> we use for mailing-related stuff in <a href="http://www.scribd.com">Scribd</a>. </p>
<p>Bounces-handler package is a simple set of scripts to automatically process email bounces and ISP‘s feedback loops emails, maintain your mailing blacklists and a Rails plugin to use those blacklists in your RoR applications.</p>
<p>This piece of software has been developed as a part of more global work on mailing quality improvement in Scribd.com, but it was one of the most critical steps after setting up reverse DNS records, DKIM and SPF. </p>
<p>The <a href="http://github.com/kovyrin/bounces-handler/tree/master">package</a> itself consists of two parts:</p>
<ul>
<li>Perl scripts to process incoming email:
<ul>
<li>bounces processor — could be assigned to process all your bounce emails</li>
<li>feedback loops messages processor — more specific for Scribd, but still &#8211; could be modified for your needs (will be released soon).</li>
</ul>
</li>
<li>Rails plugin to work with mailing blacklists</li>
</ul>
<p>For more information, please check our <a href="http://github.com/kovyrin/bounces-handler/tree/master/README.rdoc">README file</a>. If you have any questions, comments or suggestions, please leave them here as a comments and I&#8217;ll try to reply as soon as possible.</p>
]]></content:encoded>
			<wfw:commentRss>http://kovyrin.net/2008/08/03/bounce-handler-released/feed/</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
	</channel>
</rss>
