<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" media="screen" href="/~d/styles/rss2full.xsl"?><?xml-stylesheet type="text/css" media="screen" href="http://feeds.feedburner.com/~d/styles/itemcontent.css"?><rss xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:sy="http://purl.org/rss/1.0/modules/syndication/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/" xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0" version="2.0">

<channel>
	<title>Alexey Kovyrin's Blog</title>
	
	<link>http://kovyrin.net</link>
	<description>Yet Another Admin's blog</description>
	<lastBuildDate>Tue, 03 Nov 2009 15:36:26 +0000</lastBuildDate>
	<generator>http://wordpress.org/?v=2.8.4</generator>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
			<atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="self" href="http://feeds.feedburner.com/Homo-Adminus-Ru" type="application/rss+xml" /><atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="hub" href="http://pubsubhubbub.appspot.com" /><item>
		<title>DB Charmer – ActiveRecord Connection Magic Plugin</title>
		<link>http://feedproxy.google.com/~r/Homo-Adminus-Ru/~3/_bkc3hWZQr0/</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>

<p><a href="http://feedads.g.doubleclick.net/~a/H_BCSir_5IseLi2mHyTZyQqUf9Q/0/da"><img src="http://feedads.g.doubleclick.net/~a/H_BCSir_5IseLi2mHyTZyQqUf9Q/0/di" border="0" ismap="true"></img></a><br/>
<a href="http://feedads.g.doubleclick.net/~a/H_BCSir_5IseLi2mHyTZyQqUf9Q/1/da"><img src="http://feedads.g.doubleclick.net/~a/H_BCSir_5IseLi2mHyTZyQqUf9Q/1/di" border="0" ismap="true"></img></a></p><div class="feedflare">
<a href="http://feeds.feedburner.com/~ff/Homo-Adminus-Ru?a=_bkc3hWZQr0:tDjUt-_xpVg:V_sGLiPBpWU"><img src="http://feeds.feedburner.com/~ff/Homo-Adminus-Ru?i=_bkc3hWZQr0:tDjUt-_xpVg:V_sGLiPBpWU" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/Homo-Adminus-Ru?a=_bkc3hWZQr0:tDjUt-_xpVg:F7zBnMyn0Lo"><img src="http://feeds.feedburner.com/~ff/Homo-Adminus-Ru?i=_bkc3hWZQr0:tDjUt-_xpVg:F7zBnMyn0Lo" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/Homo-Adminus-Ru?a=_bkc3hWZQr0:tDjUt-_xpVg:yIl2AUoC8zA"><img src="http://feeds.feedburner.com/~ff/Homo-Adminus-Ru?d=yIl2AUoC8zA" border="0"></img></a>
</div>]]></content:encoded>
			<wfw:commentRss>http://kovyrin.net/2009/11/03/db-charmer-activerecord-connection-magic-plugin/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		<feedburner:origLink>http://kovyrin.net/2009/11/03/db-charmer-activerecord-connection-magic-plugin/</feedburner:origLink></item>
		<item>
		<title>Advanced Squid Caching in Scribd: Hardware + Software Used</title>
		<link>http://feedproxy.google.com/~r/Homo-Adminus-Ru/~3/s8GVmmmES8s/</link>
		<comments>http://kovyrin.net/2009/08/04/advanced-squid-caching-in-scribd-hardware-software-used/#comments</comments>
		<pubDate>Tue, 04 Aug 2009 05:23:18 +0000</pubDate>
		<dc:creator>Scoundrel</dc:creator>
				<category><![CDATA[Admin-tips]]></category>
		<category><![CDATA[Development]]></category>
		<category><![CDATA[Networks]]></category>
		<category><![CDATA[cache]]></category>
		<category><![CDATA[haproxy]]></category>
		<category><![CDATA[hardware]]></category>
		<category><![CDATA[Nginx]]></category>
		<category><![CDATA[scribd]]></category>
		<category><![CDATA[squid]]></category>

		<guid isPermaLink="false">http://kovyrin.net/?p=298</guid>
		<description><![CDATA[After the previous post in this caching related series I&#8217;ve received many questions on hardware and software configuration of our servers so in this post I&#8217;ll describe our server&#8217;s configs and the motivation behind those configs.

Hardware Configuration
Since in our setup Squid server uses one-process model (with an asynchronous requests processing) there was no point in [...]]]></description>
			<content:encoded><![CDATA[<p>After <a href="http://kovyrin.net/2009/07/21/advanced-squid-caching-scribd-logged-in-users-complex-urls/">the previous post in this caching related series</a> I&#8217;ve received many questions on hardware and software configuration of our servers so in this post I&#8217;ll describe our server&#8217;s configs and the motivation behind those configs.</p>
<p><span id="more-298"></span></p>
<h3>Hardware Configuration</h3>
<p>Since in our setup Squid server uses one-process model (with an asynchronous requests processing) there was no point in ordering multi-core CPUs for our boxes and since we have a lots of pages on the site and the cache is pretty huge all the servers ended up being highly I/O bound. Considering these facts we&#8217;ve decided to use the following hardware specs for the servers:</p>
<p><b>CPU:</b> One pretty cheap dual-core Intel Xeon 5148 (no need in multiple cores or really high frequencies &#8211; even these CPUs have ~1% avg load)<br />
<b>RAM:</b> 8Gb (basically to reduce I/O pressure by caching hot content in RAM)<br />
<b>Disks: </b> 4 x small SAS 15k drives in JBOD mode (no RAIDS &#8211; we&#8217;ve tried all kinds of RAID configs and it did not help with the I/O performance)</p>
<p>So, once again: <i>nothing is as important in a squid box as I/O throughput</i>. </p>
<p>Here is a sample CPU load graph from one of the boxes:</p>
<p><a href="http://kovyrin.net/wp-content/uploads/2009/08/squid-cpu-graph.png"><img src="http://kovyrin.net/wp-content/uploads/2009/08/squid-cpu-graph-300x139.png" alt="squid-cpu-graph" title="squid-cpu-graph" width="300" height="139" class="aligncenter size-medium wp-image-305" /></a></p>
<h3>Software Configuration</h3>
<p>This could be a long story, but in a few words our experience with different squid versions was the following.</p>
<p>First, when I&#8217;ve started working on this caching project I&#8217;ve just installed squid using Debian&#8217;s apt-get install squid command. As the result we&#8217;ve got some ancient squid 2.6 release that for some reason (still unclear to me) was painfully slow in I/O operations and it had some leaking file descriptors problem so after a few hours under production load the box would simply stop processing requests.</p>
<p>When the first approach failed, I&#8217;ve decided to go to the <a href="http://www.squid-cache.org/">squid web site</a>, download the latest production release and install it from sources (yes, we do it all the time when OS vendor ships too old or buggy releases). Result &#8211; freaking fast and stable squid 3.0 which worked flawlessly for about 5 months. </p>
<p>Few months ago we&#8217;ve found out about the <a href="http://www.mnot.net/blog/2007/12/12/stale">stale-* extensions</a> available in squid 2.7 and I&#8217;ve started wondering if we should change our perfectly stable 3.0 setup to 2.7. And some time later I&#8217;ve decided to use Vary HTTP header in our caching architecture and then I found out that vary-caching correctly implemented only in 2.7 and since 3.0 is a complete rewrite of the 2.X branch, vary-caching is not yet implemented there (or not in a way we&#8217;d want it to be implemented).</p>
<p>So, the  final result: at this moment in time we&#8217;re using custom-built Squid 2.7STABLE6 and really happy with it, it is stable, fast and feature-rich caching proxy server.</p>
<h3>Caching Cluster Configuration</h3>
<p>Apparently we have more than one squid server in scribd and this makes it a bit harder to use those servers (comparing to one box when you&#8217;d send all requests to one IP:port pair). We&#8217;ve tried to use round-robin balancing for the squid boxes + ICP-based neighbor checks but it was adding more latency to our responses and we&#8217;ve decided to put haproxy load balancer between nginx and squid farm and set up URL hash based balancing to distribute requests evenly amongst squid backends. </p>
<p>This scheme worked pretty nice, but we had one serious problem with this setup: if one squid box would go down, haproxy would quickly detect the problem and would remove it from the pool&#8230; And here comes the problem &#8211; removing a server from the pool completely changes hashing keys space and all cached requests become invalid. To solve this problem we&#8217;ve developed a nginx balancer module that performs consistent hashing of URLs and we&#8217;re testing this module now in production. What is really good about this module is that it removes one hop from the chain if http proxies between the site and a user.</p>
<p>So, this was a short description of what hardware we use for our caching cluster and why do we use it. In the next posts of this series we&#8217;ll talk about cache control and objects invalidation.</p>
<p><b>UPDATE:</b> squid-3.0 was not a rewrite of Squid-2. It’s a continuation of Squid-2.5, with a conversion to compile under C++.</p>

<p><a href="http://feedads.g.doubleclick.net/~a/ehdu7qRVgXouDLhL-yuJgZu86jc/0/da"><img src="http://feedads.g.doubleclick.net/~a/ehdu7qRVgXouDLhL-yuJgZu86jc/0/di" border="0" ismap="true"></img></a><br/>
<a href="http://feedads.g.doubleclick.net/~a/ehdu7qRVgXouDLhL-yuJgZu86jc/1/da"><img src="http://feedads.g.doubleclick.net/~a/ehdu7qRVgXouDLhL-yuJgZu86jc/1/di" border="0" ismap="true"></img></a></p><div class="feedflare">
<a href="http://feeds.feedburner.com/~ff/Homo-Adminus-Ru?a=s8GVmmmES8s:rOiXzkyoXvI:V_sGLiPBpWU"><img src="http://feeds.feedburner.com/~ff/Homo-Adminus-Ru?i=s8GVmmmES8s:rOiXzkyoXvI:V_sGLiPBpWU" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/Homo-Adminus-Ru?a=s8GVmmmES8s:rOiXzkyoXvI:F7zBnMyn0Lo"><img src="http://feeds.feedburner.com/~ff/Homo-Adminus-Ru?i=s8GVmmmES8s:rOiXzkyoXvI:F7zBnMyn0Lo" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/Homo-Adminus-Ru?a=s8GVmmmES8s:rOiXzkyoXvI:yIl2AUoC8zA"><img src="http://feeds.feedburner.com/~ff/Homo-Adminus-Ru?d=yIl2AUoC8zA" border="0"></img></a>
</div>]]></content:encoded>
			<wfw:commentRss>http://kovyrin.net/2009/08/04/advanced-squid-caching-in-scribd-hardware-software-used/feed/</wfw:commentRss>
		<slash:comments>14</slash:comments>
		<feedburner:origLink>http://kovyrin.net/2009/08/04/advanced-squid-caching-in-scribd-hardware-software-used/</feedburner:origLink></item>
		<item>
		<title>Advanced Squid Caching in Scribd: Logged In Users and Complex URLs Handling</title>
		<link>http://feedproxy.google.com/~r/Homo-Adminus-Ru/~3/pbtBFjj1jac/</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>

<p><a href="http://feedads.g.doubleclick.net/~a/aINY2TVr-QRU7CXEAUA3F2N0cWw/0/da"><img src="http://feedads.g.doubleclick.net/~a/aINY2TVr-QRU7CXEAUA3F2N0cWw/0/di" border="0" ismap="true"></img></a><br/>
<a href="http://feedads.g.doubleclick.net/~a/aINY2TVr-QRU7CXEAUA3F2N0cWw/1/da"><img src="http://feedads.g.doubleclick.net/~a/aINY2TVr-QRU7CXEAUA3F2N0cWw/1/di" border="0" ismap="true"></img></a></p><div class="feedflare">
<a href="http://feeds.feedburner.com/~ff/Homo-Adminus-Ru?a=pbtBFjj1jac:8xZyb-_Z5SA:V_sGLiPBpWU"><img src="http://feeds.feedburner.com/~ff/Homo-Adminus-Ru?i=pbtBFjj1jac:8xZyb-_Z5SA:V_sGLiPBpWU" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/Homo-Adminus-Ru?a=pbtBFjj1jac:8xZyb-_Z5SA:F7zBnMyn0Lo"><img src="http://feeds.feedburner.com/~ff/Homo-Adminus-Ru?i=pbtBFjj1jac:8xZyb-_Z5SA:F7zBnMyn0Lo" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/Homo-Adminus-Ru?a=pbtBFjj1jac:8xZyb-_Z5SA:yIl2AUoC8zA"><img src="http://feeds.feedburner.com/~ff/Homo-Adminus-Ru?d=yIl2AUoC8zA" border="0"></img></a>
</div>]]></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>
		<feedburner:origLink>http://kovyrin.net/2009/07/21/advanced-squid-caching-scribd-logged-in-users-complex-urls/</feedburner:origLink></item>
		<item>
		<title>The Blog v.2.0</title>
		<link>http://feedproxy.google.com/~r/Homo-Adminus-Ru/~3/aNqpWOcz6lI/</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>

<p><a href="http://feedads.g.doubleclick.net/~a/WA-mMNRkBTvpsqZxzHqU0SU01qY/0/da"><img src="http://feedads.g.doubleclick.net/~a/WA-mMNRkBTvpsqZxzHqU0SU01qY/0/di" border="0" ismap="true"></img></a><br/>
<a href="http://feedads.g.doubleclick.net/~a/WA-mMNRkBTvpsqZxzHqU0SU01qY/1/da"><img src="http://feedads.g.doubleclick.net/~a/WA-mMNRkBTvpsqZxzHqU0SU01qY/1/di" border="0" ismap="true"></img></a></p><div class="feedflare">
<a href="http://feeds.feedburner.com/~ff/Homo-Adminus-Ru?a=aNqpWOcz6lI:xTOCQewktgg:V_sGLiPBpWU"><img src="http://feeds.feedburner.com/~ff/Homo-Adminus-Ru?i=aNqpWOcz6lI:xTOCQewktgg:V_sGLiPBpWU" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/Homo-Adminus-Ru?a=aNqpWOcz6lI:xTOCQewktgg:F7zBnMyn0Lo"><img src="http://feeds.feedburner.com/~ff/Homo-Adminus-Ru?i=aNqpWOcz6lI:xTOCQewktgg:F7zBnMyn0Lo" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/Homo-Adminus-Ru?a=aNqpWOcz6lI:xTOCQewktgg:yIl2AUoC8zA"><img src="http://feeds.feedburner.com/~ff/Homo-Adminus-Ru?d=yIl2AUoC8zA" border="0"></img></a>
</div>]]></content:encoded>
			<wfw:commentRss>http://kovyrin.net/2009/07/20/the-blog-v-2-0/feed/</wfw:commentRss>
		<slash:comments>8</slash:comments>
		<feedburner:origLink>http://kovyrin.net/2009/07/20/the-blog-v-2-0/</feedburner:origLink></item>
		<item>
		<title>Loops plugin for rails and merb released</title>
		<link>http://feedproxy.google.com/~r/Homo-Adminus-Ru/~3/y7KldQ1VQjQ/</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>

<p><a href="http://feedads.g.doubleclick.net/~a/4udDQgC-9TZCkhQNbfrbLSDTDro/0/da"><img src="http://feedads.g.doubleclick.net/~a/4udDQgC-9TZCkhQNbfrbLSDTDro/0/di" border="0" ismap="true"></img></a><br/>
<a href="http://feedads.g.doubleclick.net/~a/4udDQgC-9TZCkhQNbfrbLSDTDro/1/da"><img src="http://feedads.g.doubleclick.net/~a/4udDQgC-9TZCkhQNbfrbLSDTDro/1/di" border="0" ismap="true"></img></a></p><div class="feedflare">
<a href="http://feeds.feedburner.com/~ff/Homo-Adminus-Ru?a=y7KldQ1VQjQ:l1IaNwfs-vg:V_sGLiPBpWU"><img src="http://feeds.feedburner.com/~ff/Homo-Adminus-Ru?i=y7KldQ1VQjQ:l1IaNwfs-vg:V_sGLiPBpWU" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/Homo-Adminus-Ru?a=y7KldQ1VQjQ:l1IaNwfs-vg:F7zBnMyn0Lo"><img src="http://feeds.feedburner.com/~ff/Homo-Adminus-Ru?i=y7KldQ1VQjQ:l1IaNwfs-vg:F7zBnMyn0Lo" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/Homo-Adminus-Ru?a=y7KldQ1VQjQ:l1IaNwfs-vg:yIl2AUoC8zA"><img src="http://feeds.feedburner.com/~ff/Homo-Adminus-Ru?d=yIl2AUoC8zA" border="0"></img></a>
</div>]]></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>
		<feedburner:origLink>http://kovyrin.net/2009/02/17/loops-plugin-for-rails-and-merb-released/</feedburner:origLink></item>
		<item>
		<title>Rails Developer for a Large Startup: My Vision of an Ideal Candidate</title>
		<link>http://feedproxy.google.com/~r/Homo-Adminus-Ru/~3/4-kMZ47hGCE/</link>
		<comments>http://kovyrin.net/2009/02/07/rails-developer-for-a-large-startup-my-vision-of-an-ideal-candidate/#comments</comments>
		<pubDate>Sat, 07 Feb 2009 07:41:22 +0000</pubDate>
		<dc:creator>Scoundrel</dc:creator>
				<category><![CDATA[Databases]]></category>
		<category><![CDATA[Development]]></category>
		<category><![CDATA[General]]></category>
		<category><![CDATA[Links]]></category>
		<category><![CDATA[developer]]></category>
		<category><![CDATA[hiring]]></category>
		<category><![CDATA[rails]]></category>
		<category><![CDATA[scribd]]></category>
		<category><![CDATA[startup]]></category>

		<guid isPermaLink="false">http://blog.kovyrin.net/?p=176</guid>
		<description><![CDATA[Few days ago we were chatting in our corporate Campfire room and one of the guys asked me what do I think about Rails developers hiring process, what questions I&#8217;d ask a candidate, etc&#8230; This question started really long and interesting discussion and I&#8217;d like to share my thoughts on this question in this post.

So, [...]]]></description>
			<content:encoded><![CDATA[<p>Few days ago we were chatting in <a href="http://www.scribd.com">our</a> corporate Campfire room and one of the guys asked me what do I think about Rails developers hiring process, what questions I&#8217;d ask a candidate, etc&#8230; This question started really long and interesting discussion and I&#8217;d like to share my thoughts on this question in this post.</p>
<p><span id="more-176"></span></p>
<p>So, first of all I would like to explain what kind of interviews I really hate <img src='http://kovyrin.net/wp-includes/images/smilies/icon_smile.gif' alt=':-)' class='wp-smiley' />  Ever since I was thinking of myself as of a developer (many years ago) and was going to &#8220;software developer position&#8221; interviews I really hated questions like &#8220;What is the name and possible values of the third parameter of the function some_freakin_weird_func() from some_weird.h&#8221; or &#8220;How to declare a virtual destructor and when it could be useful?&#8221;&#8230; All my life I had pretty practical thinking and never bothered to learn APIs or some really deep language concepts that are useful in 1% of the development time in really weird edge case situations. I always thought that a Real Engineer should know where to find an answer and should be able to find a way to implement something without knowing all possible ways to do it &#8211; engineering mind should be flexible! </p>
<p>So, if I&#8217;d interview a developer for our company, I&#8217;d never ask them this weird kind of theoretical questions because honestly speaking web development is not a rocket science and in most of the cases you don&#8217;t need to have a CS Master degree to be a good developer.</p>
<p>So, let&#8217;s get closer to our discussion in <a href="http://www.scribd.com">Scribd</a>. During this discussion we were talking about our standard hiring practices, about questions we used to ask our candidates and such. As the result I decided to write a set of questions I&#8217;d really like an ideal candidate to be able to answer. Many of them are not rails specific, but rather high-load and high-scale specific. Let me share some of those questions here.</p>
<ol>
<li>
You have a generic web application (does not matter if it is rails or not) with a set of layers and parties involved in each request:</p>
<ul>
<li>Database</li>
<li>Application</li>
<li>Internet</li>
<li>User</li>
</ul>
<p>What security problems could occur on each level and what action should developer take to prevent those problems from happening? For example take a login action in your application and explain possible problems.
</li>
<li>
You have a table with 100 millions of records, you need to make some changes in each records and it is obvious that you won&#8217;t be able to do that using some SQL queries, so you need to do in some application-level code (one time use script). Your database is MySQL.</p>
<ul>
<li>Where would you put the code in the application: model, module with a bunch of methods, migration, some dedicated script?</li>
<li>How would you process all those records: Model.find(:all).each {}, a loop with different offsets and a limit, a loop walking through primary key values with some step, something else?</li>
<li>What would you choose for this task and why: Model.find or Model.find_by_sql? What is the major difference between those two? When would you definitely use find_by_sql?</li>
<li>What would you use: update_attribute, attribute assignments + saves, Model.connection.execute or something else to update the table? When and why would you use each of those? </li>
</ul>
</li>
<li>
You have a rails action that accepts uploads and then transfers them to a pretty slow storage (S3, some slow nfs, something else). If you have a lots of uploads, pretty soon many of your mongrels (if not all) will be used for uploads and your application would become unusable. What options do you see to solve this problem?
</li>
<li>
You have a huge table in mysql, you need to select a few randomly selected records (and this is not a one-time action, it&#8217;ll be used pretty often, like on the home page), how&#8217;d you do that?
</li>
<li>
You have a document model with the following fields: id, user_id, title, body, created_at, views_count, download_count. There are 10000000 documents. You need to implement a download counters on your site so when one downloads a document, you&#8217;d increase the download_counter field. How would you implement counter management?
</li>
<li>
How&#8217;d you write an SQL query to find all users in a system that have no docs (standard User has_many docs relationship)?
</li>
<li>
Caching:</p>
<ul>
<li>What kinds of caching techniques do you know?</li>
<li>What caching options does Rails provide, when would you use those?</li>
<li>What caching options does HTTP protocol provide?</li>
<li>You have a pretty popular action on your site which uses some heavy SQL query which should be cached. But the problem is that sometimes when the cache expires and too many users hit the action, all those SQL queries will start hammering the database trying to refresh your caches&#8230; That causes huge problems. How&#8217;d you try to solve the problem? </li>
</ul>
</li>
</ol>
<hr/>
<p>As you can see, these questions aren&#8217;t asking you about REST or new cool Rails buzzwords (ActiveResource, named_scope, etc, etc) because IMHO &#8211; you can learn those in a week of skimming through any rails-related mailing list. My questions are aimed at the most often happening problems in high-traffic startups where not all common practices could be used and you need to think before writing some code that&#8217;d be executed millions of times per day.</p>
<p>So, if you&#8217;ve read the questions and you think you know (or is able to find in Google/books/etc) answers and you think you know Rails I think you should contact us because we&#8217;re hiring. At this moment we need a few really great Ruby-developers that are not afraid of working on high-load web applications and who isn&#8217;t just a buzzword junkies, but a developers that could use their tools effectively.</p>
<p>For more information and more formal position information, please check out <a href="http://www.scribd.com/jobs/2">our jobs page</a> or just <a href="mailto:alexey@scribd.com">email me</a> with any questions related to these positions.</p>
<p>P. S. If you&#8217;re really good, we could work with you remotely w/o relocation to San Francisco (we have some team members in Canada and Ukraine now).</p>

<p><a href="http://feedads.g.doubleclick.net/~a/Pu6TjnGfsOokl-k6neRwfVABqow/0/da"><img src="http://feedads.g.doubleclick.net/~a/Pu6TjnGfsOokl-k6neRwfVABqow/0/di" border="0" ismap="true"></img></a><br/>
<a href="http://feedads.g.doubleclick.net/~a/Pu6TjnGfsOokl-k6neRwfVABqow/1/da"><img src="http://feedads.g.doubleclick.net/~a/Pu6TjnGfsOokl-k6neRwfVABqow/1/di" border="0" ismap="true"></img></a></p><div class="feedflare">
<a href="http://feeds.feedburner.com/~ff/Homo-Adminus-Ru?a=4-kMZ47hGCE:5_QhqN_kZWM:V_sGLiPBpWU"><img src="http://feeds.feedburner.com/~ff/Homo-Adminus-Ru?i=4-kMZ47hGCE:5_QhqN_kZWM:V_sGLiPBpWU" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/Homo-Adminus-Ru?a=4-kMZ47hGCE:5_QhqN_kZWM:F7zBnMyn0Lo"><img src="http://feeds.feedburner.com/~ff/Homo-Adminus-Ru?i=4-kMZ47hGCE:5_QhqN_kZWM:F7zBnMyn0Lo" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/Homo-Adminus-Ru?a=4-kMZ47hGCE:5_QhqN_kZWM:yIl2AUoC8zA"><img src="http://feeds.feedburner.com/~ff/Homo-Adminus-Ru?d=yIl2AUoC8zA" border="0"></img></a>
</div>]]></content:encoded>
			<wfw:commentRss>http://kovyrin.net/2009/02/07/rails-developer-for-a-large-startup-my-vision-of-an-ideal-candidate/feed/</wfw:commentRss>
		<slash:comments>3</slash:comments>
		<feedburner:origLink>http://kovyrin.net/2009/02/07/rails-developer-for-a-large-startup-my-vision-of-an-ideal-candidate/</feedburner:origLink></item>
		<item>
		<title>ActiveMQ Tips: Flow Control and Stalled Producers Problem</title>
		<link>http://feedproxy.google.com/~r/Homo-Adminus-Ru/~3/u_Earkj8438/</link>
		<comments>http://kovyrin.net/2009/01/23/activemq-tips-flow-control-and-stalled-producers-problem/#comments</comments>
		<pubDate>Fri, 23 Jan 2009 07:23:09 +0000</pubDate>
		<dc:creator>Scoundrel</dc:creator>
				<category><![CDATA[Admin-tips]]></category>
		<category><![CDATA[Development]]></category>
		<category><![CDATA[activemq]]></category>
		<category><![CDATA[java]]></category>
		<category><![CDATA[queue]]></category>
		<category><![CDATA[Ruby]]></category>
		<category><![CDATA[scribd]]></category>
		<category><![CDATA[server]]></category>
		<category><![CDATA[tips]]></category>

		<guid isPermaLink="false">http://blog.kovyrin.net/?p=167</guid>
		<description><![CDATA[It&#8217;s been a few months since we&#8216;ve started actively using ActiveMQ queue server in our project. For some time we had pretty weird problems with it and even started thinking about switching to something else or even writing our own queue server which would comply with our requirements. The most annoying problem was the following: [...]]]></description>
			<content:encoded><![CDATA[<p>It&#8217;s been a few months since <a href="http://www.scribd.com">we</a>&#8216;ve started actively using <a href="http://activemq.apache.org/">ActiveMQ</a> queue server in our project. For some time we had pretty weird problems with it and even started thinking about switching to something else or even writing our own queue server which would comply with our requirements. The most annoying problem was the following: some time after activemq restart everything worked really well and then activemq started lagging, queue started growing and all producer processes were stalling on push() operations. We rewrote our producers from Ruby to JRuby, then to Java and still &#8211; after some time everything was in a bad shape until we restarted the queue server.</p>
<p><span id="more-167"></span></p>
<p>So, long story short, after a lots of docs and source code reading we&#8217;ve found really interesting thing. There is a &#8220;feature&#8221; added in the recent ActiveMQ release (5.X) called &#8220;<a href="http://activemq.apache.org/producer-flow-control.html">flow control</a>&#8220;, which is used by ActiveMQ core to <a href="http://activemq.apache.org/what-happens-with-a-fast-producer-and-slow-consumer.html">slow down too fast producers</a> when consumers lag or just aren&#8217;t as fast as ActiveMQ wants them to be. This results in unpredictable producers stalls and all kinds of weird problems. </p>
<p>If you&#8217;re experiencing the same problems, you could try to add the following to your activemq.xml file to make it stop using flow control and let your producers spool data to the queue as fast as it is possible (that&#8217;s exactly what I&#8217;d expect from a queue server):</p>
<div class="codecolorer-container xml 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="xml codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap"><span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;destinationPolicy<span style="color: #000000; font-weight: bold;">&gt;</span></span></span><br />
&nbsp; &nbsp; <span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;policyMap<span style="color: #000000; font-weight: bold;">&gt;</span></span></span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;policyEntries<span style="color: #000000; font-weight: bold;">&gt;</span></span></span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;policyEntry</span> <span style="color: #000066;">queue</span>=<span style="color: #ff0000;">&quot;&gt;</span></span>&quot; producerFlowControl=&quot;false&quot; memoryLimit=&quot;64mb&quot; /&gt;<br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;/policyEntries<span style="color: #000000; font-weight: bold;">&gt;</span></span></span><br />
&nbsp; &nbsp; <span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;/policyMap<span style="color: #000000; font-weight: bold;">&gt;</span></span></span><br />
<span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;/destinationPolicy<span style="color: #000000; font-weight: bold;">&gt;</span></span></span></div></td></tr></tbody></table></div>
<p>When this is added, all your queues (if they take more than 64Mb of RAM) will start persisting their messages to the disk and freeing your RAM to consume more messages w/o any slowdowns.</p>
<p>Another useful feature we use in our producer is <a href="http://activemq.apache.org/async-sends.html">Async Sends</a> which basically means that your producers won&#8217;t require any acknowledgments from ActiveMQ after it pushes your messages to a queue. This makes push() operations MUCH faster and your producers&#8217; throughput will become tremendously higher. If you use Java as your producer language, you could use the following code to make your connection async:</p>
<div class="codecolorer-container java 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="java codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">queueConnectionFactory <span style="color: #339933;">=</span> <span style="color: #000000; font-weight: bold;">new</span> ActiveMQConnectionFactory<span style="color: #009900;">&#40;</span>queue_user, queue_password, queue_url<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span><br />
queueConnectionPool <span style="color: #339933;">=</span> <span style="color: #000000; font-weight: bold;">new</span> PooledConnectionFactory<span style="color: #009900;">&#40;</span>queueConnectionFactory<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span><br />
queueConnectionPool.<span style="color: #006633;">setMaxConnections</span><span style="color: #009900;">&#40;</span>200<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span><br />
<br />
<span style="color: #666666; font-style: italic;">// This line is used to make your connection async</span><br />
<span style="color: #009900;">&#40;</span><span style="color: #009900;">&#40;</span>ActiveMQConnectionFactory<span style="color: #009900;">&#41;</span>queueConnectionFactory<span style="color: #009900;">&#41;</span>.<span style="color: #006633;">setUseAsyncSend</span><span style="color: #009900;">&#40;</span><span style="color: #000066; font-weight: bold;">true</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span></div></td></tr></tbody></table></div>
<p>After we&#8217;ve made the changes explained above, our queue server and producers became rock stable and blazing fast so there is no point to look for another solution because looks like <a href="http://blog.kovyrin.net/2008/10/30/activemq-ruby-stomp-client-how-to-process-elements-one-by-one/">I was right</a> &#8211; ActiveMQ is an amazing piece of software &#8211; you just need to learn it before using.</p>
<p>This is it for today, if you have any questions, feel free to ask them in the comments and I&#8217;d be glad to answer.</p>

<p><a href="http://feedads.g.doubleclick.net/~a/h7diBG90WGbxCiGX5tJi0Ty4J0s/0/da"><img src="http://feedads.g.doubleclick.net/~a/h7diBG90WGbxCiGX5tJi0Ty4J0s/0/di" border="0" ismap="true"></img></a><br/>
<a href="http://feedads.g.doubleclick.net/~a/h7diBG90WGbxCiGX5tJi0Ty4J0s/1/da"><img src="http://feedads.g.doubleclick.net/~a/h7diBG90WGbxCiGX5tJi0Ty4J0s/1/di" border="0" ismap="true"></img></a></p><div class="feedflare">
<a href="http://feeds.feedburner.com/~ff/Homo-Adminus-Ru?a=u_Earkj8438:pDBG_KTtIuo:V_sGLiPBpWU"><img src="http://feeds.feedburner.com/~ff/Homo-Adminus-Ru?i=u_Earkj8438:pDBG_KTtIuo:V_sGLiPBpWU" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/Homo-Adminus-Ru?a=u_Earkj8438:pDBG_KTtIuo:F7zBnMyn0Lo"><img src="http://feeds.feedburner.com/~ff/Homo-Adminus-Ru?i=u_Earkj8438:pDBG_KTtIuo:F7zBnMyn0Lo" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/Homo-Adminus-Ru?a=u_Earkj8438:pDBG_KTtIuo:yIl2AUoC8zA"><img src="http://feeds.feedburner.com/~ff/Homo-Adminus-Ru?d=yIl2AUoC8zA" border="0"></img></a>
</div>]]></content:encoded>
			<wfw:commentRss>http://kovyrin.net/2009/01/23/activemq-tips-flow-control-and-stalled-producers-problem/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		<feedburner:origLink>http://kovyrin.net/2009/01/23/activemq-tips-flow-control-and-stalled-producers-problem/</feedburner:origLink></item>
		<item>
		<title>Using SSH tunnel connection as a SOCKS5 proxy</title>
		<link>http://feedproxy.google.com/~r/Homo-Adminus-Ru/~3/mnCmTXCykZM/</link>
		<comments>http://kovyrin.net/2008/12/11/using-ssh-tunnel-connection-as-a-socks5-proxy/#comments</comments>
		<pubDate>Thu, 11 Dec 2008 23:13:51 +0000</pubDate>
		<dc:creator>Scoundrel</dc:creator>
				<category><![CDATA[Admin-tips]]></category>
		<category><![CDATA[Networks]]></category>
		<category><![CDATA[proxy]]></category>
		<category><![CDATA[socks]]></category>
		<category><![CDATA[ssh]]></category>
		<category><![CDATA[tunnel]]></category>

		<guid isPermaLink="false">http://blog.kovyrin.net/?p=156</guid>
		<description><![CDATA[Month ago I was on a vacation and as usual even though our hotel provided us with an internet connection on a pretty decent speeds, I wasn&#8217;t able to work there because they&#8217;ve banned all tcp ports but some major ones (like 80, 21, etc) and I needed to be able to use ssh, mysql, [...]]]></description>
			<content:encoded><![CDATA[<p>Month ago I was on a vacation and as usual even though our hotel provided us with an internet connection on a pretty decent speeds, I wasn&#8217;t able to work there because they&#8217;ve banned all tcp ports but some major ones (like 80, 21, etc) and I needed to be able to use ssh, mysql, IMs and other non-web software.</p>
<p>After a short research I&#8217;ve found a pretty simple to set up and easy to use approach to such a connection problems I&#8217;d like to describe here.</p>
<p><span id="more-156"></span></p>
<p>First, you&#8217;ll need someone (or you can do it before leaving home) to start an ssh daemon on port 80 on one of your servers. I use one of my Slicehub slices for this to permanently have an ability to use it. You can do it like this (if it is a temporary solution):</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: #666666; font-style: italic;"># `which sshd` -p 80</span></div></td></tr></tbody></table></div>
<p><i>Notice: this <tt>`which sshd`</tt> was used because on some OSes sshd does not want to start w/o an absolute path to its binary.</i></p>
<p>If you&#8217;d like to have it permanent, just add one line to your <tt>/etc/ssh/sshd_config</tt> file and restart your ssh server:</p>
<div class="codecolorer-container text 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="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">Port 80</div></td></tr></tbody></table></div>
<p>OK, this step is finished and you have sshd listening on port 80. Now, let&#8217;s imagine you go somewhere and need to use ssh (or ICQ/GTalk/Jabber/MSN/AIM, or torrents) and some weird admin banned all tcp ports but port 80. Here&#8217;s what you&#8217;ll need to do:</p>
<ol>
<li>You open terminal on your machine (most likely it&#8217;ll be your laptop)</li>
<li>You run the following command:
<div class="codecolorer-container text 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="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">$ ssh -D 1080 -p 80 -v user@your-host.com &quot;sleep 1000000&quot;</div></td></tr></tbody></table></div>
</li>
<li>Use 127.0.0.1 (port 1080) as your local socks4/5 proxy server (no authorization) in any software you want</li>
</ol>
<p>When you have theses steps are finished, you can go to any place you want and wherever you have an access to web servers, you&#8217;ll have an access to anything you need (tested on: ssh, safari, adium, vuze torrent client, rubygems, macports).</p>
<p><em>P. S.</em> On my macbook to make this thing even more useful I&#8217;ve installed <b>tsocks</b> port (non-mac users, check it out <a href="http://giig.ugr.es/~rgarcia/tsocks/">here</a>).</p>

<p><a href="http://feedads.g.doubleclick.net/~a/beNMi8RwlqaXbB49OEJFUFTnQ9c/0/da"><img src="http://feedads.g.doubleclick.net/~a/beNMi8RwlqaXbB49OEJFUFTnQ9c/0/di" border="0" ismap="true"></img></a><br/>
<a href="http://feedads.g.doubleclick.net/~a/beNMi8RwlqaXbB49OEJFUFTnQ9c/1/da"><img src="http://feedads.g.doubleclick.net/~a/beNMi8RwlqaXbB49OEJFUFTnQ9c/1/di" border="0" ismap="true"></img></a></p><div class="feedflare">
<a href="http://feeds.feedburner.com/~ff/Homo-Adminus-Ru?a=mnCmTXCykZM:pnnCF1RctsY:V_sGLiPBpWU"><img src="http://feeds.feedburner.com/~ff/Homo-Adminus-Ru?i=mnCmTXCykZM:pnnCF1RctsY:V_sGLiPBpWU" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/Homo-Adminus-Ru?a=mnCmTXCykZM:pnnCF1RctsY:F7zBnMyn0Lo"><img src="http://feeds.feedburner.com/~ff/Homo-Adminus-Ru?i=mnCmTXCykZM:pnnCF1RctsY:F7zBnMyn0Lo" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/Homo-Adminus-Ru?a=mnCmTXCykZM:pnnCF1RctsY:yIl2AUoC8zA"><img src="http://feeds.feedburner.com/~ff/Homo-Adminus-Ru?d=yIl2AUoC8zA" border="0"></img></a>
</div>]]></content:encoded>
			<wfw:commentRss>http://kovyrin.net/2008/12/11/using-ssh-tunnel-connection-as-a-socks5-proxy/feed/</wfw:commentRss>
		<slash:comments>6</slash:comments>
		<feedburner:origLink>http://kovyrin.net/2008/12/11/using-ssh-tunnel-connection-as-a-socks5-proxy/</feedburner:origLink></item>
		<item>
		<title>Lighttpd Book from Packt – Great Thanksgiving Present</title>
		<link>http://feedproxy.google.com/~r/Homo-Adminus-Ru/~3/z_eg4N9WZW4/</link>
		<comments>http://kovyrin.net/2008/11/27/lighttpd-book-from-packt-great-thanksgiving-present/#comments</comments>
		<pubDate>Thu, 27 Nov 2008 07:15:30 +0000</pubDate>
		<dc:creator>Scoundrel</dc:creator>
				<category><![CDATA[Admin-tips]]></category>
		<category><![CDATA[Networks]]></category>
		<category><![CDATA[books]]></category>
		<category><![CDATA[Lighttpd]]></category>
		<category><![CDATA[Nginx]]></category>

		<guid isPermaLink="false">http://blog.kovyrin.net/?p=146</guid>
		<description><![CDATA[Many people know me as a nginx web server evangelist. But as (IMHO) any professional I think that it is really rewarding to know as much as possible about all the tools available on the market so every time you need to make a decision on some technical issue, you&#8217;d consider all pros and cons [...]]]></description>
			<content:encoded><![CDATA[<p>Many people know me as a nginx web server evangelist. But as (IMHO) any professional I think that it is really rewarding to know as much as possible about all the tools available on the market so every time you need to make a decision on some technical issue, you&#8217;d consider all pros and cons based on my own knowledge. </p>
<p>This is why when I received an email from <a href="http://www.packtpub.com/">Packt</a> company asking if I&#8217;d like to read and review <a href="http://www.packtpub.com/lighttpd/book">their book on Lighttpd</a> I decided to give it a shot (I usually do not review any books because I do not always have enough time to read a book thoroughly to be able to write a review). So, here are my impressions from this book.</p>
<p><span id="more-146"></span></p>
<p>First, when I received the book, I was in doubt: how such a small book could cover so flexible and multi-purpose piece of software like <a href="http://www.lighttpd.net/">Lighttpd</a>? But after reading a few first chapters I understood that the book is written as any <b>professional</b> book should be written &#8211; no weird long chapters explaining what is Internet, web server, HTTP or HTML. From the first pages you dive to the world of Lighttpd starting from installation and going through the most popular and useful configuration options to really interesting virtual hosting configurations.</p>
<p>Pretty interesting security-related chapters explain typical process of obtaining and installing SSL certificates. This always was the most painful process for me in almost any web setup I worked on, don&#8217;t know why though, this process seemed kind of messy for me, but I think those chapters could help novice web server administrators understand the process of securing a web server. </p>
<p>For many new lighttpd users the most interesting chapters should be ones that explain typical problems and use cases of using apache with or instead of apache servers and describe how to set up lighttpd to be used with the most popular backend web software (Ruby on Rails, wordpress, phpmyadmin and more). From my consulting experience I should say that this is the most popular lighttpd deployment scheme (lighttpd frontend + some backends) and this is why these chapters should be the most interesting for new users. As a RoR server administrator I wish authors would explain more different deployment options for Rails, but of course I understand that the book is dedicated to lighttpd, not Rails so it is OK to miss some options. </p>
<p>And finally, for experienced users like me who understands all the internals of the web traffic handling and needs to get more control over the traffic there is <b>just awesome</b> chapters on using Lua with lighttpd and writing custom lighttpd modules. I really wish I had such an explanations when I was doing nginx modules development for <a href="http://www.scribd.com">our</a> projects! If I&#8217;d write some lighttpd module I&#8217;d definitely buy this book just because of those Lua and modules-related chapters &#8211; there is not so much info on these topics on the Net and such a solid explanation of the basics of web server extension could help a lot.</p>
<p>So, as the final words on <a href="http://www.packtpub.com/lighttpd/book">this book</a>, I&#8217;d like to recommend it to all web services administrators (even if they do not use lighttpd yet) &#8211; this book explains many fundamental things that you&#8217;d really like to know. As for professional developers/admins, this books could be useful if they are going to work on some advanced configurations or modules for lighttpd (yes, I loved those lua and modules sections).</p>

<p><a href="http://feedads.g.doubleclick.net/~a/QAPVhWwALvnEIXXeHYvSUZDIZOs/0/da"><img src="http://feedads.g.doubleclick.net/~a/QAPVhWwALvnEIXXeHYvSUZDIZOs/0/di" border="0" ismap="true"></img></a><br/>
<a href="http://feedads.g.doubleclick.net/~a/QAPVhWwALvnEIXXeHYvSUZDIZOs/1/da"><img src="http://feedads.g.doubleclick.net/~a/QAPVhWwALvnEIXXeHYvSUZDIZOs/1/di" border="0" ismap="true"></img></a></p><div class="feedflare">
<a href="http://feeds.feedburner.com/~ff/Homo-Adminus-Ru?a=z_eg4N9WZW4:52A6sQcYuvQ:V_sGLiPBpWU"><img src="http://feeds.feedburner.com/~ff/Homo-Adminus-Ru?i=z_eg4N9WZW4:52A6sQcYuvQ:V_sGLiPBpWU" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/Homo-Adminus-Ru?a=z_eg4N9WZW4:52A6sQcYuvQ:F7zBnMyn0Lo"><img src="http://feeds.feedburner.com/~ff/Homo-Adminus-Ru?i=z_eg4N9WZW4:52A6sQcYuvQ:F7zBnMyn0Lo" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/Homo-Adminus-Ru?a=z_eg4N9WZW4:52A6sQcYuvQ:yIl2AUoC8zA"><img src="http://feeds.feedburner.com/~ff/Homo-Adminus-Ru?d=yIl2AUoC8zA" border="0"></img></a>
</div>]]></content:encoded>
			<wfw:commentRss>http://kovyrin.net/2008/11/27/lighttpd-book-from-packt-great-thanksgiving-present/feed/</wfw:commentRss>
		<slash:comments>5</slash:comments>
		<feedburner:origLink>http://kovyrin.net/2008/11/27/lighttpd-book-from-packt-great-thanksgiving-present/</feedburner:origLink></item>
		<item>
		<title>ActiveMQ + Ruby Stomp Client: How to process elements one by one</title>
		<link>http://feedproxy.google.com/~r/Homo-Adminus-Ru/~3/wGh930t7wBU/</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>

<p><a href="http://feedads.g.doubleclick.net/~a/pN_5XGq8dMAa2ctOIXqoCiJfpl4/0/da"><img src="http://feedads.g.doubleclick.net/~a/pN_5XGq8dMAa2ctOIXqoCiJfpl4/0/di" border="0" ismap="true"></img></a><br/>
<a href="http://feedads.g.doubleclick.net/~a/pN_5XGq8dMAa2ctOIXqoCiJfpl4/1/da"><img src="http://feedads.g.doubleclick.net/~a/pN_5XGq8dMAa2ctOIXqoCiJfpl4/1/di" border="0" ismap="true"></img></a></p><div class="feedflare">
<a href="http://feeds.feedburner.com/~ff/Homo-Adminus-Ru?a=wGh930t7wBU:gIwFmVtAHMA:V_sGLiPBpWU"><img src="http://feeds.feedburner.com/~ff/Homo-Adminus-Ru?i=wGh930t7wBU:gIwFmVtAHMA:V_sGLiPBpWU" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/Homo-Adminus-Ru?a=wGh930t7wBU:gIwFmVtAHMA:F7zBnMyn0Lo"><img src="http://feeds.feedburner.com/~ff/Homo-Adminus-Ru?i=wGh930t7wBU:gIwFmVtAHMA:F7zBnMyn0Lo" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/Homo-Adminus-Ru?a=wGh930t7wBU:gIwFmVtAHMA:yIl2AUoC8zA"><img src="http://feeds.feedburner.com/~ff/Homo-Adminus-Ru?d=yIl2AUoC8zA" border="0"></img></a>
</div>]]></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>
		<feedburner:origLink>http://kovyrin.net/2008/10/30/activemq-ruby-stomp-client-how-to-process-elements-one-by-one/</feedburner:origLink></item>
	</channel>
</rss>
