<?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>Oleksiy Kovyrin</title>
	
	<link>http://kovyrin.net</link>
	<description>Yet Another Admin's blog</description>
	<lastBuildDate>Mon, 13 May 2013 15:18:46 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.2.1</generator>
		<atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="self" type="application/rss+xml" href="http://feeds.feedburner.com/Homo-Adminus-Ru" /><feedburner:info uri="homo-adminus-ru" /><atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="hub" href="http://pubsubhubbub.appspot.com/" /><item>
		<title>New Chapter: Swiftype</title>
		<link>http://feedproxy.google.com/~r/Homo-Adminus-Ru/~3/0FWNMRjgEdc/</link>
		<comments>http://kovyrin.net/2013/01/31/new-chapter-swiftype/#comments</comments>
		<pubDate>Thu, 31 Jan 2013 01:49:39 +0000</pubDate>
		<dc:creator>Oleksiy Kovyrin</dc:creator>
				<category><![CDATA[Blog]]></category>
		<category><![CDATA[General]]></category>
		<category><![CDATA[My Projects]]></category>
		<category><![CDATA[job]]></category>
		<category><![CDATA[search]]></category>
		<category><![CDATA[startup]]></category>
		<category><![CDATA[swiftype]]></category>
		<category><![CDATA[work]]></category>

		<guid isPermaLink="false">http://kovyrin.net/?p=613</guid>
		<description><![CDATA[So, after a few weeks of looking for a new job I&#8217;m really excited to start my journey in a young, but very ambitious startup called Swiftype which is focused on developing a technology for private site search, that could be used on everything from small blogs to large product sites. The company is growing [...]]]></description>
			<content:encoded><![CDATA[<p>So, after a few weeks of <a href="http://kovyrin.net/2013/01/14/looking-for-a-new-gig/">looking for a new job</a> I&#8217;m really excited to start my journey in a young, but very ambitious startup called <a href="https://swiftype.com/" title="Private site search service" target="_blank">Swiftype</a> which is focused on developing a technology for private site search, that could be used on everything from small blogs to large product sites. The company is growing really fast and I&#8217;m going to lead all the work on infrastructure, build the ops team and hope to get a chance to do some coding along the way.</p>
<p>Stay tuned &#8211; I really hope to finally get a chance to do more blogging this year. <img src='http://kovyrin.net/wp-includes/images/smilies/icon_smile.gif' alt=':-)' class='wp-smiley' /> </p>
<div class="feedflare">
<a href="http://feeds.feedburner.com/~ff/Homo-Adminus-Ru?a=0FWNMRjgEdc:IUjsB3nVnqc:V_sGLiPBpWU"><img src="http://feeds.feedburner.com/~ff/Homo-Adminus-Ru?i=0FWNMRjgEdc:IUjsB3nVnqc:V_sGLiPBpWU" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/Homo-Adminus-Ru?a=0FWNMRjgEdc:IUjsB3nVnqc:F7zBnMyn0Lo"><img src="http://feeds.feedburner.com/~ff/Homo-Adminus-Ru?i=0FWNMRjgEdc:IUjsB3nVnqc:F7zBnMyn0Lo" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/Homo-Adminus-Ru?a=0FWNMRjgEdc:IUjsB3nVnqc: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/2013/01/31/new-chapter-swiftype/feed/</wfw:commentRss>
		<slash:comments>3</slash:comments>
		<feedburner:origLink>http://kovyrin.net/2013/01/31/new-chapter-swiftype/</feedburner:origLink></item>
		<item>
		<title>Looking for a New Gig</title>
		<link>http://feedproxy.google.com/~r/Homo-Adminus-Ru/~3/axP8GCX7Eec/</link>
		<comments>http://kovyrin.net/2013/01/14/looking-for-a-new-gig/#comments</comments>
		<pubDate>Mon, 14 Jan 2013 17:18:35 +0000</pubDate>
		<dc:creator>Oleksiy Kovyrin</dc:creator>
				<category><![CDATA[Blog]]></category>
		<category><![CDATA[General]]></category>
		<category><![CDATA[My Projects]]></category>
		<category><![CDATA[job search]]></category>
		<category><![CDATA[new job]]></category>
		<category><![CDATA[resume]]></category>
		<category><![CDATA[work]]></category>

		<guid isPermaLink="false">http://kovyrin.net/?p=602</guid>
		<description><![CDATA[As of today I&#8217;m no longer working for LivingSocial and I&#8217;m looking for the next thing to work on. Since my family is in Toronto and I have an apartment (mortgage) here, I&#8217;m not looking to relocate and currently looking for something remote (I have 7+ years of remote work experience) or something local in [...]]]></description>
			<content:encoded><![CDATA[<p>As of today I&#8217;m no longer working for LivingSocial and I&#8217;m looking for the next thing to work on. Since my family is in Toronto and I have an apartment (mortgage) here, I&#8217;m not looking to relocate and currently looking for something remote (I have 7+ years of remote work experience) or something local in Toronto.</p>
<p>For more information on my background, please check <a href="https://github.com/kovyrin">my Github profile</a>, <a href="http://ca.linkedin.com/in/kovyrin/">my linkedin profile</a> or the <a href="http://kovyrin.net/resume/">resume section</a> on this blog. If you need to contact me, feel free to use any channels listed on the <a href="http://kovyrin.net/contact/">contacts page</a>.</p>
<p><em><strong>Update: After a few initial interviews I&#8217;d like to update this post with a bit more details on what I&#8217;m looking for in the new position. </strong></p>
<p>First of all, I&#8217;m really not sure I want to be yet another ops engineer working on &#8220;everything ops&#8221; in my next company. If I&#8217;d be to join a company as a regular ops engineer, I&#8217;d prefer it to be a clearly defined role with a clear focus on some set of challenging problems. I&#8217;m honestly tired of setting up cacti/nagios/chef at this point and would like the job to be a little bit more challenging. </p>
<p>Though even more I&#8217;m interested in being able to make strategic technical decisions for an operations team and apply my experience and knowledge for solving challenging tasks with a dedicated team of ops engineers. This could be anything from a tech ops team lead role (in a medium/large companies) to a director of technical operations (in a small-to-medium sized startups).</em></p>
<p><em><strong>Update: Ok, I&#8217;ve found a <a href="http://kovyrin.net/2013/01/31/new-chapter-swiftype/">new job</a> &#8211; I work for <a href="https://swiftype.com/" title="Custom site search" target="_blank">Swiftype</a> now!</strong></em></p>
<div class="feedflare">
<a href="http://feeds.feedburner.com/~ff/Homo-Adminus-Ru?a=axP8GCX7Eec:PWT3uv8Ezss:V_sGLiPBpWU"><img src="http://feeds.feedburner.com/~ff/Homo-Adminus-Ru?i=axP8GCX7Eec:PWT3uv8Ezss:V_sGLiPBpWU" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/Homo-Adminus-Ru?a=axP8GCX7Eec:PWT3uv8Ezss:F7zBnMyn0Lo"><img src="http://feeds.feedburner.com/~ff/Homo-Adminus-Ru?i=axP8GCX7Eec:PWT3uv8Ezss:F7zBnMyn0Lo" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/Homo-Adminus-Ru?a=axP8GCX7Eec:PWT3uv8Ezss: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/2013/01/14/looking-for-a-new-gig/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		<feedburner:origLink>http://kovyrin.net/2013/01/14/looking-for-a-new-gig/</feedburner:origLink></item>
		<item>
		<title>Momentum MTA Performance Tuning Tips</title>
		<link>http://feedproxy.google.com/~r/Homo-Adminus-Ru/~3/aMjvuL6CJTM/</link>
		<comments>http://kovyrin.net/2012/01/07/momentum-ecelerity-tuning-tips/#comments</comments>
		<pubDate>Sat, 07 Jan 2012 18:40:25 +0000</pubDate>
		<dc:creator>Oleksiy Kovyrin</dc:creator>
				<category><![CDATA[Admin-tips]]></category>
		<category><![CDATA[ecelerity]]></category>
		<category><![CDATA[mail]]></category>
		<category><![CDATA[Momentum]]></category>
		<category><![CDATA[MTA]]></category>
		<category><![CDATA[performance]]></category>
		<category><![CDATA[tuning]]></category>

		<guid isPermaLink="false">http://kovyrin.net/?p=568</guid>
		<description><![CDATA[This post is being constantly updated as we find out more useful information on Momentum tuning. Last update: 2012-05-05. About 2 months ago I&#8217;ve joined LivingSocial technical operations team and one of my first tasks there was to figure out a way to make our MTAs perform better and deliver faster. We use a really [...]]]></description>
			<content:encoded><![CDATA[<p><em>This post is being constantly updated as we find out more useful information on Momentum tuning. Last update: 2012-05-05.</em></p>
<p>About 2 months ago I&#8217;ve joined <a href="http://www.livingsocial.com/">LivingSocial</a> technical operations team and one of my first tasks there was to figure out a way to make our MTAs perform better and deliver faster. We use a really great product called <a href="http://www.messagesystems.com/products-momentum-outbound.php">Momentum MTA</a> (former Ecelerity) and it is really fast, but it is always good to be able to squeeze as much performance as possible so I&#8217;ve started looking for a ways to make our system faster.</p>
<p>While working on it I&#8217;ve created a set of scripts to integrate Momentum with Graphite for all kinds of crazy stats graphing, those scripts will be opensourced soon, but for now I&#8217;ve decided to share a few tips about performance-related changes we&#8217;ve made to improve our performance at least 2x:</p>
<p><span id="more-568"></span></p>
<ol>
<li><strong>Use EXT2 Filesystem for the spool storage</strong> – After a lot of benchmarking we&#8217;ve noticed that amounts of I/O we&#8217;ve been doing was way too high compared to our throughput. Some investigation showed that EXT3 filesystem we were using for the spool partition had way too high metadata update overhead because of the fact that the spool storage uses a lot of really small files. Switching to EXT2 helped us gain at least 50-75% additional performance. Additional performance gain was caused by turning on <tt>noatime</tt> option for our spool.
<p><a href="http://archives.neohapsis.com/archives/postfix/2006-01/1916.html">There</a> <a href="http://www.dovecot.org/list/dovecot/2011-August/060574.html">are</a> <a href="http://www.thesmbexchange.com/eng/qmail_fs_benchmark.html">some</a> <a href="http://www.htiweb.inf.br/benchmark/fsbench.htm">sources</a> that claim using XFS for spool directories is a better option, but we&#8217;ve decided to stick with EXT2 for now.</li>
<li><strong>Do not use <tt>%h{X}</tt> macro in your custom logs</strong> – Custom logs is an awesome feature of momentum and we use it to log our bounces along with some information from mail headers. Unfortunately the most straighforward thing to do (using <tt>%h{X}</tt> macro) was not the best option for I/O loaded servers because every time Momentum needs to log a bounce it needs to swap message body in from the disk and parse it to get you the header value.
<p>To solve this issue we&#8217;ve created a Sieve+ policy script that would extract the headers we need from a message during initial spooling phase (when the message is still in memory) and put those values to the message metadata. This way when we need to log those values we wouldn&#8217;t need to swap message body in from the disk. Here is the Sieve script to extract header value:</p>
<div class="codecolorer-container perl 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="perl codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap"><a href="http://perldoc.perl.org/functions/require.html"><span style="color: #000066;">require</span></a> <span style="color: #009900;">&#91;</span> <span style="color: #ff0000;">&quot;ec_header_get&quot;</span><span style="color: #339933;">,</span> <span style="color: #ff0000;">&quot;vctx_mess_set&quot;</span><span style="color: #339933;">,</span> <span style="color: #ff0000;">&quot;ec_log&quot;</span> <span style="color: #009900;">&#93;</span><span style="color: #339933;">;</span><br />
<br />
<span style="color: #666666; font-style: italic;"># Extract x-ls-send-id header to LsSendId context variable </span><br />
<span style="color: #666666; font-style: italic;"># (later used in deliver log)</span><br />
<span style="color: #009900;">&#40;</span><span style="color: #0000ff;">$send_id</span><span style="color: #009900;">&#41;</span> <span style="color: #339933;">=</span> ec_header_get <span style="color: #ff0000;">&quot;x-ls-send-id&quot;</span><span style="color: #339933;">;</span><br />
vctx_mess_set <span style="color: #ff0000;">&quot;LsSendId&quot;</span> <span style="color: #0000ff;">$send_id</span><span style="color: #339933;">;</span></div></td></tr></tbody></table></div>
<p>After this we could use it in a custom logger like this:</p>
<div class="codecolorer-container nginx 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="nginx codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">custom_logger <span style="color: #7f007f;">&quot;custom_logger1&quot;</span><br />
<span style="color: #66cc66;">&#123;</span><br />
&nbsp; delivery_logfile = <span style="color: #7f007f;">&quot;cluster:///var/log/ecelerity/ls-delivery_log.cluster=&gt;master&quot;</span><br />
&nbsp; delivery_format = <span style="color: #7f007f;">&quot;%t@%BI@%i@%CI@D@%r@%R@%m@%M@%H@%p@%g@%b@%vctx_mess{LsSendId}&quot;</span><br />
&nbsp; delivery_log_mode = 0664<br />
<span style="color: #66cc66;">&#125;</span></div></td></tr></tbody></table></div>
</li>
<li><strong>Give more RAM to Momentum</strong> – When Momentum receives a message, it stores it to the disk (as required by SMTP standard) and then tries to deliver the copy it has in memory, if delivery succeeds, on-disk copy is unlinked. The problem with a really have outbound traffic load is that momentum needs to keep tons of emails in memory, but by default it could only hold 250 messages. With a load of 250-500 messages a second this is just too small.
<p>To change this limit we&#8217;ve increased <tt><a href="https://support.messagesystems.com/docs/web-ref3/conf.ref.max_resident_active_queue.php">Max_Resident_Active_Queue</a></tt> parameter and changed it to 1000000 (of course we made sure have enough RAM to hold that many messages if needed) and <tt><a href="https://support.messagesystems.com/docs/web-ref3/conf.ref.max_resident_messages.php">Max_Resident_Messages</a></tt> to 0 (which means unlimited). This allows Momentum keep as many messages resident as possible and reduce the load caused by swap-in operations required for re-delivery attempts, etc.</li>
<li><strong>Choose a proper size for your I/O-related thread pools</strong> – in default Momentum config they set SwapIn and SwapOut <a href="https://support.messagesystems.com/docs/web-ref3/conf.ref.threadpool.php">thread pool</a> sizes to 20. Under really high load even on our 4xSAS15k RAID10 this tends to be too high value. We&#8217;ve switched those pools to 8 threads each and it helped to reduce I/O contention and overall I/O throughput. </li>
<li><strong>Disable adaptive delivery if you do not need it</strong> (especially critical in 3.4 with cluster-aware adaptive delivery) &#8211; in our case it allowed us to increase single server throughput from 1.5M to 2M messages/hour for servers with warmed-up bindings that do not need adaptive warmup and other fancy features. The reason behind this difference is in the fact that to be able to do adaptive delivery magic, your MTA needs to spend considerable amounts of resources on all those decisions that make adaptive really valuable for warming-up new IPs (adaptive backoff on ISP blocks, moving mail between nodes based on bindings suppression information). After you disable Adaptive, make sure yo restart your <tt>ecelerity</tt> process and check that module is not loaded (using <tt>module list</tt> console command).</li>
<li><strong>Remove <tt>Replicate</tt> clauses from your configs</strong> &#8211; if you do not use <a href="https://support.messagesystems.com/docs/web-ref/cluster.config.replication.php">data replication</a> features in momentum (<tt>inbound_cidr</tt>, <tt>metrics</tt>, etc), disabling all replicate stanzas in your config files could give you a great performance boost because apparently this replication stuff takes a lot of resources and greatly reduces throughput (though it could be useful in some cases).</li>
</ol>
<p>As a summary, I&#8217;d like to note, that as with any optimizations, before tuning your system it really helps to set up as much monitoring for your MTA servers as possible: cacti graphs, graphite, ganglia or something else &#8211; does not matter. Just make sure you see all the aspects of your system performance and understand what is going on with your system before changing any performance-related settings.</p>
<div class="feedflare">
<a href="http://feeds.feedburner.com/~ff/Homo-Adminus-Ru?a=aMjvuL6CJTM:kXxTOu-ISSg:V_sGLiPBpWU"><img src="http://feeds.feedburner.com/~ff/Homo-Adminus-Ru?i=aMjvuL6CJTM:kXxTOu-ISSg:V_sGLiPBpWU" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/Homo-Adminus-Ru?a=aMjvuL6CJTM:kXxTOu-ISSg:F7zBnMyn0Lo"><img src="http://feeds.feedburner.com/~ff/Homo-Adminus-Ru?i=aMjvuL6CJTM:kXxTOu-ISSg:F7zBnMyn0Lo" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/Homo-Adminus-Ru?a=aMjvuL6CJTM:kXxTOu-ISSg: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/2012/01/07/momentum-ecelerity-tuning-tips/feed/</wfw:commentRss>
		<slash:comments>5</slash:comments>
		<feedburner:origLink>http://kovyrin.net/2012/01/07/momentum-ecelerity-tuning-tips/</feedburner:origLink></item>
		<item>
		<title>Quick (and dirty) Patch for Ruby Enterprise Edition 2011.03 to Prevent Hash Collision Attacks</title>
		<link>http://feedproxy.google.com/~r/Homo-Adminus-Ru/~3/V534dICoisA/</link>
		<comments>http://kovyrin.net/2011/12/29/ree-hash-collision-patch/#comments</comments>
		<pubDate>Thu, 29 Dec 2011 18:59:39 +0000</pubDate>
		<dc:creator>Oleksiy Kovyrin</dc:creator>
				<category><![CDATA[Admin-tips]]></category>
		<category><![CDATA[Development]]></category>
		<category><![CDATA[DoS]]></category>
		<category><![CDATA[patch]]></category>
		<category><![CDATA[Ruby]]></category>
		<category><![CDATA[rubyee]]></category>

		<guid isPermaLink="false">http://kovyrin.net/?p=550</guid>
		<description><![CDATA[As you may have heard, this week on December 28, 2011, a group of security experts released information about a nasty problem in almost all languages and platforms related to hash function collisions and possibility of using those for DoS attack on web applications. Ruby core team released new 1.8.7-p357 version with the problem fixed. [...]]]></description>
			<content:encoded><![CDATA[<p>As you may have heard, this week on December 28, 2011, a group of security experts <a href="http://events.ccc.de/congress/2011/Fahrplan/events/4680.en.html">released information</a> about a nasty problem in almost all languages and platforms related to hash function collisions and possibility of using those for DoS attack on web applications. </p>
<p>Ruby core team <a href="http://www.ruby-forum.com/topic/3312298#1038441">released new 1.8.7-p357 version</a> with the <a href="http://www.ruby-lang.org/en/news/2011/12/28/denial-of-service-attack-was-found-for-rubys-hash-algorithm/">problem</a> fixed. JRuby development team came out with the new <a href="http://jruby.org/2011/12/27/jruby-1-6-5-1">1.6.5.1 release</a>. Unfortunately 2 days after the release there is still no information from Ruby Enterprise Edition team on what to do with all the ree deployments. </p>
<p>So, since there is no patch for Ruby Enterprise Edition 2011.03 to prevent an attack, I&#8217;ve quickly ported ruby core patch to be used with the latest ree release. Here it is:</p>
<div class="codecolorer-container text twitlight" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;height:300px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br />3<br />4<br />5<br />6<br />7<br />8<br />9<br />10<br />11<br />12<br />13<br />14<br />15<br />16<br />17<br />18<br />19<br />20<br />21<br />22<br />23<br />24<br />25<br />26<br />27<br />28<br />29<br />30<br />31<br />32<br />33<br />34<br />35<br />36<br />37<br />38<br />39<br />40<br />41<br />42<br />43<br />44<br />45<br />46<br />47<br />48<br />49<br />50<br />51<br />52<br />53<br />54<br />55<br />56<br />57<br />58<br />59<br />60<br />61<br />62<br />63<br />64<br />65<br />66<br />67<br />68<br />69<br />70<br />71<br />72<br />73<br />74<br />75<br />76<br />77<br />78<br />79<br />80<br />81<br />82<br />83<br />84<br />85<br />86<br />87<br />88<br />89<br />90<br />91<br />92<br />93<br />94<br />95<br />96<br />97<br />98<br />99<br />100<br />101<br />102<br />103<br />104<br />105<br />106<br />107<br />108<br />109<br />110<br />111<br />112<br />113<br />114<br />115<br />116<br />117<br />118<br />119<br />120<br />121<br />122<br />123<br />124<br />125<br />126<br />127<br />128<br />129<br />130<br />131<br />132<br />133<br />134<br />135<br />136<br />137<br />138<br />139<br />140<br />141<br />142<br />143<br />144<br />145<br />146<br />147<br />148<br />149<br />150<br />151<br />152<br />153<br />154<br />155<br />156<br />157<br />158<br />159<br />160<br />161<br />162<br />163<br />164<br />165<br />166<br />167<br />168<br />169<br />170<br />171<br />172<br />173<br />174<br />175<br />176<br />177<br />178<br />179<br />180<br />181<br />182<br />183<br />184<br />185<br />186<br />187<br />188<br />189<br />190<br />191<br />192<br />193<br />194<br />195<br />196<br />197<br />198<br />199<br />200<br />201<br />202<br />203<br />204<br />205<br />206<br />207<br />208<br />209<br />210<br />211<br />212<br />213<br />214<br />215<br />216<br />217<br />218<br />219<br />220<br />221<br />222<br />223<br />224<br />225<br />226<br />227<br />228<br />229<br />230<br />231<br />232<br />233<br />234<br />235<br />236<br />237<br />238<br />239<br />240<br />241<br />242<br />243<br />244<br />245<br />246<br />247<br />248<br />249<br />250<br />251<br />252<br />253<br />254<br />255<br />256<br />257<br />258<br />259<br />260<br />261<br />262<br />263<br />264<br />265<br />266<br />267<br />268<br />269<br />270<br />271<br />272<br />273<br />274<br />275<br />276<br />277<br />278<br />279<br />280<br />281<br />282<br />283<br />284<br />285<br />286<br />287<br />288<br />289<br />290<br />291<br />292<br />293<br />294<br />295<br />296<br />297<br />298<br />299<br />300<br /></div></td><td><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">From 4f69a748ab820c6a8bd204f94d13d970847f575c Mon Sep 17 00:00:00 2001<br />
From: RPM Builder &lt;rpmbuild@livingsocial.com&gt;<br />
Date: Thu, 29 Dec 2011 19:48:52 +0000<br />
Subject: [PATCH] Backport randomized hash patch from upstream<br />
<br />
---<br />
&nbsp;source/inits.c &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;| &nbsp; &nbsp;4 ++<br />
&nbsp;source/random.c &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; | &nbsp; 74 ++++++++++++++++++++++++++++++---------<br />
&nbsp;source/st.c &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; | &nbsp; 14 +++++++-<br />
&nbsp;source/string.c &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; | &nbsp; &nbsp;7 +++-<br />
&nbsp;source/test/ruby/test_string.rb | &nbsp; 13 +++++++<br />
&nbsp;source/version.c &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;| &nbsp; &nbsp;2 +-<br />
&nbsp;6 files changed, 94 insertions(+), 20 deletions(-)<br />
<br />
diff --git a/source/inits.c b/source/inits.c<br />
index 947bbbe..a0e061f 100644<br />
--- a/source/inits.c<br />
+++ b/source/inits.c<br />
@@ -38,6 +38,7 @@ void Init_Precision _((void));<br />
&nbsp;void Init_sym _((void));<br />
&nbsp;void Init_process _((void));<br />
&nbsp;void Init_Random _((void));<br />
+void Init_RandomSeed _((void));<br />
&nbsp;void Init_Range _((void));<br />
&nbsp;void Init_Regexp _((void));<br />
&nbsp;void Init_signal _((void));<br />
@@ -46,10 +47,13 @@ void Init_Struct _((void));<br />
&nbsp;void Init_Time _((void));<br />
&nbsp;void Init_var_tables _((void));<br />
&nbsp;void Init_version _((void));<br />
+void Init_st _((void));<br />
&nbsp;<br />
&nbsp;void<br />
&nbsp;rb_call_inits()<br />
&nbsp;{<br />
+ &nbsp; &nbsp;Init_RandomSeed();<br />
+ &nbsp; &nbsp;Init_st();<br />
&nbsp; &nbsp; &nbsp;Init_sym();<br />
&nbsp; &nbsp; &nbsp;Init_var_tables();<br />
&nbsp; &nbsp; &nbsp;Init_Object();<br />
diff --git a/source/random.c b/source/random.c<br />
index 258b0b2..790eda8 100644<br />
--- a/source/random.c<br />
+++ b/source/random.c<br />
@@ -189,6 +189,7 @@ rb_genrand_real(void)<br />
&nbsp;#include &lt;fcntl.h&gt;<br />
&nbsp;#endif<br />
&nbsp;<br />
+static int seed_initialized = 0;<br />
&nbsp;static VALUE saved_seed = INT2FIX(0);<br />
&nbsp;<br />
&nbsp;static VALUE<br />
@@ -250,27 +251,22 @@ rand_init(vseed)<br />
&nbsp; &nbsp; &nbsp;return old;<br />
&nbsp;}<br />
&nbsp;<br />
-static VALUE<br />
-random_seed()<br />
+#define DEFAULT_SEED_LEN (4 * sizeof(long))<br />
+<br />
+static void<br />
+fill_random_seed(ptr)<br />
+ &nbsp; &nbsp;char *ptr;<br />
&nbsp;{<br />
&nbsp; &nbsp; &nbsp;static int n = 0;<br />
+ &nbsp; &nbsp;unsigned long *seed;<br />
&nbsp; &nbsp; &nbsp;struct timeval tv;<br />
&nbsp; &nbsp; &nbsp;int fd;<br />
&nbsp; &nbsp; &nbsp;struct stat statbuf;<br />
+ &nbsp; &nbsp;char *buf = (char*)ptr;<br />
&nbsp;<br />
- &nbsp; &nbsp;int seed_len;<br />
- &nbsp; &nbsp;BDIGIT *digits;<br />
- &nbsp; &nbsp;unsigned long *seed;<br />
- &nbsp; &nbsp;NEWOBJ(big, struct RBignum);<br />
- &nbsp; &nbsp;OBJSETUP(big, rb_cBignum, T_BIGNUM);<br />
-<br />
- &nbsp; &nbsp;seed_len = 4 * sizeof(long);<br />
- &nbsp; &nbsp;big-&gt;sign = 1;<br />
- &nbsp; &nbsp;big-&gt;len = seed_len / SIZEOF_BDIGITS + 1;<br />
- &nbsp; &nbsp;digits = big-&gt;digits = ALLOC_N(BDIGIT, big-&gt;len);<br />
- &nbsp; &nbsp;seed = (unsigned long *)big-&gt;digits;<br />
+ &nbsp; &nbsp;seed = (unsigned long *)buf;<br />
&nbsp;<br />
- &nbsp; &nbsp;memset(digits, 0, big-&gt;len * SIZEOF_BDIGITS);<br />
+ &nbsp; &nbsp;memset(buf, 0, DEFAULT_SEED_LEN);<br />
&nbsp;<br />
&nbsp;#ifdef S_ISCHR<br />
&nbsp; &nbsp; &nbsp;if ((fd = open(&quot;/dev/urandom&quot;, O_RDONLY<br />
@@ -285,7 +281,7 @@ random_seed()<br />
&nbsp;#endif<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;)) &gt;= 0) {<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;if (fstat(fd, &amp;statbuf) == 0 &amp;&amp; S_ISCHR(statbuf.st_mode)) {<br />
- &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;read(fd, seed, seed_len);<br />
+ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;read(fd, seed, DEFAULT_SEED_LEN);<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;}<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;close(fd);<br />
&nbsp; &nbsp; &nbsp;}<br />
@@ -296,13 +292,37 @@ random_seed()<br />
&nbsp; &nbsp; &nbsp;seed[1] ^= tv.tv_sec;<br />
&nbsp; &nbsp; &nbsp;seed[2] ^= getpid() ^ (n++ &lt;&lt; 16);<br />
&nbsp; &nbsp; &nbsp;seed[3] ^= (unsigned long)&amp;seed;<br />
+}<br />
+<br />
+static VALUE<br />
+make_seed_value(char *ptr)<br />
+{<br />
+ &nbsp; &nbsp;BDIGIT *digits;<br />
+ &nbsp; &nbsp;NEWOBJ(big, struct RBignum);<br />
+ &nbsp; &nbsp;OBJSETUP(big, rb_cBignum, T_BIGNUM);<br />
+<br />
+ &nbsp; &nbsp;RBIGNUM_SET_SIGN(big, 1);<br />
+<br />
+ &nbsp; &nbsp;digits = ALLOC_N(char, DEFAULT_SEED_LEN);<br />
+ &nbsp; &nbsp;RBIGNUM(big)-&gt;digits = digits;<br />
+ &nbsp; &nbsp;RBIGNUM(big)-&gt;len = DEFAULT_SEED_LEN / SIZEOF_BDIGITS;<br />
+<br />
+ &nbsp; &nbsp;MEMCPY(digits, ptr, char, DEFAULT_SEED_LEN);<br />
&nbsp;<br />
&nbsp; &nbsp; &nbsp;/* set leading-zero-guard if need. */<br />
- &nbsp; &nbsp;digits[big-&gt;len-1] = digits[big-&gt;len-2] &lt;= 1 ? 1 : 0;<br />
+ &nbsp; &nbsp;digits[RBIGNUM_LEN(big)-1] = digits[RBIGNUM_LEN(big)-2] &lt;= 1 ? 1 : 0;<br />
&nbsp;<br />
&nbsp; &nbsp; &nbsp;return rb_big_norm((VALUE)big);<br />
&nbsp;}<br />
&nbsp;<br />
+static VALUE<br />
+random_seed(void)<br />
+{<br />
+ &nbsp; &nbsp;char buf[DEFAULT_SEED_LEN];<br />
+ &nbsp; &nbsp;fill_random_seed(buf);<br />
+ &nbsp; &nbsp;return make_seed_value(buf);<br />
+}<br />
+<br />
&nbsp;/*<br />
&nbsp; * &nbsp;call-seq:<br />
&nbsp; * &nbsp; &nbsp; srand(number=0) &nbsp; &nbsp;=&gt; old_seed<br />
@@ -443,6 +463,9 @@ rb_f_rand(argc, argv, obj)<br />
&nbsp; &nbsp; &nbsp;long val, max;<br />
&nbsp;<br />
&nbsp; &nbsp; &nbsp;rb_scan_args(argc, argv, &quot;01&quot;, &amp;vmax);<br />
+ &nbsp; &nbsp;if (!seed_initialized) {<br />
+ &nbsp; &nbsp; &nbsp; rand_init(random_seed());<br />
+ &nbsp; &nbsp;}<br />
&nbsp; &nbsp; &nbsp;switch (TYPE(vmax)) {<br />
&nbsp; &nbsp; &nbsp; &nbsp;case T_FLOAT:<br />
&nbsp; &nbsp; if (RFLOAT(vmax)-&gt;value &lt;= LONG_MAX &amp;&amp; RFLOAT(vmax)-&gt;value &gt;= LONG_MIN) {<br />
@@ -490,10 +513,27 @@ rb_f_rand(argc, argv, obj)<br />
&nbsp; &nbsp; &nbsp;return LONG2NUM(val);<br />
&nbsp;}<br />
&nbsp;<br />
+static char initial_seed[DEFAULT_SEED_LEN];<br />
+<br />
+void<br />
+Init_RandomSeed(void)<br />
+{<br />
+ &nbsp; &nbsp;fill_random_seed(initial_seed);<br />
+ &nbsp; &nbsp;init_by_array((unsigned long*)initial_seed, DEFAULT_SEED_LEN/sizeof(unsigned long));<br />
+ &nbsp; &nbsp;seed_initialized = 1;<br />
+}<br />
+<br />
+static void<br />
+Init_RandomSeed2(void)<br />
+{<br />
+ &nbsp; &nbsp;saved_seed = make_seed_value(initial_seed);<br />
+ &nbsp; &nbsp;memset(initial_seed, 0, DEFAULT_SEED_LEN);<br />
+}<br />
+<br />
&nbsp;void<br />
&nbsp;Init_Random()<br />
&nbsp;{<br />
- &nbsp; &nbsp;rand_init(random_seed());<br />
+ &nbsp; &nbsp;Init_RandomSeed2();<br />
&nbsp; &nbsp; &nbsp;rb_define_global_function(&quot;srand&quot;, rb_f_srand, -1);<br />
&nbsp; &nbsp; &nbsp;rb_define_global_function(&quot;rand&quot;, rb_f_rand, -1);<br />
&nbsp; &nbsp; &nbsp;rb_global_variable(&amp;saved_seed);<br />
diff --git a/source/st.c b/source/st.c<br />
index c16c310..21e157a 100644<br />
--- a/source/st.c<br />
+++ b/source/st.c<br />
@@ -9,6 +9,7 @@<br />
&nbsp;#include &lt;stdlib.h&gt;<br />
&nbsp;#endif<br />
&nbsp;#include &lt;string.h&gt;<br />
+#include &lt;limits.h&gt;<br />
&nbsp;#include &quot;st.h&quot;<br />
&nbsp;<br />
&nbsp;typedef struct st_table_entry st_table_entry;<br />
@@ -521,6 +522,8 @@ st_foreach(table, func, arg)<br />
&nbsp; &nbsp; &nbsp;return 0;<br />
&nbsp;}<br />
&nbsp;<br />
+static unsigned long hash_seed = 0;<br />
+<br />
&nbsp;static int<br />
&nbsp;strhash(string)<br />
&nbsp; &nbsp; &nbsp;register const char *string;<br />
@@ -550,10 +553,11 @@ strhash(string)<br />
&nbsp;<br />
&nbsp; &nbsp; &nbsp;return val + (val &lt;&lt; 15);<br />
&nbsp;#else<br />
- &nbsp; &nbsp;register int val = 0;<br />
+ &nbsp; &nbsp;register unsigned long val = hash_seed;<br />
&nbsp;<br />
&nbsp; &nbsp; &nbsp;while ((c = *string++) != '\0') {<br />
&nbsp; &nbsp; val = val*997 + c;<br />
+ &nbsp; val = (val &lt;&lt; 13) | (val &gt;&gt; (sizeof(st_data_t) * CHAR_BIT - 13));<br />
&nbsp; &nbsp; &nbsp;}<br />
&nbsp;<br />
&nbsp; &nbsp; &nbsp;return val + (val&gt;&gt;5);<br />
@@ -573,3 +577,11 @@ numhash(n)<br />
&nbsp;{<br />
&nbsp; &nbsp; &nbsp;return n;<br />
&nbsp;}<br />
+<br />
+extern unsigned long rb_genrand_int32(void);<br />
+<br />
+void<br />
+Init_st(void)<br />
+{<br />
+ &nbsp; &nbsp;hash_seed = rb_genrand_int32();<br />
+}<br />
diff --git a/source/string.c b/source/string.c<br />
index c6b2301..94a0281 100644<br />
--- a/source/string.c<br />
+++ b/source/string.c<br />
@@ -875,13 +875,15 @@ rb_str_concat(str1, str2)<br />
&nbsp; &nbsp; &nbsp;return str1;<br />
&nbsp;}<br />
&nbsp;<br />
+static unsigned long hash_seed;<br />
+<br />
&nbsp;int<br />
&nbsp;rb_str_hash(str)<br />
&nbsp; &nbsp; &nbsp;VALUE str;<br />
&nbsp;{<br />
&nbsp; &nbsp; &nbsp;register long len = RSTRING(str)-&gt;len;<br />
&nbsp; &nbsp; &nbsp;register char *p = RSTRING(str)-&gt;ptr;<br />
- &nbsp; &nbsp;register int key = 0;<br />
+ &nbsp; &nbsp;register unsigned long key = hash_seed;<br />
&nbsp;<br />
&nbsp;#if defined(HASH_ELFHASH)<br />
&nbsp; &nbsp; &nbsp;register unsigned int g;<br />
@@ -905,6 +907,7 @@ rb_str_hash(str)<br />
&nbsp; &nbsp; &nbsp;while (len--) {<br />
&nbsp; &nbsp; key = key*65599 + *p;<br />
&nbsp; &nbsp; p++;<br />
+ &nbsp; key = (key &lt;&lt; 13) | (key &gt;&gt; ((sizeof(unsigned long) * CHAR_BIT) - 13));<br />
&nbsp; &nbsp; &nbsp;}<br />
&nbsp; &nbsp; &nbsp;key = key + (key&gt;&gt;5);<br />
&nbsp;#endif<br />
@@ -5062,4 +5065,6 @@ Init_String()<br />
&nbsp; &nbsp; &nbsp;rb_fs = Qnil;<br />
&nbsp; &nbsp; &nbsp;rb_define_variable(&quot;$;&quot;, &amp;rb_fs);<br />
&nbsp; &nbsp; &nbsp;rb_define_variable(&quot;$-F&quot;, &amp;rb_fs);<br />
+<br />
+ &nbsp; &nbsp;hash_seed = rb_genrand_int32();<br />
&nbsp;}<br />
diff --git a/source/test/ruby/test_string.rb b/source/test/ruby/test_string.rb<br />
index 5f2c54f..4d97182 100644<br />
--- a/source/test/ruby/test_string.rb<br />
+++ b/source/test/ruby/test_string.rb<br />
@@ -1,4 +1,5 @@<br />
&nbsp;require 'test/unit'<br />
+require File.expand_path('envutil', File.dirname(__FILE__))<br />
&nbsp;<br />
&nbsp;class TestString &lt; Test::Unit::TestCase<br />
&nbsp; &nbsp;def check_sum(str, bits=16)<br />
@@ -29,4 +30,16 @@ class TestString &lt; Test::Unit::TestCase<br />
&nbsp; &nbsp;ensure<br />
&nbsp; &nbsp; &nbsp;$KCODE = original_kcode<br />
&nbsp; &nbsp;end<br />
+<br />
+ &nbsp;def test_hash_random<br />
+ &nbsp; &nbsp;str = 'abc'<br />
+ &nbsp; &nbsp;a = [str.hash.to_s]<br />
+ &nbsp; &nbsp;cmd = sprintf(&quot;%s -e 'print %s.hash'&quot;, EnvUtil.rubybin, str.dump)<br />
+ &nbsp; &nbsp;3.times {<br />
+ &nbsp; &nbsp; &nbsp;IO.popen(cmd, &quot;rb&quot;) {|o|<br />
+ &nbsp; &nbsp; &nbsp; &nbsp;a &lt;&lt; o.read<br />
+ &nbsp; &nbsp; &nbsp;}<br />
+ &nbsp; &nbsp;}<br />
+ &nbsp; &nbsp;assert_not_equal([str.hash.to_s], a.uniq)<br />
+ &nbsp;end<br />
&nbsp;end<br />
diff --git a/source/version.c b/source/version.c<br />
index 8b41cc9..5781cab 100644<br />
--- a/source/version.c<br />
+++ b/source/version.c<br />
@@ -46,7 +46,7 @@ Init_version()<br />
&nbsp; &nbsp; &nbsp;rb_define_global_const(&quot;RUBY_PATCHLEVEL&quot;, INT2FIX(RUBY_PATCHLEVEL));<br />
&nbsp;<br />
&nbsp; &nbsp; &nbsp;snprintf(description, sizeof(description),<br />
- &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &quot;ruby %s (%s %s %d) [%s], MBARI 0x%x, Ruby Enterprise Edition %s&quot;,<br />
+ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &quot;ruby %s (%s %s %d) [%s], MBARI 0x%x, Ruby Enterprise Edition %s (with hash random)&quot;,<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; RUBY_VERSION, RUBY_RELEASE_DATE, RUBY_RELEASE_STR,<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; RUBY_RELEASE_NUM, RUBY_PLATFORM,<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; STACK_WIPE_SITES, REE_VERSION);<br />
-- <br />
1.7.6.4</div></td></tr></tbody></table></div>
<p>You can <a href="https://gist.github.com/1535569">view it</a> or <a href="https://raw.github.com/gist/1535569/b59b5e3e753288fa7a2f23d640a285775ab0879b/ruby-1.8.7-hash-randomize.patch">download it from github</a>. <b>Disclaimer:</b> This is provided as is, no guarantees are provided, etc.</p>
<p><span id="more-550"></span></p>
<p>For more information about the issue, here is a video of the official presentation:</p>
<p><center><br />
<object width="560" height="315"><param name="movie" value="http://www.youtube.com/v/R2Cq3CLI6H8?version=3&amp;hl=en_US"></param><param name="allowFullScreen" value="true"></param><param name="allowscriptaccess" value="always"></param><embed src="http://www.youtube.com/v/R2Cq3CLI6H8?version=3&amp;hl=en_US" type="application/x-shockwave-flash" width="560" height="315" allowscriptaccess="always" allowfullscreen="true"></embed></object><br />
</center></p>
<p><i>Update #1:</i> Updated the patch to apply cleanly on REE (w/o a need to switch directory to <tt>source</tt>).<br />
<i>Update #2:</i> The patch is currently deployed in production at <a href="http://www.scribd.com/">Scribd</a> and is being tested and deployed on <a href="http://www.livingsocial.com/">LivingSocial</a> servers.<br />
<i>Update #3:</i> Phusion released <a href="http://www.rubyenterpriseedition.com/download.html">2011.12 release of REE</a> now, so this patch is not needed anymore.</p>
<div class="feedflare">
<a href="http://feeds.feedburner.com/~ff/Homo-Adminus-Ru?a=V534dICoisA:fJDDu5P_f0A:V_sGLiPBpWU"><img src="http://feeds.feedburner.com/~ff/Homo-Adminus-Ru?i=V534dICoisA:fJDDu5P_f0A:V_sGLiPBpWU" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/Homo-Adminus-Ru?a=V534dICoisA:fJDDu5P_f0A:F7zBnMyn0Lo"><img src="http://feeds.feedburner.com/~ff/Homo-Adminus-Ru?i=V534dICoisA:fJDDu5P_f0A:F7zBnMyn0Lo" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/Homo-Adminus-Ru?a=V534dICoisA:fJDDu5P_f0A: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/2011/12/29/ree-hash-collision-patch/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		<feedburner:origLink>http://kovyrin.net/2011/12/29/ree-hash-collision-patch/</feedburner:origLink></item>
		<item>
		<title>DbCharmer 1.7.0 Release: Rails 3.0 Support and Forced Slave Reads</title>
		<link>http://feedproxy.google.com/~r/Homo-Adminus-Ru/~3/3Legwu10f_g/</link>
		<comments>http://kovyrin.net/2011/09/01/dbcharmer-1-7-0/#comments</comments>
		<pubDate>Thu, 01 Sep 2011 00:46:34 +0000</pubDate>
		<dc:creator>Oleksiy Kovyrin</dc:creator>
				<category><![CDATA[Databases]]></category>
		<category><![CDATA[Development]]></category>
		<category><![CDATA[My Projects]]></category>
		<category><![CDATA[DbCharmer]]></category>
		<category><![CDATA[rails]]></category>
		<category><![CDATA[Ruby]]></category>
		<category><![CDATA[Ruby On Rails]]></category>
		<category><![CDATA[sharding]]></category>

		<guid isPermaLink="false">http://kovyrin.net/?p=526</guid>
		<description><![CDATA[This week, after 3 months in the works, we&#8217;ve finally released version 1.7.0 of DbCharmer ruby gem &#8211; Rails plugin that significantly extends ActiveRecord’s ability to work with multiple databases and/or database servers by adding features like multiple databases support, master/slave topologies support, sharding, etc. New features in this release: Rails 3.0 support. We&#8217;ve worked [...]]]></description>
			<content:encoded><![CDATA[<p>This week, after 3 months in the works, we&#8217;ve finally released version 1.7.0 of <a href="https://rubygems.org/gems/db-charmer">DbCharmer</a> ruby gem &#8211; Rails plugin that significantly extends ActiveRecord’s ability to work with multiple databases and/or database servers by adding features like multiple databases support, master/slave topologies support, sharding, etc. </p>
<p>New features in this release: </p>
<ul>
<li><strong>Rails 3.0 support.</strong> We&#8217;ve worked really hard to bring all the features we supported in Rails 2.X to the new version of Rails and now I&#8217;m proud that we&#8217;ve implemented them all and the implementation looks much cleaner and more universal (all kinds of relations in rails 3 work in exactly the same way and we do not need to implement connection switching for all kinds of weird corner-cases in ActiveRecord).</li>
<li><strong>Forced Slave Reads functionality</strong>. Now we could have models with slaves that are not used by default, but could be turned on globally (per-controller, per-action or in a block). This is a new feature that brings our master/slave routing capabilities to a really new level &#8211; we could now use it for  a really mission-critical models on demand and not be afraid of breaking major functionality of our applications by switching them to slave reads. </li>
<li>Lots of changes were made in the <strong>structure of our code and tests</strong> to make sure it would be much easier for new developers to understand DbCharmer internals and make changes in its code. </li>
</ul>
<p>Along with the new release we&#8217;ve got a <a href="http://dbcharmer.net/">brand new web site</a>. You can find much better, cleaner and, most importantly, correct documentation for the library on the web site. We&#8217;ll be adding more examples, will try to add more in-depth explanation of our core functions, etc. </p>
<p>If you have any questions about the release, feel free to ask them in our new mailing list: <a href="http://groups.google.com/group/db-charmer">DbCharmer Users Group</a>. </p>
<p><strong>For more updates on our releases, you can follow <a href="http://twitter.com/#!/DbCharmer">@DbCharmer</a> on Twitter.</strong></p>
<div class="feedflare">
<a href="http://feeds.feedburner.com/~ff/Homo-Adminus-Ru?a=3Legwu10f_g:Xmz9rMRIRxg:V_sGLiPBpWU"><img src="http://feeds.feedburner.com/~ff/Homo-Adminus-Ru?i=3Legwu10f_g:Xmz9rMRIRxg:V_sGLiPBpWU" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/Homo-Adminus-Ru?a=3Legwu10f_g:Xmz9rMRIRxg:F7zBnMyn0Lo"><img src="http://feeds.feedburner.com/~ff/Homo-Adminus-Ru?i=3Legwu10f_g:Xmz9rMRIRxg:F7zBnMyn0Lo" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/Homo-Adminus-Ru?a=3Legwu10f_g:Xmz9rMRIRxg: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/2011/09/01/dbcharmer-1-7-0/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		<feedburner:origLink>http://kovyrin.net/2011/09/01/dbcharmer-1-7-0/</feedburner:origLink></item>
		<item>
		<title>Softlayer Cloud: a Scary Story of One Bad Service</title>
		<link>http://feedproxy.google.com/~r/Homo-Adminus-Ru/~3/6gqGy8h9zgY/</link>
		<comments>http://kovyrin.net/2011/05/02/cloudlayer-bad-story/#comments</comments>
		<pubDate>Mon, 02 May 2011 23:20:52 +0000</pubDate>
		<dc:creator>Oleksiy Kovyrin</dc:creator>
				<category><![CDATA[Admin-tips]]></category>
		<category><![CDATA[General]]></category>
		<category><![CDATA[Networks]]></category>
		<category><![CDATA[cloud]]></category>
		<category><![CDATA[downtime]]></category>
		<category><![CDATA[experience]]></category>
		<category><![CDATA[issues]]></category>
		<category><![CDATA[operations]]></category>
		<category><![CDATA[softlayer]]></category>

		<guid isPermaLink="false">http://kovyrin.net/?p=514</guid>
		<description><![CDATA[Disclaimer: the information in this post is the author&#8217;s personal opinion and is not the opinion or policy of his employer. It was spring 2010 when we decided that even though Softlayer&#8216;s server provisioning system is really great and it takes only a few hours to get a new server when we need it, it [...]]]></description>
			<content:encoded><![CDATA[<p><strong><em>Disclaimer</em>: the information in this post is the author&#8217;s personal opinion and is not the opinion or policy of his employer.</strong></p>
<hr/>
<p>It was spring 2010 when we decided that even though <a href="http://www.softlayer.com/">Softlayer</a>&#8216;s server provisioning system is really great and it takes only a few hours to get a new server when we need it, it is still too long sometimes. We wanted to be able to scale up when needed and do it faster. It was especially critical because we were working hard on bringing up Facebook integration to our site and that project could have dramatically changed our application servers cloud capacity requirements.</p>
<p>What buzzword comes to your mind when we talk about scaling up really fast, sometimes within minutes, not hours or days? Exactly &#8211; cloud computing! So, after some initial testing and playing around with Softlayer&#8217;s (really young back then) cloud solution called <a href="http://www.softlayer.com/cloudlayer/">CloudLayer</a> and talking to our account manager we&#8217;ve decided to switch our application from a bunch of huge and at the time pretty expensive 24-core monster servers to a cluster of 8-core cloud instances. To give you some perspective: we had ~250 cores at the start of the project and at the end of 2010 we&#8217;d have more then 100 instances &#8211; we weren&#8217;t a small client with a few instances).</p>
<p>For those who are not familiar with Softlayer cloud: they sell you &#8220;dedicated&#8221; cores and memory, which is supposed to give you an awesome performance characteristics comparing to shared clouds like <a href="http://aws.amazon.com/ec2/">EC2</a>.</p>
<p>Long story short, after a month of work on the project we had our application running on the cloud and were able to scale it up and down pretty fast if needed. And since the cloud was based on faster cpu and faster memory machines, we even saw improved performance of single-threaded requests processing (avg. response time dropped by ~30% as far as I remember). We were one happy operations team&#8230;</p>
<p><span id="more-514"></span></p>
<p>But then the problems started. Pretty painful and weird problems. The problems all looked exactly the same &#8211; some day (or night) some of our cloud instances would start blocking on I/O operations forever or for really long periods of time. Then some time after that our filesystems would switch to read-only mode because of write errors (timeouts) on their storage.</p>
<p>Just to make it clear: our application does not log much to local disks and almost does not read anything. But sometimes we still need to read a config file or write some logging information, and syslogd was still there and sometimes needed to write to disk. So, many times a week we would wake up, see an instance dead (all critical processes locked in D-state), call Softlayer, and spend hours (literally) to bring it back up. Sometimes it&#8217;d happen to one server, sometimes to ten of them. First time in my life I was afraid to go to bed because I was almost certain a monitoring SMS would wake me up within a few hours. This was probably one of the most stressful periods of my professional life to date.</p>
<p>What was making the situation even worse, nobody on Softlayer&#8217;s side seemed to care about the trend we all were seeing pretty clearly &#8211; their cloud was seriously broken. Every ticket about a dead instance was processed as if it was the first one with such problem ever. Support people would poke around an instance for some time, then try to restart it, etc. After some time they&#8217;d give up and escalate it to so called InfoSys team. Which would take its time  (usually hours) to finally respond with a template answer telling us there was a problem with their SAN and we were the only customers experiencing the problem and that they are working with their SAN vendor to fix this problem. We&#8217;ve got dozens of tickets and they all looked exactly the same (compared word by word) even though they were sent weeks apart.</p>
<p>After a month (I believe) of this hell in our operations team&#8217;s life and being close to a nervous breakdown I&#8217;ve contacted our dedicated account manager and told him we were going to start leaving Softlayer for some other provider (not only the cloud but all servers) if they would not fix the problem within one week. And this is where things started moving really fast: I&#8217;ve received dozens of calls from all kinds of managers, was called-in to a few meetings with a senior manager responsible for the cloud, etc and within a week they&#8217;ve found a solution.  They&#8217;d setup local disks on their hosts systems and move our filesystems to local disks instead of putting them on their SAN. This was supposed to &#8220;fix&#8221; the problem until they figured it out completely and, as expected, it did help us. The problems went away and if they were  still happening again &#8211; that&#8217;d be some instance they forgot to switch to local storage.</p>
<p>Time was passing by and for a few months we did not have any serious issues with the cloud. Then we&#8217;ve started growing. Being pretty sure the problem had been solved by that time we didn&#8217;t actually realize that new cloud instances were created on SAN again. And really soon we&#8217;ve got bad news &#8211; their problem was still there. Any issues happening with our instances were processed within the same template: </p>
<ul>
<li>an instance breaks;</li>
<li>we receive a notification and file a ticket;</li>
<li>Softlayer support guy spends up to an hour trying to figure out what&#8217;s happening;</li>
<li>the ticket is escalated to the InfoSys team;</li>
<li>we wait up to 1 day (yes, two cases out of dozens we had with them it took more than a day!) for an answer from this systems team and the problem resolution.</li>
</ul>
<p>This InfoSys team from my point of view was a really interesting case of a poorly integrated third party service within a huge company (this is my vision of the problem, I do not know how this happened in reality). Every time we&#8217;d have a problem that needed their attention, support guy would escalate the problem to the systems team and this is it &#8211; nothing he could do and the only thing we&#8217;d hear hours after the escalation was &#8220;we can&#8217;t contact systems team to get an update, we should wait&#8221;. Nobody from this team ever contacted us directly, it was impossible to reach out to any of them, they never seemed to care about those downtimes we had &#8211; they&#8217;d follow the same &#8220;fix one instance at a time&#8221; patterns again and again and whole Softlayer support department would not be able to get a few words of an update from them.</p>
<p>At this point we&#8217;ve started thinking about moving away from the cloud, but it&#8217;d involve a month or two of work and having a small operations team with lots of concurrent tasks limited your ability to allocate so many resources to such a project so we didn&#8217;t do that back then.</p>
<p>After a few more months of work with their cloud and having intermittent downtimes (rare, but still painful) at the beginning of 2011 two things happened:</p>
<p>First, we&#8217;ve got Softlayer guys to admit that their &#8220;dedicated&#8221; cores are actually Intel Hyper-Threading threads (1/2 of a cpu core). This made us realize that the cloud wasn&#8217;t actually cheap comparing to the new hardware available on the market.</p>
<p>Second, our account manager and a few guys from the systems team in clear text asked us to leave their cloud because they could not support our I/O requirements anymore (remember I told you before &#8211; we barely used their storage).</p>
<p>This was enough and we&#8217;ve finally made the decision to give up and go back to real hardware only we control and manage. It took us more than a month of work, but we think we&#8217;ve got pretty good system built as the replacement for Softlayer cloud based solution.</p>
<hr/>
<p>Now, the reason I&#8217;ve decided to finally write this blog post is funny: I was going to write it a few times and didn&#8217;t have a chance to do it. But today (April 30, 2011) one of 5 cloud instances we have left in Softlayer cloud has died. We use those instances for all kinds of experimental &#8220;servers&#8221;, etc and it wasn&#8217;t uber-critical downtime. So, this instance died and so far (4.5 hours after its death) the problems looks really similar to what we&#8217;ve experienced many-many times in the last year and again &#8211; they follow the same template: support guys have spent an hour messing with the instance, then they&#8217;ve transferred the ticket to their InfoSys team and 3 hours later there weren&#8217;t any updates and they would not be able to reach that team to get anything from them. <em>Update: It took them 4.5 hours to recover the instance and the only explanation we&#8217;ve got was &#8220;there were some problems with the host system&#8221;.</em></p>
<hr/>
<p>I really hope every single operations team in the world considering using Cloudlayer as their solution for some problems would google first, see my experience and would make their own conclusions. These are the facts and my own experience and it is up to you guys to decide how to interpret them and apply them to your own cases.</p>
<div class="feedflare">
<a href="http://feeds.feedburner.com/~ff/Homo-Adminus-Ru?a=6gqGy8h9zgY:wB5BV4Srz9k:V_sGLiPBpWU"><img src="http://feeds.feedburner.com/~ff/Homo-Adminus-Ru?i=6gqGy8h9zgY:wB5BV4Srz9k:V_sGLiPBpWU" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/Homo-Adminus-Ru?a=6gqGy8h9zgY:wB5BV4Srz9k:F7zBnMyn0Lo"><img src="http://feeds.feedburner.com/~ff/Homo-Adminus-Ru?i=6gqGy8h9zgY:wB5BV4Srz9k:F7zBnMyn0Lo" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/Homo-Adminus-Ru?a=6gqGy8h9zgY:wB5BV4Srz9k: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/2011/05/02/cloudlayer-bad-story/feed/</wfw:commentRss>
		<slash:comments>25</slash:comments>
		<feedburner:origLink>http://kovyrin.net/2011/05/02/cloudlayer-bad-story/</feedburner:origLink></item>
		<item>
		<title>Scribd is Hiring (I’m Looking for an Operations Engineer to Join My Team)</title>
		<link>http://feedproxy.google.com/~r/Homo-Adminus-Ru/~3/PqbAzRHL-mg/</link>
		<comments>http://kovyrin.net/2010/08/17/scribd-is-hiring/#comments</comments>
		<pubDate>Tue, 17 Aug 2010 15:51:06 +0000</pubDate>
		<dc:creator>Oleksiy Kovyrin</dc:creator>
				<category><![CDATA[Blog]]></category>
		<category><![CDATA[Databases]]></category>
		<category><![CDATA[Development]]></category>
		<category><![CDATA[Links]]></category>
		<category><![CDATA[Networks]]></category>
		<category><![CDATA[admin]]></category>
		<category><![CDATA[operations]]></category>
		<category><![CDATA[scribd]]></category>
		<category><![CDATA[work]]></category>

		<guid isPermaLink="false">http://kovyrin.net/?p=502</guid>
		<description><![CDATA[Scribd is a top 100 site on the web and one of the largest sites built using Ruby on Rails. As one of the first rails sites to reach scale, we&#8217;ve built a lot of infrastructure and solved a lot of challenges to get Scribd to where it is today. We actively try to push [...]]]></description>
			<content:encoded><![CDATA[<p><a href="http://www.scribd.com/">Scribd</a> is a <a href="http://rails100.pbworks.com/Alexa+Rankings">top 100 site</a> on the web and one of the largest sites built using Ruby on Rails. As one of the first rails sites to reach scale, we&#8217;ve built a lot of infrastructure and solved a lot of challenges to get Scribd to where it is today. We actively try to push the envelope and have contributed substantial work back to the open source community.</p>
<p>Scribd has an agile, startup culture and an unusually close working relationship between engineering and ops. You&#8217;ll regularly find cross-over work at Scribd, with ops people writing application-layer code and engineers figuring out operations-level problems. We think we&#8217;re able to make that work because of the uniquely talented people we have on the team.</p>
<p>To allow us to keep scaling, we&#8217;re now looking to add a strong, experienced operations guru to the team. As a member of Scribd operations, you&#8217;ll have tremendous ownership and responsibility for one of the web&#8217;s most popular applications. Because Scribd is a startup, you will wear many hats and have broader responsibility than you would at a larger company.</p>
<p>If you read this blog, you should already have a good sense of the kind of work you&#8217;ll be doing on this position. </p>
<p><strong>The Ideal Profile</strong></p>
<p>You are an experienced operations professional and have run ops at at least one large-scale website. You have comprehensive knowledge of a broad variety of system tools, from MySQL and Nginx to Squid and Memcached. You should also have strong software development skills and be well-versed in major programming languages. You should be strongly motivated, a creative solution finder, and ready to jump into the thorniest technical problems whenever necessary.</p>
<p><strong> Responsibilities</strong></p>
<ul>
<li>Develop and maintain all aspects of Scribd&#8217;s operations infrastructure, including system monitoring, backups, server configuration, databases, and caching systems</li>
<li>Collaborate with engineering to create next generation infrastructure to support changing requirements</li>
<li>Predict scaling problems before they occur and work with engineering to prevent them</li>
<li>Write and debug application level ruby code</li>
<li>Participate in an on-call rotation</li>
<li>Quickly diagnose server problems and employ preventive measures to maintain high availability servers</li>
</ul>
<p><strong>Qualifications</strong></p>
<ul>
<li>Bachelors degree in CS or equivalent experience</li>
<li>3-5 years of professional experience in site operations</li>
<li>Strong software engineering skills, including knowledge of major programming languages</li>
<li>Strong database skills, preferably with MySQL, and overall linux knowledge</li>
<li>Experience with most of the following technologies: MySQL, Nginx, Ruby, Memcached, Squid, git, Solr, HBase, Postfix</li>
<li>Proven ability to quickly learn and implement unfamiliar technologies</li>
<li>Strong desire to work hard at a rapidly growing company</li>
</ul>
<p>Location: <strong>You are preferably located near San Francisco, CA</strong>. Relocation assistance is designed on a per-case basis. In short, we&#8217;ll be creative to get you here.</p>
<p>Contact: Please send your email cover letter and resume with the subject &#8220;Your name – Senior Site Operations Engineer – via Kovyrin.net&#8221; to jobs@scribd.com or contact me directly using <a href="http://kovyrin.net/contact/">any of my contacts</a>. All communication and correspondence is held in the strictest confidence to ensure that you can connect and learn more without exposure.</p>
<div class="feedflare">
<a href="http://feeds.feedburner.com/~ff/Homo-Adminus-Ru?a=PqbAzRHL-mg:y4-nncz8nKI:V_sGLiPBpWU"><img src="http://feeds.feedburner.com/~ff/Homo-Adminus-Ru?i=PqbAzRHL-mg:y4-nncz8nKI:V_sGLiPBpWU" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/Homo-Adminus-Ru?a=PqbAzRHL-mg:y4-nncz8nKI:F7zBnMyn0Lo"><img src="http://feeds.feedburner.com/~ff/Homo-Adminus-Ru?i=PqbAzRHL-mg:y4-nncz8nKI:F7zBnMyn0Lo" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/Homo-Adminus-Ru?a=PqbAzRHL-mg:y4-nncz8nKI: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/2010/08/17/scribd-is-hiring/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		<feedburner:origLink>http://kovyrin.net/2010/08/17/scribd-is-hiring/</feedburner:origLink></item>
		<item>
		<title>Nginx-Fu: X-Accel-Redirect From Remote Servers</title>
		<link>http://feedproxy.google.com/~r/Homo-Adminus-Ru/~3/1gvnDfslGWY/</link>
		<comments>http://kovyrin.net/2010/07/24/nginx-fu-x-accel-redirect-remote/#comments</comments>
		<pubDate>Sat, 24 Jul 2010 03:45:52 +0000</pubDate>
		<dc:creator>Oleksiy Kovyrin</dc:creator>
				<category><![CDATA[Admin-tips]]></category>
		<category><![CDATA[Development]]></category>
		<category><![CDATA[Networks]]></category>
		<category><![CDATA[amazon]]></category>
		<category><![CDATA[download]]></category>
		<category><![CDATA[Nginx]]></category>
		<category><![CDATA[nginx-fu]]></category>
		<category><![CDATA[redirect]]></category>
		<category><![CDATA[S3]]></category>
		<category><![CDATA[x-accel-redirect]]></category>

		<guid isPermaLink="false">http://kovyrin.net/?p=462</guid>
		<description><![CDATA[We use nginx and its features a lot in Scribd. Many times in the last year we needed some pretty interesting, but not supported feature &#8211; we wanted nginx X-Accel-Redirect functionality to work with remote URLs. Out of the box nginx supports this functionality for local URIs only. In this short post I want to [...]]]></description>
			<content:encoded><![CDATA[<p>We use <a href="http://nginx.org/">nginx</a> and its features a lot in <a href="http://www.scribd.com/">Scribd</a>. Many times in the last year we needed some pretty interesting, but not supported feature &#8211; we wanted nginx <a href="http://wiki.nginx.org/NginxXSendfile"><tt>X-Accel-Redirect</tt></a> functionality to work with remote URLs. Out of the box nginx supports this functionality for local URIs only. In this short post I want to explain how did we make nginx serve remote content via <nobr><tt>X-Accel-Redirect</tt></nobr>.</p>
<p><span id="more-462"></span></p>
<p>First of all, here is why you may need this feature. Let&#8217;s imagine you have a file storage on <a href="http://aws.amazon.com/s3/">Amazon S3</a> where you store tons of content. And you have an application where you have some content downloading functionality that you want to be available for logged-in/paying/premium users and/or you want to keep track of downloads your users perform on your site. If your content was on your web server, you could have used simple <a href="http://kovyrin.net/2006/11/01/nginx-x-accel-redirect-php-rails/">controlled downloads</a> functionality built-in to nginx out of the box. But the problem is that your content is remote.</p>
<p>Here is what we do to solve this problem.</p>
<p>First, we create a special location on our nginx server. This location will be used as a proxy for all our accelerated file downloads:</p>
<div class="codecolorer-container nginx 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 />15<br />16<br />17<br />18<br />19<br />20<br />21<br />22<br />23<br />24<br />25<br />26<br />27<br />28<br />29<br />30<br />31<br />32<br />33<br />34<br />35<br /></div></td><td><div class="nginx codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap"><span style="color: #adadad; font-style: italic;"># Proxy download </span><br />
<span style="color: #00007f;">location</span> ~* ^/internal_redirect/<span style="color: #66cc66;">&#40;</span>.*?<span style="color: #66cc66;">&#41;</span>/<span style="color: #66cc66;">&#40;</span>.*<span style="color: #66cc66;">&#41;</span> <span style="color: #66cc66;">&#123;</span><br />
&nbsp; <span style="color: #adadad; font-style: italic;"># Do not allow people to mess with this location directly</span><br />
&nbsp; <span style="color: #adadad; font-style: italic;"># Only internal redirects are allowed</span><br />
&nbsp; <span style="color: #0000ff;">internal</span>;<br />
<br />
&nbsp; <span style="color: #adadad; font-style: italic;"># Location-specific logging</span><br />
&nbsp; <span style="color: #0000ff;">access_log</span> logs/internal_redirect.access.log main;<br />
&nbsp; <span style="color: #0000ff;">error_log</span> logs/internal_redirect.error.log warn;<br />
<br />
&nbsp; <span style="color: #adadad; font-style: italic;"># Extract download url from the request</span><br />
&nbsp; set <span style="">$download_uri</span> <span style="">$2</span>;<br />
&nbsp; set <span style="">$download_host</span> <span style="">$1</span>;<br />
<br />
&nbsp; <span style="color: #adadad; font-style: italic;"># Compose download url</span><br />
&nbsp; set <span style="">$download_url</span> <span style="color: #00007f;">http</span>://<span style="">$download_host</span>/<span style="">$download_uri</span>;<br />
<br />
&nbsp; <span style="color: #adadad; font-style: italic;"># Set download request headers</span><br />
&nbsp; <span style="color: #0000ff;">proxy_set_header</span> <span style="">Host</span> <span style="">$download_host</span>;<br />
&nbsp; <span style="color: #0000ff;">proxy_set_header</span> Authorization <span style="color: #7f007f;">''</span>;<br />
<br />
&nbsp; <span style="color: #adadad; font-style: italic;"># The next two lines could be used if your storage </span><br />
&nbsp; <span style="color: #adadad; font-style: italic;"># backend does not support Content-Disposition </span><br />
&nbsp; <span style="color: #adadad; font-style: italic;"># headers used to specify file name browsers use </span><br />
&nbsp; <span style="color: #adadad; font-style: italic;"># when save content to the disk</span><br />
&nbsp; proxy_hide_header Content-Disposition;<br />
&nbsp; add_header Content-Disposition <span style="color: #7f007f;">'attachment; filename=&quot;$args&quot;'</span>;<br />
<br />
&nbsp; <span style="color: #adadad; font-style: italic;"># Do not touch local disks when proxying </span><br />
&nbsp; <span style="color: #adadad; font-style: italic;"># content to clients</span><br />
&nbsp; proxy_max_temp_file_size <span style="color: #ff0000;">0</span>;<br />
<br />
&nbsp; <span style="color: #adadad; font-style: italic;"># Download the file and send it to client</span><br />
&nbsp; <span style="color: #0000ff;">proxy_pass</span> <span style="">$download_url</span>;<br />
<span style="color: #66cc66;">&#125;</span></div></td></tr></tbody></table></div>
<p>After adding this location to our nginx config we could start sending responses with headers like the following:</p>
<div class="codecolorer-container nginx 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="nginx codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap"><span style="color: #adadad; font-style: italic;"># This header will ask nginx to download a file </span><br />
<span style="color: #adadad; font-style: italic;"># from http://some.site.com/secret/url.ext and send it to user</span><br />
X-Accel-Redirect: /internal_redirect/some.site.com/secret/url.ext<br />
<br />
<span style="color: #adadad; font-style: italic;"># This header will ask nginx to download a file </span><br />
<span style="color: #adadad; font-style: italic;"># from http://blah.com/secret/url and send it to user as cool.pdf</span><br />
X-Accel-Redirect: /internal_redirect/blah.com/secret/url?cool.pdf</div></td></tr></tbody></table></div>
<p>Here is an example code you could use in a Rails application to use our internal redirect location:</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 /></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;">def</span> x_accel_url<span style="color:#006600; font-weight:bold;">&#40;</span>url, file_name = <span style="color:#0000FF; font-weight:bold;">nil</span><span style="color:#006600; font-weight:bold;">&#41;</span><br />
&nbsp; uri = <span style="color:#996600;">&quot;/internal_redirect/#{url.gsub('http://', '')}&quot;</span><br />
&nbsp; uri <span style="color:#006600; font-weight:bold;">&lt;&lt;</span> <span style="color:#996600;">&quot;?#{file_name}&quot;</span> <span style="color:#9966CC; font-weight:bold;">if</span> file_name<br />
&nbsp; <span style="color:#0000FF; font-weight:bold;">return</span> uri<br />
<span style="color:#9966CC; font-weight:bold;">end</span><br />
<br />
<span style="color:#9966CC; font-weight:bold;">def</span> download<br />
&nbsp; headers<span style="color:#006600; font-weight:bold;">&#91;</span><span style="color:#996600;">'X-Accel-Redirect'</span><span style="color:#006600; font-weight:bold;">&#93;</span> = x_accel_url<span style="color:#006600; font-weight:bold;">&#40;</span>some_secret_url, pretty_name<span style="color:#006600; font-weight:bold;">&#41;</span><br />
&nbsp; render <span style="color:#ff3333; font-weight:bold;">:nothing</span> <span style="color:#006600; font-weight:bold;">=&gt;</span> <span style="color:#0000FF; font-weight:bold;">true</span><br />
<span style="color:#9966CC; font-weight:bold;">end</span></div></td></tr></tbody></table></div>
<p>As you can see, nginx is really powerful tool and when you turn your creativity on you can make it even more powerful. Stay tuned for more <a href="http://kovyrin.net/tag/nginx-fu/">Nginx-Fu</a> posts.</p>
<div class="feedflare">
<a href="http://feeds.feedburner.com/~ff/Homo-Adminus-Ru?a=1gvnDfslGWY:LUrpmTqfO4k:V_sGLiPBpWU"><img src="http://feeds.feedburner.com/~ff/Homo-Adminus-Ru?i=1gvnDfslGWY:LUrpmTqfO4k:V_sGLiPBpWU" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/Homo-Adminus-Ru?a=1gvnDfslGWY:LUrpmTqfO4k:F7zBnMyn0Lo"><img src="http://feeds.feedburner.com/~ff/Homo-Adminus-Ru?i=1gvnDfslGWY:LUrpmTqfO4k:F7zBnMyn0Lo" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/Homo-Adminus-Ru?a=1gvnDfslGWY:LUrpmTqfO4k: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/2010/07/24/nginx-fu-x-accel-redirect-remote/feed/</wfw:commentRss>
		<slash:comments>7</slash:comments>
		<feedburner:origLink>http://kovyrin.net/2010/07/24/nginx-fu-x-accel-redirect-remote/</feedburner:origLink></item>
		<item>
		<title>Cool Web Designer is Looking for Work</title>
		<link>http://feedproxy.google.com/~r/Homo-Adminus-Ru/~3/irCkqZTuNPo/</link>
		<comments>http://kovyrin.net/2010/07/18/web-designer/#comments</comments>
		<pubDate>Sun, 18 Jul 2010 17:13:01 +0000</pubDate>
		<dc:creator>Oleksiy Kovyrin</dc:creator>
				<category><![CDATA[Blog]]></category>
		<category><![CDATA[General]]></category>
		<category><![CDATA[Links]]></category>
		<category><![CDATA[design]]></category>
		<category><![CDATA[job]]></category>
		<category><![CDATA[toronto]]></category>
		<category><![CDATA[web-designer]]></category>
		<category><![CDATA[work]]></category>

		<guid isPermaLink="false">http://kovyrin.net/?p=454</guid>
		<description><![CDATA[My wife &#8211; a good web designer with 6 years of experience with web design, HTML and CSS is looking for a job. Here is some information about her: Her web site (of course she is the one who designed it) Her portfolio Her resume Her LinkedIn profile We&#8217;re physically located in Toronto, Canada, but [...]]]></description>
			<content:encoded><![CDATA[<p>My wife &#8211; a <strong>good web designer</strong> with 6 years of experience with web design, HTML and CSS is <strong>looking for a job</strong>.  Here is some information about her:</p>
<ul>
<li><a href="http://kovyrina.info/">Her web site</a> (of course she is the one who designed it)</li>
<li><a href="http://kovyrina.info/portfolio">Her portfolio</a></li>
<li><a href="http://kovyrina.info/resume">Her resume</a></li>
<li><a href="http://ca.linkedin.com/in/kovyrina">Her LinkedIn profile</a>
</ul>
<p>We&#8217;re physically located in Toronto, Canada, but she has a great experience of working remotely too.  So, if you need a web designer or a junior web designer, feel free to <a href="http://kovyrina.info/contacts/">contact Tanya</a>.</p>
<div class="feedflare">
<a href="http://feeds.feedburner.com/~ff/Homo-Adminus-Ru?a=irCkqZTuNPo:7I7uKKKuk8E:V_sGLiPBpWU"><img src="http://feeds.feedburner.com/~ff/Homo-Adminus-Ru?i=irCkqZTuNPo:7I7uKKKuk8E:V_sGLiPBpWU" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/Homo-Adminus-Ru?a=irCkqZTuNPo:7I7uKKKuk8E:F7zBnMyn0Lo"><img src="http://feeds.feedburner.com/~ff/Homo-Adminus-Ru?i=irCkqZTuNPo:7I7uKKKuk8E:F7zBnMyn0Lo" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/Homo-Adminus-Ru?a=irCkqZTuNPo:7I7uKKKuk8E: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/2010/07/18/web-designer/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		<feedburner:origLink>http://kovyrin.net/2010/07/18/web-designer/</feedburner:origLink></item>
		<item>
		<title>Advanced Squid Caching in Scribd: Cache Invalidation Techniques</title>
		<link>http://feedproxy.google.com/~r/Homo-Adminus-Ru/~3/4ywVA01ppFY/</link>
		<comments>http://kovyrin.net/2010/05/29/squid-in-scribd-cache-invalidation/#comments</comments>
		<pubDate>Sat, 29 May 2010 17:02:17 +0000</pubDate>
		<dc:creator>Oleksiy Kovyrin</dc:creator>
				<category><![CDATA[Admin-tips]]></category>
		<category><![CDATA[Development]]></category>
		<category><![CDATA[My Projects]]></category>
		<category><![CDATA[Networks]]></category>
		<category><![CDATA[caching]]></category>
		<category><![CDATA[HTCP]]></category>
		<category><![CDATA[invalidation]]></category>
		<category><![CDATA[Nginx]]></category>
		<category><![CDATA[plugin]]></category>
		<category><![CDATA[squid]]></category>

		<guid isPermaLink="false">http://kovyrin.net/?p=322</guid>
		<description><![CDATA[Having a reverse-proxy web cache as one of the major infrastructure elements brings many benefits for large web applications: it reduces your application servers load, reduces average response times on your site, etc. But there is one problem every developer experiences when works with such a cache &#8211; cached content invalidation. It is a complex [...]]]></description>
			<content:encoded><![CDATA[<p>Having a <a href="http://kovyrin.net/2008/10/25/advanced-squid-caching-for-rails-applications-preface/">reverse-proxy</a> web cache as one of the major infrastructure elements brings many benefits for large web applications: it reduces your application servers load, reduces average response times on your site, etc. But there is one problem every developer experiences when works with such a cache &#8211; <em>cached content invalidation</em>.</p>
<p>It is a complex problem that usually consists of two smaller ones: i<em>ndividual cache elements invalidation</em> (you need to keep an eye on your data changes and invalidate cached pages when related data changes) and <em>full cache purges</em> (sometimes your site layout or page templates change and you need to purge all the cached pages to make sure users will get new visual elements of layout changes). In this post I&#8217;d like to look at a few techniques we use at <a href="http://www.scribd.com/">Scribd</a> to solve cache invalidation problems.</p>
<p><span id="more-322"></span></p>
<hr />So, the <strong>first problem &#8211; ongoing cache invalidation when content changes</strong>. This is actually a pretty simple task in squid: you just use <a href="http://www.htcp.org/">HTCP protocol</a> and send CLR requests to your caching farm (we didn&#8217;t find any HTCP protocol implementations so we&#8217;ve implemented <a href="http://github.com/kovyrin/htcp-ruby">our own simple client</a> that supports just one command).</p>
<p>Since we use <a href="http://haproxy.1wt.eu/">haproxy</a> to balance our traffic in the cluster it is hard to predict where should we send a purge request. So we fan those out to all cache servers.</p>
<p>To make sure cache purging won&#8217;t slow the site down, especially considering we need to do more that just a simple cache purge (submit documents to search indexes, etc, etc), we just spool a &#8220;document changed&#8221; request to a queue and then have a set of <a href="http://github.com/kovyrin/loops">asynchronous processes</a> that do all the work in background.</p>
<p>Next, <strong>The Hard Problem &#8211; handling full cache purges w/o killing our backend servers</strong> with 5x-10x traffic (our normal hit ratio is ~90-95%).</p>
<p>We&#8217;ve spent a lot of time thinking about this problem and the first idea we came up with was to have a loop process somewhere that would iterate all documents we have cached and purge them one by one&#8230; but that does not seem to be a practical solution when you have tens of millions documents (and few page versions per document) and obviously the solution would not scale with constantly growing documents corpus.</p>
<p>So we kept brainstorming and finally got one idea that works just perfectly for us: what if we&#8217;d be able to take our traffic and define a function <em>f(t)</em> that would return a percentage of the traffic that should be purged at any moment in time. So we did it &#8211; we&#8217;ve implemented a nginx module that would version our cache by assigning every cached page a revision (<a href="http://kovyrin.net/2009/07/21/advanced-squid-caching-scribd-logged-in-users-complex-urls/">using a custom HTTP-headers + Vary-caching</a>) and would be able to slowly migrate the cache from one revision to another over a pre-defined period of time.</p>
<p>Here is an overview of the requests/responses flow in our web/cache/application cluster:</p>
<p><center><img src="http://img.skitch.com/20100605-dyqee8s35ukg4cxefu3ua5jiit.png" alt="Document Page Caching" /></center></p>
<p>X-Cache-Revision value selection algorithm is the following:</p>
<p><center><img src="http://img.skitch.com/20100605-kpahhhdc92uig9qmtih4rqa6tu.png" alt="Cache Revision Selection" /></center></p>
<p>Having this kind of logic in place allows us to do so called &#8220;slow&#8221; cache purges that could take any time from a few minutes (that still helps to reduce the load spike generated by the hottest content) up to many hours (this is what we normally use) or days (never used this option, but it is definitely possible).</p>
<p>Here is an example 100% cache purge over an 8 hour interval:</p>
<ol>
<li> Daily hit ratio graph:<br />
<a href="http://img.skitch.com/20100529-pkx64g6the9winqcnk6sigiyns.png" rel="shadowbox[post-322];player=img;"><img rel="shadowbox" src="http://img.skitch.com/20100529-pkx64g6the9winqcnk6sigiyns.jpg" width="470" alt="day" /></a>
</li>
<li> Weekly hit ratio graph:<br />
<a  href="http://img.skitch.com/20100529-nk2hyafgtbw1pc1nrkgbec8st3.png" rel="shadowbox[post-322];player=img;"><img rel="shadowbox" src="http://img.skitch.com/20100529-nk2hyafgtbw1pc1nrkgbec8st3.jpg" width="470" alt="week" /></a>
</li>
</ol>
<p>As you can see, during those slow purges our cached pages would be slowly updated without putting too much pressure on the backend. Cache hit ratio would slowly degrade and then slowly get back to its normal levels, but with our normal (6-8 hours) purges hit ratio never gets lower that 65-70% which makes it possible for us to save huge amounts of money on not having 90% spare capacity just for the cache purge load surges (we used to have lots of spare application cluster capacity before introducing this approach).</p>
<p><strong>Update (June 5th, 2010):</strong> Request/response flow and cache revision selection algorithm added.</p>
<div class="feedflare">
<a href="http://feeds.feedburner.com/~ff/Homo-Adminus-Ru?a=4ywVA01ppFY:b2ode2vaNL0:V_sGLiPBpWU"><img src="http://feeds.feedburner.com/~ff/Homo-Adminus-Ru?i=4ywVA01ppFY:b2ode2vaNL0:V_sGLiPBpWU" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/Homo-Adminus-Ru?a=4ywVA01ppFY:b2ode2vaNL0:F7zBnMyn0Lo"><img src="http://feeds.feedburner.com/~ff/Homo-Adminus-Ru?i=4ywVA01ppFY:b2ode2vaNL0:F7zBnMyn0Lo" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/Homo-Adminus-Ru?a=4ywVA01ppFY:b2ode2vaNL0: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/2010/05/29/squid-in-scribd-cache-invalidation/feed/</wfw:commentRss>
		<slash:comments>3</slash:comments>
		<feedburner:origLink>http://kovyrin.net/2010/05/29/squid-in-scribd-cache-invalidation/</feedburner:origLink></item>
	</channel>
</rss>
