<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>zomo tech &#187; web</title>
	<atom:link href="http://www.zomo.co.uk/category/web/feed/" rel="self" type="application/rss+xml" />
	<link>http://www.zomo.co.uk</link>
	<description>Is it done yet?</description>
	<lastBuildDate>Wed, 14 Sep 2011 07:56:36 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.2.1</generator>
		<item>
		<title>Competing webserver workloads</title>
		<link>http://www.zomo.co.uk/2011/02/competing-webserver-workloads/</link>
		<comments>http://www.zomo.co.uk/2011/02/competing-webserver-workloads/#comments</comments>
		<pubDate>Thu, 17 Feb 2011 21:57:30 +0000</pubDate>
		<dc:creator>lemon</dc:creator>
				<category><![CDATA[sw]]></category>
		<category><![CDATA[web]]></category>

		<guid isPermaLink="false">http://www.zomo.co.uk/?p=363</guid>
		<description><![CDATA[Recently a client was receiving complaints that their busy server hosting both their WordPress sites and their OpenX1 banner delivery was underperforming. Specifically, sites including their banners were seeing page loads hang on them. If you&#8217;re in the business of selling banners this is bad news. There were reports of the WordPress sites being slow [...]]]></description>
			<content:encoded><![CDATA[<style type="text/css"><!--
th,td { font-size: 10px; align: center;}
li { margin-top: 0.25em; margin-right: 2em;}
.hr {margin-top: 0.25em; border-color: black; border-bottom-style: solid;}
.titre	{background: #20D0D0;color: #000000; font-weight: bold;}
.total	{background: #20D0D0;color: #ffff80;}
.frontend	{background: #e8e8d0;}
.backend	{background: #e8e8d0;}
.active0	{background: #ff9090;}
.active1	{background: #ffd020;}
.active2	{background: #ffffa0;}
.active3	{background: #c0ffc0;}
.active4	{background: #ffffa0;}
.active5	{background: #a0e0a0;}
.active6	{background: #e0e0e0;}
.backup0	{background: #ff9090;}
.backup1	{background: #ff80ff;}
.backup2	{background: #c060ff;}
.backup3	{background: #b0d0ff;}
.backup4	{background: #c060ff;}
.backup5	{background: #90b0e0;}
.backup6	{background: #e0e0e0;}
.rls      {letter-spacing: 0.2em; margin-right: 1px;}
table.tbl { border-collapse: collapse; border-style: none;}
table.tbl td { border-width: 1px 1px 1px 1px; border-style: solid solid solid solid; padding: 2px 3px; border-color: gray;}
table.tbl th { border-width: 1px; border-style: solid solid solid solid; border-color: gray;}
table.tbl th.pxname {background: #b00040; color: #ffff40; font-weight: bold; border-style: solid solid none solid; padding: 2px 3px; white-space: nowrap;}
table.tbl th.empty { border-style: none; empty-cells: hide; background: white;}
table.tbl th.desc { background: white; border-style: solid solid none solid; text-align: left; padding: 2px 3px;}
table.lgd { border-collapse: collapse; border-width: 1px; border-style: none none none solid; border-color: black;}
table.lgd td { border-width: 1px; border-style: solid solid solid solid; border-color: gray; padding: 2px;}
table.lgd td.noborder { border-style: none; padding: 2px; white-space: nowrap;}
-->
</style>
<p>Recently a client was receiving complaints that their busy server hosting both their <a href="http://wordpress.org/">WordPress</a> sites and their <a href="http://www.openx.org/">OpenX</a><sup><a href="http://www.zomo.co.uk/2011/02/competing-webserver-workloads/#footnote_0_363" id="identifier_0_363" class="footnote-link footnote-identifier-link" title=" advertising is a necessary evil, right? ">1</a></sup>  banner delivery was underperforming. Specifically, sites including their banners were seeing page loads hang on them. If you&#8217;re in the business of selling banners this is bad news. There were reports of the WordPress sites being slow too, but mostly from administrators<sup><a href="http://www.zomo.co.uk/2011/02/competing-webserver-workloads/#footnote_1_363" id="identifier_1_363" class="footnote-link footnote-identifier-link" title=" and that turned out to be a pagination issue ">2</a></sup> rather than site visitors.</p>
<p>I sorted out a bunch of <a href="http://www.zomo.co.uk/2011/01/timeouts-and-failing-fast/">request amplification issues</a> but still things still weren&#8217;t right, so I added a second server to help out. Instead of just chucking the combined traffic at both servers I used <a href="http://haproxy.1wt.eu/">HAProxy</a> to separate out the traffic to each, with a view to adding more OpenX servers as necessary.</p>
<p>Here&#8217;s what HAProxy&#8217;s stats had to say after some time running the sites split:</p>
<table class="tbl" width="100%">
<tbody>
<tr class="titre" align="center">
<th class="pxname" width="10%">wordpress</th>
</tr>
<tr class="titre" align="center">
<th rowspan="2"></th>
<th colspan="3">Queue</th>
<th colspan="3">Session rate</th>
<th colspan="5">Sessions</th>
<th colspan="2">Bytes</th>
</tr>
<tr class="titre" align="center">
<th>Cur</th>
<th>Max</th>
<th>Limit</th>
<th>Cur</th>
<th>Max</th>
<th>Limit</th>
<th>Cur</th>
<th>Max</th>
<th>Limit</th>
<th>Total</th>
<th>LbTot</th>
<th>In</th>
<th>Out</th>
</tr>
<tr class="active3" align="center">
<td>app01</td>
<td align="right">0</td>
<td align="right">0</td>
<td align="right">-</td>
<td align="right">1</td>
<td align="right">367</td>
<td align="right"></td>
<td align="right">2</td>
<td align="right">454</td>
<td align="right">-</td>
<td align="right"><span class="rls">1</span>75<span class="rls">8</span>026</td>
<td align="right"><span class="rls">1</span>75<span class="rls">8</span>026</td>
<td align="right"><span class="rls">1</span>33<span class="rls">6</span>54<span class="rls">1</span>933</td>
<td align="right">3<span class="rls">0</span>06<span class="rls">2</span>77<span class="rls">7</span>594</td>
</tr>
</tbody>
</table>
<table class="tbl" width="100%">
<tbody>
<tr class="titre" align="center">
<th class="pxname" width="10%">openx</th>
</tr>
<tr class="titre" align="center">
<th rowspan="2"></th>
<th colspan="3">Queue</th>
<th colspan="3">Session rate</th>
<th colspan="5">Sessions</th>
<th colspan="2">Bytes</th>
</tr>
<tr class="titre" align="center">
<th>Cur</th>
<th>Max</th>
<th>Limit</th>
<th>Cur</th>
<th>Max</th>
<th>Limit</th>
<th>Cur</th>
<th>Max</th>
<th>Limit</th>
<th>Total</th>
<th>LbTot</th>
<th>In</th>
<th>Out</th>
</tr>
<tr class="active3" align="center">
<td>app02</td>
<td align="right">0</td>
<td align="right">0</td>
<td align="right">-</td>
<td align="right">19</td>
<td align="right">45</td>
<td align="right"></td>
<td align="right">3</td>
<td align="right">75</td>
<td align="right">-</td>
<td align="right"><span class="rls">5</span>58<span class="rls">8</span>327</td>
<td align="right"><span class="rls">5</span>58<span class="rls">8</span>293</td>
<td align="right"><span class="rls">4</span>21<span class="rls">6</span>68<span class="rls">7</span>748</td>
<td align="right">1<span class="rls">1</span>87<span class="rls">8</span>95<span class="rls">1</span>168</td>
</tr>
</tbody>
</table>
<p>Some of these I found unsurprising &#8211; WordPress serves a higher volume of data, it is content heavy compared to banner delivery and related click handling. Conversely the inbound data volume for OpenX is up because it&#8217;s loaded with click information.</p>
<p>What&#8217;s interesting is that the WordPress sites have a higher maximum concurrent session count, yet the total sessions is far higher for the OpenX banners. This illustrates the benefit of separating out different server loads: one server is churning away pushing out fat content and even when heavily cached this burns enough resource that requests get queued and gum up, whilst another is fielding quick-in quick-out requests. When it&#8217;s not contending with its laggard sibling it can get on with its business unhindered.</p>
<p>Ultimately the visibility HAProxy affords beats an Apache <a href="http://httpd.apache.org/docs/2.2/mod/mod_status.html">scoreboard</a> when that Apache is fielding two differently focused workloads.</p>
<ol class="footnotes"><li id="footnote_0_363" class="footnote"> advertising is a necessary evil, right? </li><li id="footnote_1_363" class="footnote"> and that turned out to be a <a href="http://twitter.com/zomoco/status/32487132966686720">pagination</a> issue </li></ol>]]></content:encoded>
			<wfw:commentRss>http://www.zomo.co.uk/2011/02/competing-webserver-workloads/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Timeouts and failing fast</title>
		<link>http://www.zomo.co.uk/2011/01/timeouts-and-failing-fast/</link>
		<comments>http://www.zomo.co.uk/2011/01/timeouts-and-failing-fast/#comments</comments>
		<pubDate>Sun, 23 Jan 2011 20:57:24 +0000</pubDate>
		<dc:creator>lemon</dc:creator>
				<category><![CDATA[sw]]></category>
		<category><![CDATA[web]]></category>

		<guid isPermaLink="false">http://www.zomo.co.uk/?p=301</guid>
		<description><![CDATA[&#8220;Integration points are the number-one killer of systems&#8221; &#8211; Release It!, Michael Nygard Last week I had two different web systems fail in a similar way. One was a single box running two busy WordPress sites, another was a largish multi-tier publishing cluster. Both dropped off air because they didn&#8217;t handle the failure of a [...]]]></description>
			<content:encoded><![CDATA[<blockquote><p>&#8220;Integration points are the number-one killer of systems&#8221; &#8211; <a href="http://pragprog.com/titles/mnee/release-it">Release It!</a>, Michael Nygard</p></blockquote>
<p>Last week I had two different web systems fail in a similar way. One was a single box running two busy <a href="http://wordpress.org/">WordPress</a> sites, another was a largish multi-tier publishing cluster. Both dropped off air because they didn&#8217;t handle the failure of a remote system very well. In particular, both had the webserver waiting on a HTTP request to a foreign site to complete before returning a page to the client.</p>
<p>The architecture issues with this kind of request amplification are reasonably clear, and can sometimes be avoided. Data ingest from other systems can often execute asynchronously in another process. The cluster mentioned has a set of machines dedicated to just this, distributing the data they retrieve from remote systems via database and shared filesystem ready for the webservers to use when building pages.</p>
<p>However, in the situations where it is necessary for a webserver to dial out to another system at request time then it&#8217;s worth being really paranoid about how that outbound request works and untrusting of the reply. The particular problem here was lack of timeouts.</p>
<p>Here, both systems were running <a href="http://httpd.apache.org/docs/2.2/mod/prefork.html">mpm_prefork</a> <a href="http://httpd.apache.org">Apache</a>. Both were serving a request that made a HTTP call somewhere else before returning the page to the client<sup><a href="http://www.zomo.co.uk/2011/01/timeouts-and-failing-fast/#footnote_0_301" id="identifier_0_301" class="footnote-link footnote-identifier-link" title="One of the WordPress sites was doing this in every page&amp;#8217;s footer. And not caching the result. Better still, it was contacting the other WordPress site on the same server. This almost guarantees the request won&amp;#8217;t return when the shared webserver gets busy since there may be no spare server slots to accept the second request. Ouch.">1</a></sup>. Last week the remote sites that both these systems contact had outages, leaving Apache waiting for the reply up until the request timed out.</p>
<p>However, with no timeout configured that wait is effectively infinite. Long enough for these hanging Apache processes to consume all the available slots of the webserver, resulting an interesting set of observations:</p>
<ul>
<li>New TCP connections to the server just hang.</li>
<li>Server load is low, because the load measures runnable processes (and, on Linux, uninterruptible sleeping processes), and these hanging processes are just sleeping, waiting on <code>poll(2)</code> or <code>select(2)</code>.</li>
<li>No errors are logged by Apache or the application code because they haven&#8217;t failed yet.<sup><a href="http://www.zomo.co.uk/2011/01/timeouts-and-failing-fast/#footnote_1_301" id="identifier_1_301" class="footnote-link footnote-identifier-link" title="If you don&amp;#8217;t run Apache hot, then you&amp;#8217;d see a &amp;#8220;I&amp;#8217;ve reached MaxClients&amp;#8221; message in the error log, but not much else.">2</a></sup> </li>
</ul>
<p>Here&#8217;s some <code>vmstat</code> during such a wedge:<br />
<code><br />
procs -----------memory---------- ---swap-- -----io---- -system-- ----cpu----<br />
 r  b   swpd   free   buff  cache   si   so    bi    bo   in   cs us sy id wa<br />
 0  0 140580  56528  58168 516184    0    0     0     0 1026   78  0  0 100  0  0<br />
 0  0 140580  56528  58168 516184    0    0     0     0 1032   80  0  0 100  0  0<br />
 0  0 140580  56528  58168 516184    0    0     0     0 1031   78  0  0 100  0  0<br />
</code><br />
&#8220;It was quiet. <strong>Too</strong> quiet&#8221;. Hook up <code>gdb</code> and you can see what&#8217;s going on:<br />
<code><br />
(gdb) bt<br />
#0  0x00002aafffb7d14f in poll () from /lib64/libc.so.6<br />
#1  0x00002ab0090b3930 in Curl_select () from /usr/lib64/libcurl.so.3<br />
#2  0x00002ab0090ac4bc in Curl_perform () from /usr/lib64/libcurl.so.3<br />
#3  0x00002ab00850591b in zif_curl_exec () from /etc/httpd/modules/libphp5.so<br />
#4  0x00002ab0086663b2 in ?? () from /etc/httpd/modules/libphp5.so<br />
#5  0x00002ab00865651c in execute () from /etc/httpd/modules/libphp5.so<br />
#6  0x00002ab00865dc09 in ?? () from /etc/httpd/modules/libphp5.so<br />
#7  0x00002ab00865651c in execute () from /etc/httpd/modules/libphp5.so<br />
#8  0x00002ab008688750 in ?? () from /etc/httpd/modules/libphp5.so<br />
#9  0x00002ab00865651c in execute () from /etc/httpd/modules/libphp5.so<br />
#10 0x00002ab00865dc09 in ?? () from /etc/httpd/modules/libphp5.so<br />
#11 0x00002ab00865651c in execute () from /etc/httpd/modules/libphp5.so<br />
#12 0x00002ab00865c11e in ?? () from /etc/httpd/modules/libphp5.so<br />
#13 0x00002ab00865651c in execute () from /etc/httpd/modules/libphp5.so<br />
#14 0x00002ab0086395de in zend_execute_scripts () from /etc/httpd/modules/libphp5.so<br />
#15 0x00002ab0085fe697 in php_execute_script () from /etc/httpd/modules/libphp5.so<br />
#16 0x00002ab0086b6ad6 in ?? () from /etc/httpd/modules/libphp5.so<br />
#17 0x00002aaffdbb7a4a in ap_run_handler ()<br />
#18 0x00002aaffdbbaed8 in ap_invoke_handler ()<br />
#19 0x00002aaffdbc578a in ap_internal_redirect ()<br />
#20 0x00002ab0069ffbc0 in ap_make_dirstr_parent () from /etc/httpd/modules/mod_rewrite.so<br />
#21 0x00002aaffdbb7a4a in ap_run_handler ()<br />
#22 0x00002aaffdbbaed8 in ap_invoke_handler ()<br />
#23 0x00002aaffdbc5938 in ap_process_request ()<br />
#24 0x00002aaffdbc2b70 in ?? ()<br />
#25 0x00002aaffdbbecd2 in ap_run_process_connection ()<br />
#26 0x00002aaffdbc9789 in ?? ()<br />
#27 0x00002aaffdbc9a1a in ?? ()<br />
#28 0x00002aaffdbc9ad0 in ?? ()<br />
#29 0x00002aaffdbca7bb in ap_mpm_run ()<br />
#30 0x00002aaffdba4e48 in main ()<br />
</code></p>
<p>Another kink is that a <a href="http://httpd.apache.org/docs/2.2/stopping.html#graceful">graceful</a> restart of Apache, via <code>SIGUSR1</code>, doesn&#8217;t work since a graceful restart waits for a request to finish &#8211; and these ones aren&#8217;t finishing. <code>apachectl</code> or <code>service(8)</code> scripts will exit but the hung processes remain. These long running wedged processes are also visible in <code>ps</code>:<br />
<code><br />
apache     580  0.0  0.3  30332  5492 ?        S    Jan02   0:00<br />
/usr/sbin/httpd<br />
</code><br />
and can be matched up with the hung connections via <code>netstat -anp</code><br />
<code><br />
tcp        0      0 172.18.74.113:50129         192.0.32.10:80<br />
    ESTABLISHED 580/httpd<br />
</code></p>
<p>This is a mostly a long winded way of pointing out the importance of timeouts on code that executes during a request, particularly when doing something non-local. Fail fast!</p>
<p>To keep the WordPress sites running I ran through the code adding <code>CURLOPT_CONNECTTIMEOUT</code>, <code>CURLOPT_TIMEOUT</code> settings to all the cURL calls I could find, and the development team took care of the code running on the cluster. Both have been stable since.</p>
<p>There are plenty of other pitfalls in these integration points between systems. Michael Nygard&#8217;s book, linked at top, makes a good survey of them alongside other stability antipatterns in complex systems. It&#8217;s a recommended read.</p>
<ol class="footnotes"><li id="footnote_0_301" class="footnote">One of the WordPress sites was doing this in every page&#8217;s footer. And not caching the result. Better still, it was contacting the other WordPress site on the same server. This almost guarantees the request won&#8217;t return when the shared webserver gets busy since there may be no spare server slots to accept the second request. Ouch.</li><li id="footnote_1_301" class="footnote">If you don&#8217;t run Apache hot, then you&#8217;d see a &#8220;I&#8217;ve reached <code>MaxClients</code>&#8221; message in the error log, but not much else.</li></ol>]]></content:encoded>
			<wfw:commentRss>http://www.zomo.co.uk/2011/01/timeouts-and-failing-fast/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>SOAP in unexpected &#8220;actually, quite easy&#8221; incident.</title>
		<link>http://www.zomo.co.uk/2009/09/soap-in-unexpected-actually-quite-easy-incident/</link>
		<comments>http://www.zomo.co.uk/2009/09/soap-in-unexpected-actually-quite-easy-incident/#comments</comments>
		<pubDate>Wed, 30 Sep 2009 21:02:18 +0000</pubDate>
		<dc:creator>lemon</dc:creator>
				<category><![CDATA[sw]]></category>
		<category><![CDATA[web]]></category>

		<guid isPermaLink="false">http://www.zomo.co.uk/?p=203</guid>
		<description><![CDATA[In web service access shuffle, today&#8217;s mission is introducing large N number of new backend pools, traffic rules and virtual servers to Zeus ZXTM balancers. No time for monkeying around in the web UI, better check out their well documented API. It uses SOAP, which I&#8217;ve never got busy with before &#8211; slightly apprehensive. The [...]]]></description>
			<content:encoded><![CDATA[<p>In web service access shuffle, today&#8217;s mission is introducing large N number of new backend pools, traffic rules and virtual servers to Zeus <a href="http://www.zeus.com/products/zxtm/">ZXTM</a> balancers. No time for monkeying around in the web UI, better check out their <a href="http://knowledgehub.zeus.com/media/5.1/ZXTM_5.1_Control_API.pdf">well documented</a> API. It uses <a href="http://en.wikipedia.org/wiki/SOAP">SOAP</a>, which I&#8217;ve never got busy with before &#8211; slightly apprehensive.</p>
<p>The reference documentation has examples in Perl and PHP which got me so far, but I&#8217;m most comfortable in Ruby now, and was happy to find <a href="http://knowledgehub.zeus.com/code/2009/04/09/ruby_and_soap_example_2"><sup><a href="http://www.zomo.co.uk/2009/09/soap-in-unexpected-actually-quite-easy-incident/#footnote_0_203" id="identifier_0_203" class="footnote-link footnote-identifier-link" title="I was going to paste example code, but this&amp;#8217;ll do">1</a></sup> this</a> pointer to using the <a href="http://dev.ctor.org/soap4r">soap4r</a> library.</p>
<p>Chief bonus here is the <code>wsdl2ruby.rb</code> tool that&#8217;ll transform the <a href="http://www.w3.org/TR/wsdl">WSDL</a> data into Ruby objects with heirarchy, attribute accessors and everything else to make operating the API really comfortable. If your WSDL is a moving target during development it&#8217;ll even do this on the fly.</p>
<p>This meant getting the scripting done to configure the ZXTMs was pretty straightforward, without any faffing with the underlying access mech. Refreshing!</p>
<ol class="footnotes"><li id="footnote_0_203" class="footnote">I was going to paste example code, but this&#8217;ll do</li></ol>]]></content:encoded>
			<wfw:commentRss>http://www.zomo.co.uk/2009/09/soap-in-unexpected-actually-quite-easy-incident/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>OpenSSH&#8217;s SOCKS forwarding</title>
		<link>http://www.zomo.co.uk/2009/01/opensshs-socks-forwarding/</link>
		<comments>http://www.zomo.co.uk/2009/01/opensshs-socks-forwarding/#comments</comments>
		<pubDate>Sun, 18 Jan 2009 16:32:21 +0000</pubDate>
		<dc:creator>lemon</dc:creator>
				<category><![CDATA[stash]]></category>
		<category><![CDATA[web]]></category>

		<guid isPermaLink="false">http://www.zomo.co.uk/?p=126</guid>
		<description><![CDATA[It&#8217;s always great to discover a feature in a tool you use everyday. Someone pointed out to me that, in addition to local and remote port forwarding, OpenSSH also offers what it calls dynamic application-level port forwarding. Put more simply, OpenSSH can act as a local SOCKS proxy punting the traffic out the remote end [...]]]></description>
			<content:encoded><![CDATA[<p>It&#8217;s always great to discover a feature in a tool you use everyday. Someone pointed out to me that, in addition to local and remote port forwarding, OpenSSH also offers what it calls dynamic application-level port forwarding. Put more simply, OpenSSH can act as a local SOCKS proxy punting the traffic out the remote end of the connection:</p>
<pre style="padding-left: 30px;">ssh -Dlocalhost:1080 -C host.example.com</pre>
<p>You can now aim your browser&#8217;s SOCKS settting at localhost:1080. Bonus points for using a locally-hosted PAC file to determine which traffic is routed via the proxy:</p>
<pre>function FindProxyForURL(url, host) {
  if (shExpMatch(host, "*.example.com"))
    return "SOCKS 127.0.0.1:1080"
  else
    return "DIRECT";
}</pre>
<p>For getting sight of servers behind a firewall without remote proxies (hello <a href="http://www.privoxy.org/">Privoxy</a>) and continual browser fiddling this is ideal. Thanks Murb!</p>
]]></content:encoded>
			<wfw:commentRss>http://www.zomo.co.uk/2009/01/opensshs-socks-forwarding/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Using Single Sign-On To Integrate Ning With An External Site</title>
		<link>http://www.zomo.co.uk/2008/08/using-single-sign-on-to-integrate-ning-with-an-external-site/</link>
		<comments>http://www.zomo.co.uk/2008/08/using-single-sign-on-to-integrate-ning-with-an-external-site/#comments</comments>
		<pubDate>Wed, 20 Aug 2008 18:14:10 +0000</pubDate>
		<dc:creator>lemon</dc:creator>
				<category><![CDATA[all]]></category>
		<category><![CDATA[sw]]></category>
		<category><![CDATA[web]]></category>

		<guid isPermaLink="false">http://www.zomo.co.uk/?p=77</guid>
		<description><![CDATA[Overview Ning provide off the peg hosted social networks. The service is free unless you pay to not have their context-driven ads on your pages. Within a few minutes of sign-up you&#8217;re away. Particulary cool is that they will let you at the source of your network. You can&#8217;t then wander off and run it [...]]]></description>
			<content:encoded><![CDATA[<p><strong>Overview</strong><br />
<a href="http://www.ning.com/">Ning</a> provide off the peg hosted social networks. The service is free unless you pay to not have their context-driven ads on your pages. Within a few minutes of sign-up you&#8217;re away.</p>
<p>Particulary cool is that they will let you at the source of your network. You can&#8217;t then wander off and run it elsewhere, it sits atop of their core web framework that you can&#8217;t see. With the source (which is encouragingly well written PHP) you can do perform all manner of modifications to bend the template network to your will.</p>
<p>Ning provide their own authentication system, and there&#8217;s no API to hook in someone else&#8217;s which is a hassle if you&#8217;re trying to build a social network alongside another site: maintaining a login for each site is going to annoy the users and be a nightmare to manage. Nil points.</p>
<p>One solution is to build a <a href="http://en.wikipedia.org/wiki/Single_sign-on">single sign-on</a> system around what Ning already provide, which is robust, tested and better presented than anything we could achieve! I&#8217;d sketched out such a system for a proposal years ago but never had the opportunity to build it. A current project provided the perfect excuse to try this out.</p>
<p><strong>Layout</strong><br />
<a href="http://www.zomo.co.uk/wp-content/uploads/2008/08/ning-sso.png"><img src="http://www.zomo.co.uk/wp-content/uploads/2008/08/ning-sso.png" alt="" title="ning-sso" width="300" height="197" class="aligncenter size-medium wp-image-96" /></a><br />
<strong>Operation</strong><br />
The numbers here match those in the diagram.</p>
<ol>
<li>User visits <em>www.example.com</em> (which for us happens to be a <a href="http://www.rubyonrails.org/">Rails</a>) site.</li>
<li>The PageController notices the browser supplied no cookie and must therefore log on to Ning before proceeding. The site returns a redirect to Ning&#8217;s authentication page.</li>
<li>The browser follows this redirect to Ning.</li>
<li>Ning authenticates the user. The login code is modified from the original Ning behaviour. Here, it issues a redirect to <em>sso.example.com</em> with some parameters, including the user&#8217;s Ning ID and a salted hash to prevent spoofing. In this redirect Ning sets a range of cookies, including one that identifies the user to Ning.</li>
<li>The browser follows this redirect to the SSO server (which happens to be a <a href="http://merbivore.com/">Merb</a> site &#8211; I wanted to try it out!)</li>
<li>The SSO app checks the provided hash against its own idea of what it should be. Assuming they match it considers the Ning ID to be valid. Finally, the SSO app issues another redirect along with cookies for just <em>.example.com</em>. This cookie identifies the user to the external site</li>
<li>In passing, the SSO app keeps track of users it has seen, and if this is a new user it will make an API request to Ning to fetch that user&#8217;s profile data and create a matching user on the <em>www.example.com</em> site.</li>
<li>The browser follows this last redirect back to Ning. It could be back to <em>www.example.com</em> or Ning  depending on the situation.</li>
<li>Ning knows who the user is by merit of its cookies.</li>
<li>As does our external site.</li>
</ol>
<p><strong>Notes</strong></p>
<ul>
<li>Having the SSO app different from the <em>www.example.com</em> site is perhaps a bit baroque. It works because the SSO app issues a cookie for <em>.example.com</em> which the browser will offer to both <em>sso.example.com</em> and <em>www.example.com</em>. In favour of this approach is that the SSO app is simple, and thus less likely to fail during development iterations than the nascent <em>www.example.com</em> site. A failure in the SSO app is bad, becaise it locks people out of Ning too. That Rails and Merb can share session data (and a database) is cool.</li>
<li>The SSO app&#8217;s fetching of Ning profile data allows us to maintain a local version of a user&#8217;s profile to avoid the need to fetch it from Ning every time we need. There&#8217;s a nuance of Ning API authentication that meant I had to write a custom widget (Ning site component) to handle that.</li>
</ul>
<p><strong>Specifics</strong><br />
I was about to paste bits of modified Ning code, but I&#8217;ll need to check if I can under the various blurbs you agree too when signing up! Anyhow, the information above should help answer some of the requests for details from the Ning developers forum.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.zomo.co.uk/2008/08/using-single-sign-on-to-integrate-ning-with-an-external-site/feed/</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
		<item>
		<title>High Availability Rails Cluster</title>
		<link>http://www.zomo.co.uk/2008/08/high-availability-rails-cluster/</link>
		<comments>http://www.zomo.co.uk/2008/08/high-availability-rails-cluster/#comments</comments>
		<pubDate>Tue, 19 Aug 2008 18:33:30 +0000</pubDate>
		<dc:creator>lemon</dc:creator>
				<category><![CDATA[all]]></category>
		<category><![CDATA[sw]]></category>
		<category><![CDATA[web]]></category>

		<guid isPermaLink="false">http://tech.zomo.co.uk/?p=38</guid>
		<description><![CDATA[I&#8217;ve been asked about this a few times, so I figured I&#8217;d post here. This is a brief description of a highly available Rails cluster I&#8217;ve built. Some preliminaries: There&#8217;s no invention here, I believe this setup is very common. High availability isn&#8217;t the same thing as load balanced. There is nothing here to intelligently [...]]]></description>
			<content:encoded><![CDATA[<p>I&#8217;ve been asked about this a few times, so I figured I&#8217;d post here. This is a brief description of a highly available Rails cluster I&#8217;ve built. Some preliminaries:</p>
<ul>
<li>There&#8217;s no invention here, I believe this setup is very common.</li>
<li>High availability isn&#8217;t the same thing as load balanced. There is nothing here to intelligently shared load across the frontend servers, and one backend server is essentially idle all the time.</li>
<li>This cluster is built with a bunch of open-source software on non-fancy kit. As such it doesn&#8217;t have the enormous capacity of clusters built upon commercial shared-storage products, SAN kit, layer 7 web switches etc. Its ambition is to run a few busy Rails sites well whilst coping with hardware failure gracefully.</li>
</ul>
<p><strong>Layout</strong><br />
<img src="http://tech.zomo.co.uk/wp-content/uploads/2008/08/high-availability.png" alt="" title="high-availability" width="253" height="300" class="aligncenter size-medium wp-image-37" /></p>
<p><strong>Operation</strong></p>
<ul>
<li>Web traffic is spread across the managed frontend interfaces by multiple A records in the DNS.</li>
<li><a href="http://www.backhand.org/wackamole/">Wackamole</a> uses a <a href="http://www.spread.org/">Spread messaging network</a> to ensure these multiple A record IPs are always present across the frontend. It achieves this by managing the hosts&#8217; interfaces when it detects hosts joining or leaving the cluster.</li>
<li>A pair of <a href="http://mysql.com/">MySQL</a> servers run in master:master configuration on the backend hosts</li>
<li>The backend hosts use <a href="http://www.drbd.org/">DRBD</a> to maintain a mirrored block device between them.</li>
<li>These block devices back a <a href="http://en.wikipedia.org/wiki/Network_File_System_(protocol)">NFS</a> filesystem.</li>
<li><a href="http://www.linux-ha.org/">Heartbeat</a> runs on the backend hosts to do several tasks:
<ol>
<li>Manage which host is the DRBD primary and therefore can be written to.</li>
<li>Manage which host has the DRBD filesystem mounted and exported with NFS.</li>
<li>Manage the IP through which the frontend mounts the filesystem and talks to MySQL.</li>
</ol>
</li>
<li>With all this in place, <a href="http://nginx.net/">Nginx</a> accepts web connections and serves static assets off the NFS mount and passess other requests to <a href="http://mongrel.rubyforge.org/">Mongrel</a>, a HTTP server that&#8217;s well suited to running a <a href="http://www.rubyonrails.org/">Rails</a> instance.</li>
</ul>
<p><strong>Notes</strong>
<ul>
<li>One of the main hazards of MySQL master:master setups is primary key collision if an INSERT occurs on both hosts at once. We avoid that here by letting Hearbeat manage the IP that the frontends connect to.</li>
<li>I&#8217;ve built two of these clusters to date. The second one is now four servers wide on the frontend.</li>
</ul>
<p><strong>Future work</strong>
<ul>
<li>DRBD can now run in dual-primary mode, allowing both hosts to accept writes. This makes it a candidate for filesystems like <a href="http://www.redhat.com/gfs/">GFS</a> that use shared storage to present a filesystem that can be written to on multiple hosts. More <a href="http://www.drbd.org/users-guide/ch-gfs.html">here</a>.</li>
<li>To add some load balancing I&#8217;m considering using <a href="http://haproxy.1wt.eu/">HAProxy</a> or <a href="http://www.linuxvirtualserver.org/">LVS</a> to actively distribute traffic across the frontends.</li>
<li>HA aside, there&#8217;s also some cool things like <a href="http://swiftiply.swiftcore.org/mongrel.html">evented Mongrel</a> that it would be interesting to try.</li>
</ul>
]]></content:encoded>
			<wfw:commentRss>http://www.zomo.co.uk/2008/08/high-availability-rails-cluster/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Javascript Trac Bookmark</title>
		<link>http://www.zomo.co.uk/2008/07/javascript-trac-bookmark/</link>
		<comments>http://www.zomo.co.uk/2008/07/javascript-trac-bookmark/#comments</comments>
		<pubDate>Fri, 11 Jul 2008 11:16:24 +0000</pubDate>
		<dc:creator>lemon</dc:creator>
				<category><![CDATA[all]]></category>
		<category><![CDATA[web]]></category>

		<guid isPermaLink="false">http://tech.zomo.co.uk/?p=36</guid>
		<description><![CDATA[Substitute https://trac.example.com for your Trac instance and drop into a Firefox bookmark, perhaps on the toolbar: javascript:q='%s';if(q=='%'+'s')void(q=prompt('Trac%20#',''));if(q)location.href='https://trac.example.com/trac/ticket/'+escape(q);else%20location.href='https://trac.example.com/trac/report/1' Click / select the bookmark to be prompted for a Trac issue number, which you can leave blank to just load /report/1 Extra credit for assigning a keyword (eg &#8216;ktx&#8217;) in the bookmark properties, allowing you to just [...]]]></description>
			<content:encoded><![CDATA[<p>Substitute <code>https://trac.example.com</code> for your Trac instance and drop into a Firefox bookmark, perhaps on the toolbar:<br />
<code>javascript:q='%s';if(q=='%'+'s')void(q=prompt('Trac%20#',''));if(q)location.href='https://trac.example.com/trac/ticket/'+escape(q);else%20location.href='https://trac.example.com/trac/report/1'</code><br />
Click / select the bookmark to be prompted for a Trac issue number, which you can leave blank to just load /report/1</p>
<p>Extra credit for assigning a keyword (eg &#8216;ktx&#8217;) in the bookmark properties, allowing you to just type, eg, &#8216;ktx 1234&#8242; in the Location bar to achieve the same.</p>
<p>This is a rework of a similar hack for a much older ticketer, <a href="http://transwebtools.com/pts/index.html">PTS</a>, which is amazingly still in use at one of my previous workplaces. You can gauge its age by the fact that it was ported <i>to</i> PHP<strong>3</strong> and was pretty open to most injection attacks!</p>
]]></content:encoded>
			<wfw:commentRss>http://www.zomo.co.uk/2008/07/javascript-trac-bookmark/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>rotatelogs</title>
		<link>http://www.zomo.co.uk/2005/10/rotatelogs/</link>
		<comments>http://www.zomo.co.uk/2005/10/rotatelogs/#comments</comments>
		<pubDate>Thu, 20 Oct 2005 12:51:41 +0000</pubDate>
		<dc:creator>lemon</dc:creator>
				<category><![CDATA[todo]]></category>
		<category><![CDATA[web]]></category>

		<guid isPermaLink="false">http://tech.zomo.co.uk/?p=3</guid>
		<description><![CDATA[I&#8217;m basically a fan of rotatelogs(1) for Apache logging: no need to gracefully restart, easy filenames to script with, encourages log ageing. I do however find myself forever doing stuff like $ ls -tr /a/log/dir &#124; tail error_log.1129593600 error_log.1129680000 error_log.1129766400 $ tail -f error_log.1129766400 to diagnose a problem. What would be cool was if rotatelogs [...]]]></description>
			<content:encoded><![CDATA[<p>I&#8217;m basically a fan of <code>rotatelogs(1)</code> for Apache logging: no need to gracefully restart, easy filenames to script with, encourages log ageing.</p>
<p>I do however find myself forever doing stuff like<br />
<code>$ ls -tr /a/log/dir | tail<br />
error_log.1129593600<br />
error_log.1129680000<br />
error_log.1129766400<br />
$ tail -f error_log.1129766400</code> to diagnose a problem.</p>
<p>What would be cool was if <code>rotatelogs</code> linked, say, <code>error_log.current</code> to the, er, current log. If it used a hard link, you could probably use <code>tail</code>&#8216;s -F flag to seemlessly glide over rotations.</p>
<p>Update: I looked at the source; what immediately occurs to me is that both soft and hard linking aren&#8217;t particularly portable, and Apache runs on many different platforms. It&#8217;s probably more straightforward to do this with a shell script or similar.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.zomo.co.uk/2005/10/rotatelogs/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>PHP, IE and file downloads</title>
		<link>http://www.zomo.co.uk/2005/10/php-ie-and-file-downloads/</link>
		<comments>http://www.zomo.co.uk/2005/10/php-ie-and-file-downloads/#comments</comments>
		<pubDate>Thu, 20 Oct 2005 10:29:32 +0000</pubDate>
		<dc:creator>lemon</dc:creator>
				<category><![CDATA[web]]></category>

		<guid isPermaLink="false">http://tech.zomo.co.uk/?p=2</guid>
		<description><![CDATA[PHP defaults to sending nocache headers, and this makes IE aggressively zap any temporary files that arise, even if they're destined to be passed to another app locally.]]></description>
			<content:encoded><![CDATA[<p>A developer I work with was having problems with some PHP code that generates a spreadsheet for download over https. It all worked swimmingly on Firefox, Safari etc, but not Internet Explorer which would promptly error with a &#8216;file not found&#8217; dialog box. The Content-Type header was variously &#8216;application/octet-stream&#8217; and &#8216;application/vnd.ms-excel&#8217;, but neither worked.</p>
<p>PHP defaults to sending nocache headers, and this makes IE aggressively zap any temporary files that arise, even if they&#8217;re destined for another app locally.</p>
<p>We fixed this by setting PHP&#8217;s cache limiter knob on a per-download basis, although it can be done system-wide also. <a href="http://uk2.php.net/manual/en/function.session-cache-limiter.php">Here&#8217;s the page in the PHP manual.</a></p>
<p>Whilst investigating this, I encountered the <a href="http://msdn.microsoft.com/workshop/networking/moniker/overview/appendix_a.asp">ugly truth</a> about IE&#8217;s mime-type handling. Seems fiddly!</p>
]]></content:encoded>
			<wfw:commentRss>http://www.zomo.co.uk/2005/10/php-ie-and-file-downloads/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>

