<?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, 02 Feb 2010 22:34:00 +0000</lastBuildDate>
	<generator>http://wordpress.org/?v=2.9.1</generator>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
			<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>Scoundrel</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 connections to a [...]]]></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> git:<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> 5<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>Scoundrel</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"><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"><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>Scoundrel</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>Scoundrel</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>Scoundrel</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>Scoundrel</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 a [...]]]></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>&#8217;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>&#8217;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>&#8217;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>Scoundrel</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 software [...]]]></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>
		<item>
		<title>Using Sphinx for Non-Fulltext Queries</title>
		<link>http://kovyrin.net/2008/05/19/using-sphinx-for-non-fulltext-queries/</link>
		<comments>http://kovyrin.net/2008/05/19/using-sphinx-for-non-fulltext-queries/#comments</comments>
		<pubDate>Mon, 19 May 2008 16:18:54 +0000</pubDate>
		<dc:creator>Scoundrel</dc:creator>
				<category><![CDATA[Admin-tips]]></category>
		<category><![CDATA[Databases]]></category>
		<category><![CDATA[Development]]></category>
		<category><![CDATA[My Projects]]></category>
		<category><![CDATA[full-text]]></category>
		<category><![CDATA[index]]></category>
		<category><![CDATA[MySQL]]></category>
		<category><![CDATA[optimization]]></category>
		<category><![CDATA[Ruby]]></category>
		<category><![CDATA[scalability]]></category>
		<category><![CDATA[scribd]]></category>
		<category><![CDATA[sphinx]]></category>

		<guid isPermaLink="false">http://blog.kovyrin.net/2008/05/19/using-sphinx-for-non-fulltext-queries/</guid>
		<description><![CDATA[How often do you think about the reasons why your favorite RDBMS sucks?   Last few months I was doing this quite often and yes, my favorite RDBMS is MySQL. The reason why I was thinking so because one of my recent tasks at Scribd was fixing scalability problems in documents browsing. 
The problem [...]]]></description>
			<content:encoded><![CDATA[<p>How often do you think about the reasons why your favorite RDBMS sucks? <img src='http://kovyrin.net/wp-includes/images/smilies/icon_smile.gif' alt=':-)' class='wp-smiley' />  Last few months I was doing this quite often and yes, my favorite RDBMS is MySQL. The reason why I was thinking so because one of my recent tasks at <a href="http://www.scribd.com">Scribd</a> was fixing scalability problems in documents browsing. </p>
<p>The problem with browsing was pretty simple to describe and as hard to fix &#8211; we have large data set which consists of a few tables with many fields with really bad selectivity (flag fields like is_deleted, is_private, etc; file_type, language_id , category_id and others). As the result of this situation it becomes really hard (if possible at all) to display documents lists like &#8220;<a href="http://www.scribd.com/browse?c=123&#038;ft=pdf&#038;l=8&#038;p=1&#038;page=1&#038;t=popular">most popular 1-10 pages PDF documents in Italian language from the category &#8220;Business&#8221;</a> (of course, non-deleted, non-private, etc). If you&#8217;ll try to create appropriate indexes for each possible filters combination, you&#8217;ll end up having tens or hundreds of indexes and every INSERT query in your tables will take ages.</p>
<p><span id="more-125"></span></p>
<p>So, we were doing many weird things to solve this problem, like having really huge covering indexes for most popular browsing situations, having powerful (and expensive) dedicated servers for browsing queries, etc. Nothing helped and we ended up reducing filters number to 1-2 per query. But with continuously growing documents base we&#8217;d really love to help people find interesting content and filters is what we&#8217;d really want to add to the project.</p>
<p>Long story short, we&#8217;ve decided to try to use <a href="http://www.sphinxsearch.com/">Sphinx search engine</a> for really unusual thing &#8211; we decided to move all SELECT queries for our documents to a small (proof of concept) sphinx installation on one of our DB servers and use Sphinx&#8217; <a href="http://www.sphinxsearch.com/doc.html#matching-modes">full-scan feature</a>. After a few hours of development I&#8217;ve created a simple to use module which mimics ActiveRecord&#8217;s find method interface and allows us to perform queries like the following (sometimes even more powerful than AR):</p>
<div class="codecolorer-container ruby twitlight" style="overflow:auto;white-space:nowrap;border: 1px solid #9F9F9F;width:435px;height:300px;"><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 />15<br />16<br />17<br />18<br />19<br />20<br />21<br />22<br /></div></td><td><div class="ruby codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">browser = <span style="color:#6666ff; font-weight:bold;">Scribd::SphinxBrowser</span>.<span style="color:#9900CC;">new</span><span style="color:#006600; font-weight:bold;">&#40;</span><span style="color:#ff3333; font-weight:bold;">:host</span> <span style="color:#006600; font-weight:bold;">=&gt;</span> <span style="color:#996600;">'ip.add.re.ss'</span><span style="color:#006600; font-weight:bold;">&#41;</span><br />
<br />
<span style="color:#008000; font-style:italic;"># Get top 10 hottest docs</span><br />
result = browser.<span style="color:#9900CC;">browse</span><br />
<br />
<span style="color:#008000; font-style:italic;"># Get top 10 most commented docs this week</span><br />
result = browser.<span style="color:#9900CC;">browse</span><span style="color:#006600; font-weight:bold;">&#40;</span><span style="color:#ff3333; font-weight:bold;">:order</span> <span style="color:#006600; font-weight:bold;">=&gt;</span> <span style="color:#996600;">'comments_week DESC'</span><span style="color:#006600; font-weight:bold;">&#41;</span><br />
<br />
<span style="color:#008000; font-style:italic;"># Get top 5 most voted docs of all times</span><br />
result = browser.<span style="color:#9900CC;">browse</span><span style="color:#006600; font-weight:bold;">&#40;</span><span style="color:#ff3333; font-weight:bold;">:order</span> <span style="color:#006600; font-weight:bold;">=&gt;</span> <span style="color:#996600;">'votes_total DESC'</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 />
<br />
<span style="color:#008000; font-style:italic;"># Get top 10 most viewed docs for today (only docs with language_id = 5 returned)</span><br />
result = browser.<span style="color:#9900CC;">browse</span><span style="color:#006600; font-weight:bold;">&#40;</span><span style="color:#ff3333; font-weight:bold;">:order</span> <span style="color:#006600; font-weight:bold;">=&gt;</span> <span style="color:#996600;">'views_today DESC'</span>, <span style="color:#ff3333; font-weight:bold;">:filters</span> <span style="color:#006600; font-weight:bold;">=&gt;</span> <span style="color:#006600; font-weight:bold;">&#123;</span> <span style="color:#ff3333; font-weight:bold;">:language_id</span> <span style="color:#006600; font-weight:bold;">=&gt;</span> <span style="color:#006666;">5</span> <span style="color:#006600; font-weight:bold;">&#125;</span><span style="color:#006600; font-weight:bold;">&#41;</span><br />
<br />
<span style="color:#008000; font-style:italic;"># Get 10th to 19th hottest docs with language_id = 1, 2 or 3</span><br />
result = browser.<span style="color:#9900CC;">browse</span><span style="color:#006600; font-weight:bold;">&#40;</span><span style="color:#ff3333; font-weight:bold;">:filters</span> <span style="color:#006600; font-weight:bold;">=&gt;</span> <span style="color:#006600; font-weight:bold;">&#123;</span> <span style="color:#ff3333; font-weight:bold;">:language_id</span> <span style="color:#006600; font-weight:bold;">=&gt;</span> <span style="color:#006600; font-weight:bold;">&#91;</span>1, 2, 3<span style="color:#006600; font-weight:bold;">&#93;</span> <span style="color:#006600; font-weight:bold;">&#125;</span>, <span style="color:#ff3333; font-weight:bold;">:limit</span> <span style="color:#006600; font-weight:bold;">=&gt;</span> 10, <span style="color:#ff3333; font-weight:bold;">:offset</span> <span style="color:#006600; font-weight:bold;">=&gt;</span> <span style="color:#006666;">10</span><span style="color:#006600; font-weight:bold;">&#41;</span><br />
<br />
<span style="color:#008000; font-style:italic;"># Get top 10 hottest docs with language_id = 1 and pages count between 1 and 10 (excluding doc with ID = 5)</span><br />
result = browser.<span style="color:#9900CC;">browse</span><span style="color:#006600; font-weight:bold;">&#40;</span><span style="color:#ff3333; font-weight:bold;">:filters</span> <span style="color:#006600; font-weight:bold;">=&gt;</span> <span style="color:#006600; font-weight:bold;">&#123;</span> <span style="color:#ff3333; font-weight:bold;">:language_id</span> <span style="color:#006600; font-weight:bold;">=&gt;</span> 1, <span style="color:#ff3333; font-weight:bold;">:page_count</span> <span style="color:#006600; font-weight:bold;">=&gt;</span> 1..10 <span style="color:#006600; font-weight:bold;">&#125;</span>, <span style="color:#ff3333; font-weight:bold;">:remove_id</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 />
<br />
<span style="color:#008000; font-style:italic;"># Get top 10 latest docs with file_type = 1 and word_user_id = 12345</span><br />
result = browser.<span style="color:#9900CC;">browse</span><span style="color:#006600; font-weight:bold;">&#40;</span><span style="color:#ff3333; font-weight:bold;">:order</span> <span style="color:#006600; font-weight:bold;">=&gt;</span> <span style="color:#996600;">'created_at DESC'</span>, <span style="color:#ff3333; font-weight:bold;">:filters</span> <span style="color:#006600; font-weight:bold;">=&gt;</span> <span style="color:#006600; font-weight:bold;">&#123;</span> <span style="color:#ff3333; font-weight:bold;">:file_type</span> <span style="color:#006600; font-weight:bold;">=&gt;</span> 1, <span style="color:#ff3333; font-weight:bold;">:user_id</span> <span style="color:#006600; font-weight:bold;">=&gt;</span> <span style="color:#006666;">12345</span> <span style="color:#006600; font-weight:bold;">&#125;</span><span style="color:#006600; font-weight:bold;">&#41;</span></div></td></tr></tbody></table></div>
<p>After a short testing we&#8217;ve got our performance benchmarks results and here are some facts: </p>
<ul>
<li>documents count &#8211; 3000000+</li>
<li>re-indexing time &#8211; 20-30 seconds for an entire data set (4 large joined tables)</li>
<li>longest query (surprisingly &#8211; a query without any filters) takes about 0.5 sec on one core</li>
<li>generic query with 1-2 filters takes 0.01-0.05 sec on one core</li>
<li>queries with many filters take 0.01 sec and less on one core</li>
<li>queries time could be reduced almost linearly when adding more cores (splitting index on many pieces).</li>
<li>sphinx scales really easily on more than one box if we&#8217;ll need it to.</li>
</ul>
<p>As you can see here, it is really useful sometimes to think &#8220;out of the box&#8221; when you work with some tools &#8211; we were using sphinx for a quite a long time now, but this solution for browsing was like a blessing and really helped us to keep up with service popularity growth.</p>
]]></content:encoded>
			<wfw:commentRss>http://kovyrin.net/2008/05/19/using-sphinx-for-non-fulltext-queries/feed/</wfw:commentRss>
		<slash:comments>12</slash:comments>
		</item>
		<item>
		<title>InnoDB Recovery toolset Version 0.3 Released</title>
		<link>http://kovyrin.net/2008/04/14/innodb-recovery-toolset-version-03-released/</link>
		<comments>http://kovyrin.net/2008/04/14/innodb-recovery-toolset-version-03-released/#comments</comments>
		<pubDate>Mon, 14 Apr 2008 19:43:12 +0000</pubDate>
		<dc:creator>Scoundrel</dc:creator>
				<category><![CDATA[Databases]]></category>
		<category><![CDATA[Development]]></category>
		<category><![CDATA[My Projects]]></category>
		<category><![CDATA[community]]></category>
		<category><![CDATA[innodb]]></category>
		<category><![CDATA[MySQL]]></category>
		<category><![CDATA[recovery]]></category>

		<guid isPermaLink="false">http://blog.kovyrin.net/2008/04/14/innodb-recovery-toolset-version-03-released/</guid>
		<description><![CDATA[Even though I didn&#8217;t go to MySQL conf this year (really sad about this), this week is gonna be most active in the community so I decided to do some community stuff too   Today I&#8217;ve released version 0.3 of our innodb recovery toolkit. Now it became much faster, stable and accurate. At this [...]]]></description>
			<content:encoded><![CDATA[<p>Even though I didn&#8217;t go to <a href="http://en.oreilly.com/mysql2008/public/content/home">MySQL conf</a> this year (really sad about this), this week is gonna be most active in the community so I decided to do some community stuff too <img src='http://kovyrin.net/wp-includes/images/smilies/icon_smile.gif' alt=':-)' class='wp-smiley' />  Today I&#8217;ve released version 0.3 of our<a href="http://code.google.com/p/innodb-tools/"> innodb recovery toolkit</a>. Now it became much faster, stable and accurate. At this moment it is possible to recover almost any table from corrupted/deleted tablespace without so much effort as it was before. Here is a short changes list (since 0.1 announced here):</p>
<ul>
<li>More MySQL data types added: DECIMAL (both old and new), DATE, TIME</li>
<li>CHAR data type handling improved in table definitions generator</li>
<li>Indexes filtering added to page_parser</li>
<li>64-bit stat() support added to all tools</li>
<li>Linux has no isnumber() function so we define our own implementation (pretty simple)</li>
<li>Lots of fixes in create_defs.pl script &#8211; now it generates definitions which could recover your data in 80% cases w/o any changes.</li>
<li>Min/max record size calculation fixed in constraints-based parser.</li>
<li>Nullable fixed-size columns support is fixed. </li>
<li>Debug logging is much cleaner now. </li>
</ul>
<p>As always, if you need any help with your recovery, <a href="http://www.percona.com/">we</a> <a href="http://www.percona.com/services/data-recovery-services-mysql.html">would love to help</a>.</p>
]]></content:encoded>
			<wfw:commentRss>http://kovyrin.net/2008/04/14/innodb-recovery-toolset-version-03-released/feed/</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
		<item>
		<title>FastSessions Rails Plugin Released</title>
		<link>http://kovyrin.net/2008/02/06/fastsessions-rails-plugin-released/</link>
		<comments>http://kovyrin.net/2008/02/06/fastsessions-rails-plugin-released/#comments</comments>
		<pubDate>Wed, 06 Feb 2008 00:53:53 +0000</pubDate>
		<dc:creator>Scoundrel</dc:creator>
				<category><![CDATA[Databases]]></category>
		<category><![CDATA[Development]]></category>
		<category><![CDATA[My Projects]]></category>
		<category><![CDATA[Fast-Sessions]]></category>
		<category><![CDATA[performance]]></category>
		<category><![CDATA[plugin]]></category>
		<category><![CDATA[Ruby]]></category>
		<category><![CDATA[Ruby On Rails]]></category>
		<category><![CDATA[scalability]]></category>

		<guid isPermaLink="false">http://blog.kovyrin.net/2008/02/06/fastsessions-rails-plugin-released/</guid>
		<description><![CDATA[How often do we think about our http sessions implementation? I mean, do you know, how your currently used sessions-related code will behave when sessions number in your database will grow up to millions (or, even, hundreds of millions) of records? This is one of the things we do not think about. But if you&#8217;ll [...]]]></description>
			<content:encoded><![CDATA[<p>How often do we think about our http sessions implementation? I mean, do you know, how your currently used sessions-related code will behave when sessions number in your database will grow up to millions (or, even, hundreds of millions) of records? This is one of the things we do not think about. But if you&#8217;ll think about it, you&#8217;ll notice, that 99% of your session-related operations are read-only and 99% of your sessions writes are not needed. Almost all your sessions table records have the same information: session_id and serialized empty session in the data field.</p>
<p>Looking at this sessions-related situation we have created really simple (and, at the same time, really useful for large Rails projects) plugin, which replaces ActiveRecord-based session store and makes sessions much more effective. Below you can find some information about implementation details and decisions we&#8217;ve made in this plugin, but if you just want to try it, then <a href="http://code.google.com/p/rails-fast-sessions/">check out our project site</a>.</p>
<p><span id="more-117"></span></p>
<p>FastSessions is a sessions class for ActiveRecord sessions store created to work fast (really fast). It uses some techniques which are not so widely known in developers&#8217; community and only when they cause huge problems, performance consultants are trying to help with them.</p>
<h2>The Problem</h2>
<p>Original ActiveRecord sessions store is slow. It is fine for some low traffic blogs, but it is too slow to use it on some big/large/huge sites. First of all, it is slow because ActiveRecord is slow. It is powerful ORM framework, but it is overkill for such simple task as a sessions management.</p>
<p>That is why people created SqlSession store. It works with mysql directly with database APIs and works much faster than original AR session store. But it is still slow because:</p>
<ul>
<li>it creates/updates session on each hit &#8211; even dumb bots crawling your sites create thousands of thousands of useless records in your sessions table, 99% of hits do not require any session updates!</li>
<li>it uses 32-char string as a key for sessions records &#8211; all databases work with string keys MUCH slower that with integers keys, so it would be much better to use integers, but we have so long session ids and all session stores use these session ids as a key.</li>
<li>it uses auto_increment primary key, which causes table-level locks in InnoDB for all MySQL versions prior to 5.1.21. These table-level locks with unnecessary inserts cause really weird problems for large sites.</li>
</ul>
<h2>The Solution</h2>
<p>FastSessions plugin was born as a hack created for <a href="http://www.scribd.com">Scribd.com</a> (large RoR-based web project), which was suffering from InnoDB auto-increment table-level locks on sessions table.</p>
<p>So, first of all, we removed id field from the table. Next step was to make lookups faster and we&#8217;ve used a following technique: instead of using (session_id) as a lookup key, we started using (CRC32(session_id), session_id) &#8211; two-columns key which really helps MySQL to find sessions faster because almost all lookups use crc32 field only to find needed record.</p>
<p>And last, but most powerful change we&#8217;ve tried to make was to not create database records for empty sessions and to not save sessions data back to database if this data has not been changed during current request processing.</p>
<p>All of these changes were implemented and you can use them automatically after a simple plugin installation.</p>
<h2>Controversial Decisions</h2>
<p>Many plugin users would never think about one problem we&#8217;ve introduced when removed that auto-increment primary key, so I&#8217;d like to describe it here. The problem is following.</p>
<p>InnoDB groups all data in tables by primary key. This means that when we create auto-increment primary key and insert records to a table, our sessions records are grouped together and saved sequentially on the disk. But if we&#8217;ll make pretty random value (like crc32 of a random session id) a primary key, then every session record will be inserted in its own place and it will generate some random I/O which is not so good for I/O bound servers.</p>
<p>So, we decided to let the user choose what primary key to use in his deployment of our plugin, so if you&#8217;re going to use this module with MySQL 5.1.22+, then you&#8217;d like to set</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">&nbsp; <span style="color:#CC00FF; font-weight:bold;"><span style="color:#6666ff; font-weight:bold;">CGI::Session</span></span>::<span style="color:#6666ff; font-weight:bold;">ActiveRecordStore::FastSessions</span>.<span style="color:#9900CC;">use_auto_increment</span> = <span style="color:#0000FF; font-weight:bold;">true</span></div></td></tr></tbody></table></div>
<p>because it will provide you with consecutive data inserts in InnoDB. Another cases when you&#8217;d like to use it is when your MySQL server is I/O bound now and you do not want to add random I/O because of randomized primary key.</p>
<h2>Working With Old AR Sessions Table</h2>
<p>If you do not like to loose old sessions created with default AR sessions plugin, you could set</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">&nbsp; <span style="color:#CC00FF; font-weight:bold;"><span style="color:#6666ff; font-weight:bold;">CGI::Session</span></span>::<span style="color:#6666ff; font-weight:bold;">ActiveRecordStore::FastSessions</span>.<span style="color:#9900CC;">fallback_to_old_table</span> = <span style="color:#0000FF; font-weight:bold;">true</span></div></td></tr></tbody></table></div>
<p>and then all session reads will fall back to old sessions table if some session_id was not found in default fast sessions table. Old sessions table name could be set using</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">&nbsp; <span style="color:#CC00FF; font-weight:bold;"><span style="color:#6666ff; font-weight:bold;">CGI::Session</span></span>::<span style="color:#6666ff; font-weight:bold;">ActiveRecordStore::FastSessions</span>.<span style="color:#9900CC;">old_table_name</span></div></td></tr></tbody></table></div>
<p>variable.</p>
<h2>Installation</h2>
<p>This plugin installation is pretty simple and described in a few steps below:</p>
<ol>
<li>Install this plugin sources in your vendor/plugins directory (it could be ./script/plugin install, or piston import command &#8211; it is up to you) from our SVN reposipory. For example:
<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">$ piston import http:<span style="color: #000000; font-weight: bold;">//</span>rails-fast-sessions.googlecode.com<span style="color: #000000; font-weight: bold;">/</span>svn<span style="color: #000000; font-weight: bold;">/</span>trunk<span style="color: #000000; font-weight: bold;">/</span> vendor<span style="color: #000000; font-weight: bold;">/</span>plugins<span style="color: #000000; font-weight: bold;">/</span>fast_sessions</div></td></tr></tbody></table></div>
</li>
<li>Enable ActiveRecord session store in your config/environment.rb file:
<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 /></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:#6666ff; font-weight:bold;">Rails::Initializer</span>.<span style="color:#9900CC;">run</span> <span style="color:#9966CC; font-weight:bold;">do</span> <span style="color:#006600; font-weight:bold;">|</span>config<span style="color:#006600; font-weight:bold;">|</span><br />
&nbsp; ......<br />
&nbsp; <span style="color:#9900CC;">config</span>.<span style="color:#9900CC;">action_controller</span>.<span style="color:#9900CC;">session_store</span> = <span style="color:#ff3333; font-weight:bold;">:active_record_store</span><br />
&nbsp; ......<br />
<span style="color:#9966CC; font-weight:bold;">end</span></div></td></tr></tbody></table></div>
</li>
<li>Create migration for your new sessions table:
<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">$ &nbsp;.<span style="color: #000000; font-weight: bold;">/</span>script<span style="color: #000000; font-weight: bold;">/</span>generate fast_session_migration AddFastSessions</div></td></tr></tbody></table></div>
</li>
<li>Open your newly created migration and change table_name and use_auto_increment parameters of the plugin (if you want to).</li>
<li>Run your migration:
<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">$ &nbsp;rake db:migrate</div></td></tr></tbody></table></div>
</li>
<li>Start your application and try to perform some actions which would definitely save some data to your session. Then check your fast_sessions table (if you did not renamed it) for a records.
</li>
</ol>
<h2>Downloading</h2>
<p>Most recent version if this plugin could be found on the <a href="http://code.google.com/p/rails-fast-sessions/">project&#8217;s site</a> or in <a href="http://rails-fast-sessions.googlecode.com/svn/trunk/">SVN repository</a></p>
<h2>Author</h2>
<p>This plugin has been created by <a href="http://kovyrin.info/">Alexey Kovyrin</a>. Development is sponsored by <a href="http://www.scribd.com/">Scribd.com</a>.</p>
]]></content:encoded>
			<wfw:commentRss>http://kovyrin.net/2008/02/06/fastsessions-rails-plugin-released/feed/</wfw:commentRss>
		<slash:comments>13</slash:comments>
		</item>
	</channel>
</rss>
