<?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:atom="http://www.w3.org/2005/Atom" xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0" version="2.0"><channel><title>Colin Howe's Blog</title><link>http://www.colinhowe.co.uk/recent/</link><description /><language>en-gb</language><lastBuildDate>Sun, 12 Aug 2012 13:40:20 +0100</lastBuildDate><atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="self" type="application/rss+xml" href="http://feeds.feedburner.com/colinhowe" /><feedburner:info uri="colinhowe" /><atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="hub" href="http://pubsubhubbub.appspot.com/" /><feedburner:emailServiceId>colinhowe</feedburner:emailServiceId><feedburner:feedburnerHostname>http://feedburner.google.com</feedburner:feedburnerHostname><item><title>Monner - Monitor CPU, memory and network whilst running a program
</title><link>http://feedproxy.google.com/~r/colinhowe/~3/aoVIONg2AA8/</link><description>&lt;p&gt;I'm looking at re-implementing one of our services in various different ways. To help me decide between the different ways I want to run a benchmark of the services and monitor how much of a systems resources they use.&lt;/p&gt;
&lt;p&gt;After a lot of digging the best solutions I found involved running various different commands, scraping together the output and then cobbling together a report. Not fun.&lt;/p&gt;
&lt;p&gt;Enter Monner. A simple python script that lets you do:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;monner some-program
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;And will output results like:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;CPU (%)    Memory used (mb)    Network in (kb)    Network out (kb)
100.0      3379.9              0.0                0.0
100.0      3380.4              0.5                0.0
100.0      3380.4              0.1                0.0
100.0      3380.4              0.0                0.0
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;This can then be loaded into a spreadsheet program and graphed to your hearts content.&lt;/p&gt;
&lt;p&gt;Code is on github: &lt;a rel="nofollow" href="http://github.com/colinhowe/monner"&gt;http://github.com/colinhowe/monner&lt;/a&gt;&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/colinhowe?a=aoVIONg2AA8:7CO0MrTzOMo:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/colinhowe?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/colinhowe?a=aoVIONg2AA8:7CO0MrTzOMo:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/colinhowe?i=aoVIONg2AA8:7CO0MrTzOMo:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/colinhowe?a=aoVIONg2AA8:7CO0MrTzOMo:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/colinhowe?i=aoVIONg2AA8:7CO0MrTzOMo:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/colinhowe/~4/aoVIONg2AA8" height="1" width="1"/&gt;</description><pubDate>Sun, 12 Aug 2012 13:40:20 +0100</pubDate><guid isPermaLink="false">http://www.colinhowe.co.uk/2012/aug/12/monner/</guid><feedburner:origLink>http://www.colinhowe.co.uk/2012/aug/12/monner/</feedburner:origLink></item><item><title>SSDs on AWS - Impact on Conversocial
</title><link>http://feedproxy.google.com/~r/colinhowe/~3/hKAECmY-EYA/</link><description>&lt;h3&gt;AWS releases SSD instances&lt;/h3&gt;

&lt;p&gt;AWS &lt;a rel="nofollow" href="http://aws.typepad.com/aws/2012/07/new-high-io-ec2-instance-type-hi14xlarge.html"&gt;recently released&lt;/a&gt; a new instance type with two 1TB SSDs attached locally.&lt;/p&gt;
&lt;p&gt;These SSDs are fast. Especially compared to EBS. EBS will push to about 700 random reads/second when RAIDed and network conditions are kind. The new SSDs will do about 120,000 random reads/second, a 170x performance boost, irrespective of network conditions.&lt;/p&gt;
&lt;h3&gt; We switched our MongoDB over&lt;/h3&gt;

&lt;p&gt;We've just switched our primary MongoDB replica set over to the new SSD instances instead of high-memory quadruple extra large Instances (68GB RAM each). We've put the two drives together in a RAID1 array for extra reliability.&lt;/p&gt;
&lt;h3&gt;Performance Impact&lt;/h3&gt;

&lt;p&gt;Our average response time across all of &lt;a rel="nofollow" href="http://www.conversocial.com/"&gt;Conversocial&lt;/a&gt; is &lt;b&gt;43% faster&lt;/b&gt; (392ms to 274ms). One particular view that grabs a lot of random data is now &lt;b&gt;74% faster&lt;/b&gt; (877ms to 504ms). We've not done much optimization beyond indexing yet so this is great.&lt;/p&gt;
&lt;p&gt;According to MMS our CPU usage on our old primary server was hovering around 90% iowait (we definitely needed to shard). It now hovers around 3%.&lt;/p&gt;
&lt;h3&gt;Long-term Gain - Less Optimizing for Random IO&lt;/h3&gt;

&lt;p&gt;We've been starting to look at performance and have been considering a lot of fun tricks to get our data clustered together and optimised for reading from platter-based disks. Now that we have SSDs we can save ourselves a lot of time and hassle by not doing these optimisations. &lt;/p&gt;
&lt;p&gt;This is a feature we've been waiting for AWS to do for a long time. It's now here and, as always, AWS have done an amazing job.&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/colinhowe?a=hKAECmY-EYA:dV0BuUDvyvA:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/colinhowe?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/colinhowe?a=hKAECmY-EYA:dV0BuUDvyvA:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/colinhowe?i=hKAECmY-EYA:dV0BuUDvyvA:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/colinhowe?a=hKAECmY-EYA:dV0BuUDvyvA:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/colinhowe?i=hKAECmY-EYA:dV0BuUDvyvA:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/colinhowe/~4/hKAECmY-EYA" height="1" width="1"/&gt;</description><pubDate>Mon, 23 Jul 2012 14:58:37 +0100</pubDate><guid isPermaLink="false">http://www.colinhowe.co.uk/2012/jul/23/ssds-on-aws--impact-on-conversocial/</guid><feedburner:origLink>http://www.colinhowe.co.uk/2012/jul/23/ssds-on-aws--impact-on-conversocial/</feedburner:origLink></item><item><title>MongoDB - Collection Per User Performance
</title><link>http://feedproxy.google.com/~r/colinhowe/~3/3cdyEoBlpkc/</link><description>&lt;h4&gt;Theory&lt;/h4&gt;

&lt;p&gt;On the MongoDB site &lt;a rel="nofollow" href="http://www.mongodb.org/display/DOCS/Using+a+Large+Number+of+Collections"&gt;there is&lt;/a&gt; a suggestion that collections can be used to cluster data and get better performance as a result.&lt;/p&gt;
&lt;p&gt;The idea is that a different collection could be used for each user's data. Internally, MongoDB will use different extents for each collection (an extent is a contiguous block of memory). By doing this we guarantee that a user's data will be stored in mostly sequential blocks on disk - making it far easier to read in data if going to disk.&lt;/p&gt;
&lt;h4&gt;Performance - Experiment&lt;/h4&gt;

&lt;p&gt;The theory is sound, so what about performance?&lt;/p&gt;
&lt;p&gt;In this experiment I used two c1.medium (high CPU) instances on AWS - one to run the test and one to be the server.&lt;/p&gt;
&lt;p&gt;The test script inserted 5,000,000 ~1kb emails into the database spread over 10,000 users (using a triangle distribution with a mean of 5,000 to simulate high/low volume users). 10,000 queries were then performed for 20 e-mails from a random slice of time for a random user. I'll add a link to the script tonight when I get access to my home laptop.&lt;/p&gt;
&lt;p&gt;There was an index on user/time on every collection.&lt;/p&gt;
&lt;p&gt;There were three variants:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;One collection for all users&lt;/li&gt;
&lt;li&gt;Fifty collections with user's spread equally amongst the collections&lt;/li&gt;
&lt;li&gt;One collection for each user&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;Performance - Results&lt;/h4&gt;

&lt;p&gt;Having a single collection for each user reduced the amount of storage needed (due to not needing to store/index the user ID):&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;308mb vs 476mb for indices&lt;/li&gt;
&lt;li&gt;4044mb vs 4101mb for data&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Query performance was as follows:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;One collection for all users - 91.0ms / query&lt;/li&gt;
&lt;li&gt;Fifty collections - 20.0ms / query&lt;/li&gt;
&lt;li&gt;One collection per user - 13.2ms / query&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Insert performance was:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;One collection for all users - 5,167 inserts / sec&lt;/li&gt;
&lt;li&gt;Fifty collections - 4,350 inserts / sec&lt;/li&gt;
&lt;li&gt;One collection per user - 1,645 inserts / sec&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;Conclusions&lt;/h4&gt;

&lt;p&gt;Having a single collection per user was ~7x faster for reads, but, ~3x slower for writes.&lt;/p&gt;
&lt;p&gt;Using fifty collections seemed to give a decent balance, ~4.5x faster for reads and only 15% slower for writes.&lt;/p&gt;
&lt;p&gt;This technique will introduce more complexity into your system and reduce the flexibility of querying. However, if performance is a concern then it is a technique worth considering - but benchmark it first as your mileage may vary :)&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/colinhowe?a=3cdyEoBlpkc:Xgf6K11M040:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/colinhowe?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/colinhowe?a=3cdyEoBlpkc:Xgf6K11M040:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/colinhowe?i=3cdyEoBlpkc:Xgf6K11M040:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/colinhowe?a=3cdyEoBlpkc:Xgf6K11M040:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/colinhowe?i=3cdyEoBlpkc:Xgf6K11M040:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/colinhowe/~4/3cdyEoBlpkc" height="1" width="1"/&gt;</description><pubDate>Mon, 09 Jul 2012 11:34:57 +0100</pubDate><guid isPermaLink="false">http://www.colinhowe.co.uk/2012/jul/09/mongodb-collection-per-user-performance/</guid><feedburner:origLink>http://www.colinhowe.co.uk/2012/jul/09/mongodb-collection-per-user-performance/</feedburner:origLink></item><item><title>Powering Conversocial&amp;#39;s Analytics
</title><link>http://feedproxy.google.com/~r/colinhowe/~3/TQqqhoVfBgk/</link><description>&lt;h3 id="powering-conversocials-analytics"&gt;Powering Conversocial's Analytics&lt;/h3&gt;
&lt;p&gt;&lt;a rel="nofollow" href="http://www.conversocial.com/filestorage/upload/789e12a336ca44bf0323acdeb5b5ad5b.jpg"&gt;&lt;img style="float: right; margin-left: 20px;" src="http://www.conversocial.com/filestorage/upload/789e12a336ca44bf0323acdeb5b5ad5b.jpg" width="250" /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;We recently released our new analytics functionality for our customers. It allows them to see stats like:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Number of messages received each day&lt;/li&gt;
&lt;li&gt;Messages processed by each agent&lt;/li&gt;
&lt;li&gt;Response times split into buckets (less than 30 minutes, less than 1 hour, etc)&lt;/li&gt;
&lt;li&gt;Sentiment breakdown&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;All of this can be viewed for different date ranges and comparisons performed to previous time periods.&lt;/p&gt;
&lt;p&gt;We've done some interesting things to make this possible and I'd like to share them.&lt;/p&gt;
&lt;h3 style="clear: both;"&gt;Queues, MongoDB and Service Oriented Architecture&lt;/h3&gt;

&lt;img src="https://s3.amazonaws.com/colinhowe-colinhowe/queue.png" /&gt;

&lt;p&gt;When an action is performed in the system (e.g. new content arrives, someone replies to a message) an event is generated and placed in our queueing system (backed by Redis and using pyres).&lt;/p&gt;
&lt;p&gt;This is then picked up by a worker which identifies which metrics need updating (often tens of metrics for a single event) and then a call is made to our analytics API to perform the actual update.&lt;/p&gt;
&lt;p&gt;The analytics API itself then handles pushing this data into MongoDB.&lt;/p&gt;
&lt;h3 id="why-have-an-internal-api"&gt;Why have an internal API?&lt;/h3&gt;
&lt;p&gt;Creating an internal service for analytics with its own API has given us a lot of benefits:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Small and self-contained code base that deals purely with analytics - this makes debugging far simpler&lt;/li&gt;
&lt;li&gt;Failure is isolated - if the analytics servers go down then everything else carries on running&lt;/li&gt;
&lt;li&gt;Freedom to use different technologies - our main application is Django, our analytics service uses Flask and pymongo as they fit the requirements better&lt;/li&gt;
&lt;li&gt;Upgrading / changing is easier - create a new analytics machine with the new code and redirect a percentage of requests to the new machine until we're happy with it&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The two downsides to this:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;The whole system now has extra moving parts - but they're simpler so this is OK&lt;/li&gt;
&lt;li&gt;Our development environment becomes more complex - but we now have a Virtual Machine with scripts to start everything so this is moot&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id="why-mongodb"&gt;Why MongoDB?&lt;/h3&gt;
&lt;p&gt;MongoDB is very fast when things are in memory. As most of our metrics are updating the reading for now we can safely assume that all our counter updates will be hitting data that is in memory.&lt;/p&gt;
&lt;p&gt;To ensure good read performance we grouped readings for an individual metric together into a single document for each month. More on that here&lt;/p&gt;
&lt;p&gt;We also considered Redis and Cassandra but ruled them out:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Redis has a memory limit which makes it useless for us - we want our customers to be able to query data from last year in just as much detail as the data today&lt;/li&gt;
&lt;li&gt;Cassandra would have also been a good fit - it has tremendous write performance. We have no experience deploying Cassandra and a lot of experience deploying MongoDB and so we went with MongoDB&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="data-structure"&gt;Data Structure&lt;/h3&gt;
&lt;p&gt;All our metrics are stored in the same way: a single document per month with a value for each day. E.g.&lt;/p&gt;
&lt;pre&gt;{ type: 'message-count', date: '2012-07', 1: 756, 2: 754, ..., 30: 760 }&lt;/pre&gt;

&lt;p&gt;This works perfectly for simple statistics such as number of messages per day. However, it doesn't really work if you want to see how the breakdown of messages by hour. To achieve this we store 24 metrics, 1 for each hour:&lt;/p&gt;
&lt;pre&gt;{ type: 'message-count-1', date: '2012-07', 1: 1, 2: 1, ..., 30: 3 }
{ type: 'message-count-2', date: '2012-07', 1: 2, 2: 3, ..., 30: 7 }
...
{ type: 'message-count-24', date: '2012-07', 1: 1, 2: 5, ..., 30: 6 }&lt;/pre&gt;

&lt;p&gt;Then if we want to get the breakdown by hour we query all 24 of these documents and combine them to create the hourly breakdown.&lt;/p&gt;
&lt;h3 id="performance"&gt;Performance&lt;/h3&gt;
&lt;p&gt;We currently use 3 of Amazon's small instances in a replica set to power this. We haven't really stress tested read performance - most queries respond in 1 or 2 milliseconds and doing lots of them at once hasn't caused CPU usage to go above 2%.&lt;/p&gt;
&lt;p&gt;Whilst migrating our existing data over to our new analytics we maxed out at around 2,000 metric updates per second.&lt;/p&gt;
&lt;h3 id="conclusions"&gt;Conclusions&lt;/h3&gt;
&lt;p&gt;We've really enjoyed creating this new analytics system for ourselves. By isolating the entire system behind an internal API we made our lives a lot easier and simpler :)&lt;/p&gt;
&lt;h3 id="were-hiring"&gt;We're Hiring!&lt;/h3&gt;
&lt;p&gt;We're on a mission to help companies give fantastic customer service on social media such as Facebook and Twitter. &lt;/p&gt;
&lt;p&gt;If you want to join a London based VC funded startup working with fun things like Redis, MongoDB, Amazon Web Services and hundreds of millions of messages then e-mail jobs@conversocial.com with your CV and covering letter.&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/colinhowe?a=TQqqhoVfBgk:9q23F7ZJix0:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/colinhowe?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/colinhowe?a=TQqqhoVfBgk:9q23F7ZJix0:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/colinhowe?i=TQqqhoVfBgk:9q23F7ZJix0:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/colinhowe?a=TQqqhoVfBgk:9q23F7ZJix0:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/colinhowe?i=TQqqhoVfBgk:9q23F7ZJix0:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/colinhowe/~4/TQqqhoVfBgk" height="1" width="1"/&gt;</description><pubDate>Tue, 03 Jul 2012 15:27:53 +0100</pubDate><guid isPermaLink="false">http://www.colinhowe.co.uk/2012/jul/03/powering-conversocials-analytics/</guid><feedburner:origLink>http://www.colinhowe.co.uk/2012/jul/03/powering-conversocials-analytics/</feedburner:origLink></item><item><title>Considerations when Sharding
</title><link>http://feedproxy.google.com/~r/colinhowe/~3/mPwUD8b0v8g/</link><description>&lt;p&gt;&lt;i&gt;Whilst this is talking about our use of MongoDB there is relevance here for any sharding discussion.&lt;/i&gt;&lt;/p&gt;
&lt;p&gt;We currently use MongoDB at &lt;a rel="nofollow" href="http://www.conversocial.com"&gt;Conversocial&lt;/a&gt; for our main content store. We're now starting to think about how we shard as the main store is getting pretty large (150 million documents across 300gb).&lt;/p&gt;
&lt;p&gt;The easy answer for us is to use MongoDB's built-in sharding. MongoDB divides all the documents into equally sized chunks and distributes these evenly around all shards. This would cause the documents to be spread fairly even manner.&lt;/p&gt;
&lt;p&gt;The difficult question then is what to shard on? Some combination of customer account and timestamps would ensure that all customer accounts are spread roughly equally around all our boxes and give us both read and write scaling.&lt;/p&gt;
&lt;p&gt;Wonderful!&lt;/p&gt;
&lt;p&gt;Except, if a shard goes down (assuming worst case scenarios) then ALL customers are affected to some extent. Reporting the temporary disappearance of &lt;i&gt;some&lt;/i&gt; of their data will be quite difficult.&lt;/p&gt;
&lt;p&gt;Instead we could limit the shard key to just customer account. This changes our failure scenario such that a subset of customers would temporarily lose access to ALL their data but the number of customers affected would be reduced. This scenario is also far simpler to explain to customers. A downside of this is that individual customers get no benefit from reads and writes being distributed around more servers and potentially running faster as a result.&lt;/p&gt;
&lt;p&gt;This sounds good - all our customers data will be evenly distributed (in terms of storage) across our shards and the failure scenario will only affect subsets of customers. Unfortunately, not all customers are created equal. Some customers have a lot of data but don't do a lot with it. Some customers have a lot of data AND do a lot with it. We can easily imagine scenarios where one server with 100gb of data on it has a far lower number of queries than another server with 100gb of data - simply because the latter server has more demanding customers placed on it. In general, this shouldn't happen as customers will be distributed randomly and so the heavy hitters should be distributed fairly evenly. In practice, we must consider worst case scenarios.&lt;/p&gt;
&lt;p&gt;To top this off - we also have customers that have more demanding performance requirements (and increasingly, regulatory requirements in terms of &lt;i&gt;where&lt;/i&gt; there data is and whether it is encrypted).&lt;/p&gt;
&lt;p&gt;Simply sharding based on storage won't deal with this.&lt;/p&gt;
&lt;p&gt;Instead, we're thinking of moving our sharding into our application and manually sharding based on how demanding a customer's usage is. The advantage of this is we can then place customers data in different regions if needed (an added boon of this is we can place data closer to where they are and give them better performance). The disadvantage is that we're going to have to do all the sharding work ourselves.&lt;/p&gt;
&lt;p&gt;We've still not made our decision. I put this up here simply as a reminder to others that when deciding to shard you must consider your business needs as well as trying to get bigger read/write volumes.&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/colinhowe?a=mPwUD8b0v8g:yodADZ3dnl4:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/colinhowe?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/colinhowe?a=mPwUD8b0v8g:yodADZ3dnl4:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/colinhowe?i=mPwUD8b0v8g:yodADZ3dnl4:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/colinhowe?a=mPwUD8b0v8g:yodADZ3dnl4:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/colinhowe?i=mPwUD8b0v8g:yodADZ3dnl4:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/colinhowe/~4/mPwUD8b0v8g" height="1" width="1"/&gt;</description><pubDate>Fri, 08 Jun 2012 14:05:18 +0100</pubDate><guid isPermaLink="false">http://www.colinhowe.co.uk/2012/jun/08/considerations-when-sharding/</guid><feedburner:origLink>http://www.colinhowe.co.uk/2012/jun/08/considerations-when-sharding/</feedburner:origLink></item><item><title>MongoDB - Strategies when hitting disk
</title><link>http://feedproxy.google.com/~r/colinhowe/~3/1wdWnFWIwIo/</link><description>&lt;p&gt;&lt;i&gt;I gave a lightning talk on this at the &lt;a rel="nofollow" href="http://www.meetup.com/London-MongoDB-User-Group/events/57879792/"&gt;London MongoDB User Group&lt;/a&gt; and thought I'd write it up here&lt;/i&gt;&lt;/p&gt;
&lt;p&gt;MongoDB sucks when it hits disk (ignoring SSDs). The general advice is to never hit disk. What if you have to hit disk? &lt;a rel="nofollow" href="http://www.conversocial.com"&gt;Conversocial's&lt;/a&gt; new metrics infrastructure will allow people to see statistics for their Facebook and Twitter channels going back indefinitely. In general, the data being queried and updated will be in the past month and we can keep this is memory. But, we want to let them query the data going back further than this - which means hitting disk.&lt;/p&gt;
&lt;p&gt;We found three good strategies for making hitting the disk less painful:&lt;/p&gt;
&lt;h3&gt;1. Use Single Big Documents&lt;/h3&gt;

&lt;p&gt;The naive implementation of our metrics system stored documents like this:&lt;/p&gt;
&lt;pre&gt;&lt;code class="js"&gt;{ metric: "content_count", client: 5, value: 51, date: ISODate("2012-04-01 13:00") }
{ metric: "content_count", client: 5, value: 49, date: ISODate("2012-04-02 13:00") }&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;An alternative implementation is:&lt;/p&gt;
&lt;pre&gt;&lt;code class="js"&gt;{ metric: "content_count", client: 5, month: "2012-04", 1: 51, 2: 49, ... }&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;In this case we have a single document that spans an entire month with the value for each day being a field inside the document.&lt;/p&gt;
&lt;p&gt;For a simple test we filled our database so that we had ~7gb of data on an Amazon c1.medium instance (1.7gb RAM) then tested how long it would take to read the data for an entire year and averaged this over multiple runs:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Naive implementation: 1.6s for a single year&lt;/li&gt;
&lt;li&gt;Single document per month: 0.3s&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;That's a huge difference.  The reasoning behind it is fairly simple:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The naive implementation has a worst case scenario where it has to read from the disk for all 365 documents and each of these results in a random seek&lt;/li&gt;
&lt;li&gt;Having a single document per month has a worst case scenario where it has to read from the disk for 12 documents&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;An added benefit of this strategy is that there is less overhead per day which means the working set can contain much more data.&lt;/p&gt;
&lt;p&gt;&lt;a rel="nofollow" href="http://engineering.foursquare.com/2011/02/09/mongodb-strategies-for-the-disk-averse/"&gt;Foursquare do this&lt;/a&gt;.&lt;/p&gt;
&lt;h3&gt;2. Unusual Indices&lt;/h3&gt;

&lt;p&gt;Sometimes it pays to experiment with unusual index layouts. The naive index for our metrics system is on metric, client and then date:&lt;/p&gt;
&lt;pre&gt;&lt;code class="js"&gt;db.metrics.ensureIndex({ metric: 1, client: 1, date: 1})&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;A common tip with indexing is to have all new values go to one side of the index. We reasoned that although the date was at the end of our index we would be writing to the right of lots of parts of the index so performance should be OK. We were wrong. We compared the performance of the 
above index with a new one:&lt;/p&gt;
&lt;pre&gt;&lt;code class="js"&gt;db.metrics.ensureIndex({ date: 1, metric: 1, client: 1 })&lt;/code&gt;&lt;/pre&gt;

&lt;ul&gt;
&lt;li&gt;The naive implementation performed 10k/sec inserts but after 20 million inserts the performance dropped down to 2.5k/sec inserts and occasionally stalled with lots of IO to disk. Ouch&lt;/li&gt;
&lt;li&gt;By switching to date at the start of the index our performance was kept constant at 10k/sec inserts&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;What about queries? By putting the date at the front of the index we realised we'd now have to query an entire year of data using an in query:&lt;/p&gt;
&lt;pre&gt;&lt;code class="js"&gt;db.metrics.find({
    metric: 'content_count', client: 1, date: { $in: [ "2012-01", "2012-02", ... ] }
})&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;A test of the read performance of this displayed no noticeable impact.&lt;/p&gt;
&lt;p&gt;The reasoning for this is that the naive implementation will be causing a lot of rebalancing of the trees used for the index. By switching the index around we ensured that all inserts went to one side of the index and rebalancing became a trivial operation.&lt;/p&gt;
&lt;h3&gt;3. Pre-Allocate for Locality&lt;/h3&gt;

&lt;p&gt;For most disks (not SSDs) the sequential read performance is vastly better than the random read performance. This means that we can read our metrics really fast from disk &lt;i&gt;if we read them all from the same part of the disk&lt;/i&gt;. With MongoDB documents will reside on disk in the order that you wrote them unless they are resized and need to be moved around.&lt;/p&gt;
&lt;p&gt;If we pre-allocate zero filled documents then we can force values for nearby months for the same metric to be stored on disk in the same location and then exploit the speed of sequential reads:&lt;/p&gt;
&lt;pre&gt;&lt;code class="js"&gt;db.metrics.insert([
    { metric: 'content_count', client: 3, date: '2012-01', 0: 0, 1: 0, 2: 0, ... }
    { .................................., date: '2012-02', ... })
    { .................................., date: '2012-03', ... })
    { .................................., date: '2012-04', ... })
    { .................................., date: '2012-05', ... })
    { .................................., date: '2012-06', ... })
    { .................................., date: '2012-07', ... })
    { .................................., date: '2012-08', ... })
    { .................................., date: '2012-09', ... })
    { .................................., date: '2012-10', ... })
    { .................................., date: '2012-11', ... })
    { .................................., date: '2012-12', ... })
])

&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Now, when client 3 wants their values for 'content_count' for the past year we can serve it using one big sequential read.&lt;/p&gt;
&lt;p&gt;And the benchmarks?&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Reading an entire year without pre-allocation: 62ms&lt;/li&gt;
&lt;li&gt;Reading an entire year with pre-allocation: 6.6ms&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Despite the performance gains from this we decided not to do this. Pre-allocation can get expensive for sparse data: you end up wasting a lot of space storing zeros that are never changed.&lt;/p&gt;
&lt;h3&gt;Conclusions&lt;/h3&gt;

&lt;p&gt;MongoDB can be made to have decent disk performance. You've just got to do some of the work yourself to ensure that reads aren't &lt;b&gt;too&lt;/b&gt; expensive.&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/colinhowe?a=1wdWnFWIwIo:0q_viCrBGsk:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/colinhowe?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/colinhowe?a=1wdWnFWIwIo:0q_viCrBGsk:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/colinhowe?i=1wdWnFWIwIo:0q_viCrBGsk:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/colinhowe?a=1wdWnFWIwIo:0q_viCrBGsk:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/colinhowe?i=1wdWnFWIwIo:0q_viCrBGsk:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/colinhowe/~4/1wdWnFWIwIo" height="1" width="1"/&gt;</description><pubDate>Thu, 26 Apr 2012 15:26:33 +0100</pubDate><guid isPermaLink="false">http://www.colinhowe.co.uk/2012/apr/26/mongodb-strategies-when-hitting-disk/</guid><feedburner:origLink>http://www.colinhowe.co.uk/2012/apr/26/mongodb-strategies-when-hitting-disk/</feedburner:origLink></item><item><title>This is What We Make Happen
</title><link>http://feedproxy.google.com/~r/colinhowe/~3/aoca4s90vKU/</link><description>&lt;p&gt;In a startup it's easy to lose focus. When you're worrying about investment, recruitment, customer requirements and all the other things you need to do, it's easy to lose sight of the problem your company is trying to solve.&lt;/p&gt;
&lt;p&gt;This is where a simple daily e-mail can really help. At &lt;a rel="nofollow" href="http://www.conversocial.com"&gt;Conversocial&lt;/a&gt; we all get an email every day with five random conversations that our customers have had with their customers (the conversations are all public on Twitter and Facebook). This reminds us, on a daily basis, of what we make happen and the problem we're solving - customer service in social media. Not only does it remind us but it motivates us too - seeing people getting their issues resolved via Conversocial gets me fired up.&lt;/p&gt;
&lt;p&gt;Give it a try yourself. Find the data that reminds you what you're about and create an automated daily e-mail for it. Afterwards, drop me a comment saying how you got on with it!&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/colinhowe?a=aoca4s90vKU:HzVAjtb90hE:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/colinhowe?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/colinhowe?a=aoca4s90vKU:HzVAjtb90hE:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/colinhowe?i=aoca4s90vKU:HzVAjtb90hE:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/colinhowe?a=aoca4s90vKU:HzVAjtb90hE:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/colinhowe?i=aoca4s90vKU:HzVAjtb90hE:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/colinhowe/~4/aoca4s90vKU" height="1" width="1"/&gt;</description><pubDate>Fri, 20 Apr 2012 08:19:31 +0100</pubDate><guid isPermaLink="false">http://www.colinhowe.co.uk/2012/apr/20/this-is-what-we-make-happen/</guid><feedburner:origLink>http://www.colinhowe.co.uk/2012/apr/20/this-is-what-we-make-happen/</feedburner:origLink></item><item><title>Never use Your ORM Directly
</title><link>http://feedproxy.google.com/~r/colinhowe/~3/IDgp6nxjT_M/</link><description>&lt;p&gt;ORMs are great for helping a startup create a product quickly. The downside is that they make it incredibly easy to litter your codebase with code like this:&lt;/p&gt;
&lt;p&gt;&lt;code class="python"&gt;    post = BlogPost.objects.get(id=post_id)&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;This seems relatively innocent at first, but, as your codebase grows you'll find it suffers from a few problems:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;When you need to refactor how you access your blog posts you will need to do a search across all your code for any usage of BlogPost and check to see if it needs changing&lt;/li&gt;
&lt;li&gt;Suppose you want to check if an index in your database is still needed. You would have to do a search across the entire codebase and check a lot of irrelevant uses of BlogPost&lt;/li&gt;
&lt;li&gt;What if you wanted to completely change your ORM? All the places you used BlogPost will need changing, and there will be a lot of these places because of how you've used the ORM&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Fortunately, it's easy to avoid this kind of problem by creating another layer between your ORM and the rest of your code - an internal API of sorts.&lt;/p&gt;
&lt;p&gt;&lt;code class="python"&gt;def get_blogpost_by_id(post_id):
    BlogPost.objects.get(id=post_id)&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;Then the example above becomes:&lt;/p&gt;
&lt;p&gt;&lt;code class="python"&gt;    post = get_blogpost_by_id(post_id)&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;This will make your life easier when the codebase has grown and you need to refactor.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Changing how getting by ID works requires searching only for get_blogpost_by_id which will always be exact matches that you are interested in&lt;/li&gt;
&lt;li&gt;Finding out about index usage is easy - you only need to check the code in your internal API (and check that the API is used)&lt;/li&gt;
&lt;li&gt;A complete change of your ORM can be made transparent to the rest of your application - they still call the same methods and the API handles the switch&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This kind of approach also makes it easier to reason about how you access your data - all the access paths are in one place for you to look over. It also lends itself to exposing a real API to the rest of the world.&lt;/p&gt;
&lt;p&gt;To summarise, &lt;b&gt;never use your ORM directly&lt;/b&gt;.&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/colinhowe?a=IDgp6nxjT_M:lK3uBZEg-Yk:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/colinhowe?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/colinhowe?a=IDgp6nxjT_M:lK3uBZEg-Yk:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/colinhowe?i=IDgp6nxjT_M:lK3uBZEg-Yk:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/colinhowe?a=IDgp6nxjT_M:lK3uBZEg-Yk:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/colinhowe?i=IDgp6nxjT_M:lK3uBZEg-Yk:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/colinhowe/~4/IDgp6nxjT_M" height="1" width="1"/&gt;</description><pubDate>Sun, 15 Apr 2012 09:54:06 +0100</pubDate><guid isPermaLink="false">http://www.colinhowe.co.uk/2012/apr/15/never-use-your-orm-directly/</guid><feedburner:origLink>http://www.colinhowe.co.uk/2012/apr/15/never-use-your-orm-directly/</feedburner:origLink></item><item><title>Tackling Technical Debt
</title><link>http://feedproxy.google.com/~r/colinhowe/~3/DNjuCf1JrF4/</link><description>&lt;p&gt;Most startups accrue a large amount of &lt;a rel="nofollow" href="http://martinfowler.com/bliki/TechnicalDebt.html"&gt;technical debt&lt;/a&gt; whilst they develop the first versions of their product. This isn't a problem. Many startups don't know exactly what they need to build until after they've shown something to their users - spending time perfecting these initial versions might be valuable time wasted.&lt;/p&gt;
&lt;p&gt;This build-up of technical debt becomes a problem once the company reaches a certain size - often when the company knows what is needed and is more focussed on growing the customer base and scaling the software. This build-up of debt constantly &lt;a rel="nofollow" href="http://jamesmckay.net/2009/04/when-technical-debt-becomes-technical-bankruptcy/"&gt;slows&lt;/a&gt; &lt;a rel="nofollow" href="http://agilepainrelief.com/notesfromatooluser/2012/02/scrummaster-tales-technical-debt-is-slowing-the-team.html"&gt;down&lt;/a&gt; the technical team and reduces effectiveness. In some cases it can even bring development to a grinding halt. So, what do you do to reduce your technical debt?&lt;/p&gt;
&lt;h3&gt;Get your Debt Visible&lt;/h3&gt;

&lt;p&gt;Before you embark on reducing your debt it it is worth spending time helping everyone understand the problem. Your sales and marketing folks probably don't (yet) care about your refactoring needs, further, some on your technical team might not be as exposed to your debt as others. &lt;b&gt;It's hard to justify spending time on a problem if not everybody agrees that there is a problem&lt;/b&gt;.&lt;/p&gt;
&lt;p&gt;So, get your debt visible. You can use fancy solutions such as &lt;a rel="nofollow" href="http://www.geckoboard.com/"&gt;Geckoboard&lt;/a&gt; to allow everyone to see the impact in real-time. Alternatively, simply sending an email every day or week summarising the situation can work wonders.&lt;/p&gt;
&lt;p&gt;But, what do you show? There are lots of ways to measure the cost of your technical debt and here's a few to get you thinking:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Code complexity&lt;/li&gt;
&lt;li&gt;Test coverage&lt;/li&gt;
&lt;li&gt;Time spent on support issues due to technical debt&lt;/li&gt;
&lt;li&gt;Slowdowns in new development caused by working around existing problems&lt;/li&gt;
&lt;li&gt;$ worth of transactions lost due to technical debt (e.g. a client walks away due to instability in your platform)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;An easily understood measure that is close to the business is your strongest ally for getting your business behind your attempts to reduce technical debt. &lt;/p&gt;
&lt;p&gt;Once your business understands that there is a problem it is usually worthwhile explaining &lt;i&gt;how&lt;/i&gt; you've got this problem. It's not because you're cowboys, it's because you wanted to help the business get established quickly.&lt;/p&gt;
&lt;h3&gt;Paying off the Debt&lt;/h3&gt;

&lt;h4&gt;How Much to Pay Off&lt;/h4&gt;

&lt;p&gt;It is tempting to spend a lot of time paying off all technical debt and making everything rock solid. This has a few drawbacks:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;It's demoralising to spend a long time reworking old code&lt;/li&gt;
&lt;li&gt;It's demoralising not creating new and exciting things&lt;/li&gt;
&lt;li&gt;It's impossible - you'll always have something that you're not totally happy with&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Instead, I find it works best to nibble away at technical debt a little bit at a time. E.g. have a week or two of codepocalypse and then get back to normal. If you've taken the time to decide on a measure for your debt then you can use this measure to decide how long and often these binges should last.&lt;/p&gt;
&lt;h4&gt;What to Pay Off&lt;/h4&gt;

&lt;p&gt;When faced with a lot of technical debt there is a tendency to favour the massive rewrite. &lt;a rel="nofollow" href="http://www.joelonsoftware.com/articles/fog0000000069.html"&gt;Resist that urge&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The most important thing to do is to prioritise your debt. It's easy to send your team off to tackle their favourite bit of debt, but, that doesn't guarantee good results. I tend to prioritise these things:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;b&gt;Hard stuff&lt;/b&gt;. The hard stuff is rewarding and is unlikely to ever get fixed in normal day-to-day development&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Infrastructure&lt;/b&gt;. Infrastructure, (e.g. server setup, error reporting, logging) is also likely to be put off to another day during normal development&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Common moans&lt;/b&gt;. &lt;a rel="nofollow" href="http://gettingreal.37signals.com/ch10_Optimize_for_Happiness.php"&gt;Optimise for happiness&lt;/a&gt;. If everybody groans when they have to work in a certain area then get that fixed&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;Useful Techniques&lt;/h4&gt;

&lt;h5&gt;Delete and Simplify Functionality&lt;/h5&gt;

&lt;p&gt;Remember that reporting functionality written six months ago as a trial feature? Does anyone use it? No? Delete it. What about the ultra-granular analytics you provide? Simplify them if no-one uses the granularity.&lt;/p&gt;
&lt;p&gt;Scan your entire product for functions no longer called and features no longer used. Deleted code incurs no debt.&lt;/p&gt;
&lt;h5&gt;The Debt Register&lt;/h5&gt;

&lt;p&gt;&lt;a rel="nofollow" href="http://madebymany.com/blog/a-simple-strategy-for-managing-technical-debt"&gt;Tracking your technical debt in a register&lt;/a&gt; is a great way to help prioritise. It can also be useful for reducing technical debt. Just the act of registering a bit of technical debt down can be enough to spur someone into great efforts to reduce it - pride is a wonderful thing :)&lt;/p&gt;
&lt;p&gt;A debt register is also helpful in planning normal development. If everyone knows where the debt is then you're less likely to get nasty surprises when starting work on new functionality.&lt;/p&gt;
&lt;h5&gt;Classify Your Code's Debt Appetite&lt;/h5&gt;

&lt;p&gt;Some code is peripheral and can have a high amount of debt before it becomes a real problem. Some code can't. Your layer for talking to your database should have a very small amount of technical debt. Your cron job that sends a daily summary of new customer counts can tolerate a fair amount of technical debt.&lt;/p&gt;
&lt;p&gt;Take the time to identify the areas of your system that are not tolerant of debt. Make sure everyone knows what these areas are and why.&lt;/p&gt;
&lt;h5&gt;Got Money? Use it&lt;/h5&gt;

&lt;p&gt;When bootstrapping a startup there are always a few technical decisions that are driven by financial reasons, e.g. "let's use these hosts because they give us free credit". Sometimes these decisions incur a lot of technical debt and the debt can easily be paid off with the sprinkling of some cash. Evaluate your past decisions (easy if you have a technical debt register) and decide if any can be wiped out with a bit of money.&lt;/p&gt;
&lt;h4&gt;How to Reduce Further Growth&lt;/h4&gt;

&lt;p&gt;After paying off your debt further growth of technical debt is inevitable and is not necessarily a bad thing. If taking on technical debt allows your company to move faster on things it needs to then it is a debt worth taking. When taking on more debt remember to keep the company aware of this so that it is not taken by surprise later.&lt;/p&gt;
&lt;p&gt;Many start-ups in their early phases produce code of a general lower quality than more mature companies where quality is of greater importance than speed. Shifting your start-up team into this mentality can be hard. Getting the debt visible is a big step towards changing this mindset. Encouraging the team to have a big discussion about how to produce better quality code can create a surprisingly rapid change due to the team being much more bought into the solutions that are thought of. &lt;/p&gt;
&lt;h3&gt;Conclusions&lt;/h3&gt;

&lt;p&gt;Technical debt is an inevitable result of any startup trying to move fast. The trick is to not get drowned by it.&lt;/p&gt;
&lt;h3&gt;Further Reading&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a rel="nofollow" href="http://www.quora.com/How-can-startups-move-fast-while-minimising-technical-debt"&gt;A really good response on Quora&lt;/a&gt; about how to reduce the amount of technical debt created in the first place&lt;/li&gt;
&lt;li&gt;&lt;a rel="nofollow" href="http://www.infoq.com/articles/technical-debt-levison"&gt;An InfoQ article&lt;/a&gt; on managing technical debt&lt;/li&gt;
&lt;li&gt;&lt;a rel="nofollow" href="http://blog.ircmaxell.com/2012/03/power-of-technical-debt.html"&gt;A good blog post&lt;/a&gt; on what technical debt is and classifying it&lt;/li&gt;
&lt;/ul&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/colinhowe?a=DNjuCf1JrF4:iyB5AjNb_PI:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/colinhowe?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/colinhowe?a=DNjuCf1JrF4:iyB5AjNb_PI:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/colinhowe?i=DNjuCf1JrF4:iyB5AjNb_PI:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/colinhowe?a=DNjuCf1JrF4:iyB5AjNb_PI:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/colinhowe?i=DNjuCf1JrF4:iyB5AjNb_PI:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/colinhowe/~4/DNjuCf1JrF4" height="1" width="1"/&gt;</description><pubDate>Sun, 08 Apr 2012 20:07:01 +0100</pubDate><guid isPermaLink="false">http://www.colinhowe.co.uk/2012/apr/08/tackling-technical-debt/</guid><feedburner:origLink>http://www.colinhowe.co.uk/2012/apr/08/tackling-technical-debt/</feedburner:origLink></item><item><title>If Your Database Server Fails, What Happens?
</title><link>http://feedproxy.google.com/~r/colinhowe/~3/s3bCllXvYCw/</link><description>&lt;p&gt;&lt;i&gt;I'm writing up everything I think someone running engineering at a startup needs to know. Every post will have a checklist at the end that can be used as a quick sanity check.&lt;br /&gt;&lt;br/&gt;This is my first post for the series. Let me know what you think!&lt;/i&gt;&lt;/p&gt;
&lt;h3&gt;If Your Database Server Fails, What Happens?&lt;/h3&gt;

&lt;p&gt;Losing all your data is generally not an option. To prevent total data loss, most people look to backups. There is far more to backups than just copying the files somewhere with a cron job - the realm of backups is big enough that you can get a job as a backup engineer if you wished. In some cases, companies run quite happily without any traditional backups and instead rely on replication of data to enough locations that their data would survive anything short of a world war.&lt;/p&gt;
&lt;p&gt;That said, there are some basic techniques and also some more advanced techniques you should be aware of to safeguard your data.&lt;/p&gt;
&lt;h3&gt;Techniques you Should be Aware of&lt;/h3&gt;

&lt;h4&gt;DR Tests&lt;/h4&gt;

&lt;p&gt;I want to talk about recovery before backups - there is no point in backups if you cannot recover. &lt;/p&gt;
&lt;p&gt;A DR (disaster recovery) test is a full or partial test of your recovery process. This can be made as close to a real disaster as you like, and the closer you get to reality the more confidence you can have in your recovery process.&lt;/p&gt;
&lt;p&gt;The most important part to test is &lt;em&gt;other people&lt;/em&gt;. You might know exactly how to handle database recovery but what if you aren't around the day that it all goes wrong?&lt;/p&gt;
&lt;p&gt;Partial DR testing can also be performed automatically. A script could be written to regularly create a new server from recent backups and check that the backups work. &lt;/p&gt;
&lt;p&gt;Examples of things to test include:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Configuration of a database server from a fresh machine&lt;/li&gt;
&lt;li&gt;Recovery of your database from backup files to a new server&lt;/li&gt;
&lt;li&gt;Switching your application to use the new server&lt;/li&gt;
&lt;li&gt;Automatic failover of your database - take care with this, if it goes wrong you've just broken your production site&lt;/li&gt;
&lt;li&gt;Communication processes - who needs to know about the disaster? Do customers need to know? Do you know what you would tell them?&lt;/li&gt;
&lt;li&gt;Failure in recovery - what if the most recent backup is corrupt?&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Whilst you are thinking about it, you shouldn't just limit DR testing to your servers. What about your office? What about people? &lt;/p&gt;
&lt;h5&gt;Benefits&lt;/h5&gt;

&lt;p&gt;A DR test will prove that you can actually recover in the event of a disaster. Until then, you're just hoping. The first time you do it, you will encounter flaws in your process. Testing it gives you the opportunity to improve it before you're doing it for real.&lt;/p&gt;
&lt;p&gt;Doing regular DR tests will also ensure that other people know how to cope in a disaster. &lt;/p&gt;
&lt;h5&gt;Problems&lt;/h5&gt;

&lt;p&gt;DR tests can be time consuming to perform and can lead to real downtime if you are testing the failover of real components. The best way to mitigate this is to do such testing out of busy times to minimise impact.&lt;/p&gt;
&lt;p&gt;The hardest part of doing DR testing is remembering to do it regularly. As your company evolves your recovery processes might need to evolve too, regular DR tests will highlight this need.&lt;/p&gt;
&lt;h4&gt;Full Database Backups&lt;/h4&gt;

&lt;p&gt;A full database backup is a useful but surprisingly hard thing to get right. Ensure you thoroughly read everything available on backups for your chosen database technology. &lt;/p&gt;
&lt;h5&gt;Benefits&lt;/h5&gt;

&lt;p&gt;Aside from the obvious, full database backups can also serve as good sources of data for testing or analysis (after being suitably cleansed of any confidential data).&lt;/p&gt;
&lt;h5&gt;Problems&lt;/h5&gt;

&lt;p&gt;Full database backups are &lt;em&gt;hard&lt;/em&gt; to get right.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Consistency - is the backup consistent? Have you performed whatever your database requires to ensure the data is consistent? E.g. do you prevent writes to the files whilst creating the backup&lt;/li&gt;
&lt;li&gt;Restorability - can the files be restored? This sounds silly but it's relatively easy to create backup files that simply do not work (e.g. your backup device could have run out of space)&lt;/li&gt;
&lt;li&gt;Time to restore - if backups are large they can take a long time to restore from (especially if copying from a different data centre). Backups are nice. Knowing it will take 24 hours to copy the backups isn't nice.&lt;/li&gt;
&lt;li&gt;Security - are your backups secure? Many leaks of confidential data have been where someone has gained access to backups&lt;/li&gt;
&lt;li&gt;Regularity - how regular are the backups? If they're your only option then you could lose a lot of data&lt;/li&gt;
&lt;li&gt;Load - creating a full backup can place a lot of load on the server chosen for the backup. It can also slow down network performance as large files are copied&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;Point-in-time Restoration&lt;/h4&gt;

&lt;p&gt;Some database technologies give you the option of maintaining logs of operations (often called journals or binary logs). These can then be combined with a full backup to restore data to a single point-in-time - providing your log goes back as far as the time when the full backup was taken. In general, the database will start with the full backup and then replay the logs to get the database to the desired point in time.&lt;/p&gt;
&lt;h5&gt;Benefits&lt;/h5&gt;

&lt;p&gt;Journals are generally smaller than the entire database and so can be backed more regularly than the entire database. This gives smaller windows in which data could be lost.&lt;/p&gt;
&lt;p&gt;Journals can also be used in the event of an undesirable operation being performed on the database (e.g. deleting a lot of data accidentally). This is such a common use case that there are tools for most databases that will assist you in replaying entire journals &lt;em&gt;except&lt;/em&gt; the specific commands that weren't wanted.&lt;/p&gt;
&lt;h5&gt;Problems&lt;/h5&gt;

&lt;p&gt;Replaying journals can take a long time. Combine that time with the time it takes to restore a full backup and you could have a long wait on your hands.&lt;/p&gt;
&lt;h4&gt;One-button Recovery&lt;/h4&gt;

&lt;p&gt;If your database fails you are probably going to be stressed. When you are stressed you are more prone to making mistakes. When recovering your database the last thing you want to do is make a mistake. Creating a script that performs recovery can make life much easier. Even better is a script that can be run with one click and zero configuration.&lt;/p&gt;
&lt;h5&gt;Benefits&lt;/h5&gt;

&lt;p&gt;Aside from reducing the chances of mistakes, a recovery script will allow others to do the recovery with minimal learning on their part.&lt;/p&gt;
&lt;p&gt;Recovery scripts also make automatic DR testing much easier.&lt;/p&gt;
&lt;h5&gt;Problems&lt;/h5&gt;

&lt;p&gt;Recovery scripts need to be kept up to date. It is also tempting to make them more complicated than they need be - which can lead to bugs in your recovery process.&lt;/p&gt;
&lt;h4&gt;Automated Recovery&lt;/h4&gt;

&lt;p&gt;Automated recovery includes any process whereby a server failure is automatically detected and the server replaced by a new server that has been configured automatically.&lt;/p&gt;
&lt;h5&gt;Benefits&lt;/h5&gt;

&lt;p&gt;Downtime is kept to an absolute minimum. No more getting up in the middle of the night to restore a server.&lt;/p&gt;
&lt;h5&gt;Problems&lt;/h5&gt;

&lt;p&gt;Creating auto-recovery systems is hard. Detecting when a server has failed is hard - there are lots of ways a server could appear failed without it being a failure, e.g. a network issue could be making it appear down to your monitoring systems. Further, the failure might not be fixed by creating a new server, e.g. disk space might have run out.&lt;/p&gt;
&lt;h4&gt;Hot Standbys&lt;/h4&gt;

&lt;p&gt;A hot standby is a secondary database server that can be switched to if the primary database fails. Typically, the standby is kept up-to-date with the primary database by replicating writes to the standby (a variety of methods for doing this exist).&lt;/p&gt;
&lt;p&gt;When the primary fails the standby is switched to and database functionality is restored, this is called failover.&lt;/p&gt;
&lt;p&gt;Most databases provide basic functionality for hot standbys but require manual failover. Some databases go further and provide functionality for automatic failover.&lt;/p&gt;
&lt;h5&gt;Benefits&lt;/h5&gt;

&lt;p&gt;Hot standbys are primarily useful when uptime is a concern. In general, a hot backup will allow you to restore database functionality within a few minutes (if not seconds).&lt;/p&gt;
&lt;p&gt;A further benefit is that very little data is lost - typically, only the data that was written to the primary but hadn't yet been written to the standby. In some configurations it is possible to have zero data loss - some databases allow for writes to be classed as successful only when they have been replicated to a standby.&lt;/p&gt;
&lt;h5&gt;Problems&lt;/h5&gt;

&lt;p&gt;Most of the problems of hot standbys are related to how they have been configured.&lt;/p&gt;
&lt;p&gt;One of the biggest problems with a hot standby is that they do little to protect against malicious (or accidental) data corruption. If someone deletes all the data on the primary server then it will likely be deleted from the standby too. Time-delayed standbys can be used to solve this problem.&lt;/p&gt;
&lt;p&gt;The placement of a hot standby is important. If the standby is in the same data centre as the primary then it is useless in the event of power outages, network outages, etc.&lt;/p&gt;
&lt;p&gt;It is tempting to have standby servers running on cheaper hardware than the primary servers. This may save money but has the downside that performance may be degraded when the primary server fails. It also increases the risk that the standby cannot keep up-to-date with the primary server.&lt;/p&gt;
&lt;h4&gt;Time-delayed Replicas&lt;/h4&gt;

&lt;p&gt;A time-delayed replica is the same as a hot standby &lt;em&gt;except it is intentionally behind the primary server&lt;/em&gt;. Typically, a time-delayed replica is kept an hour or so behind the primary. Generally, the time-delayed standby is kept out of the set of servers that can be failed over to automatically.&lt;/p&gt;
&lt;h5&gt;Benefits&lt;/h5&gt;

&lt;p&gt;A time-delayed replica is useful when the worst should happen: someone accidentally deletes your data (or maliciously during a security breach). Having a time-delayed replica makes it quicker to recover in these situations. It can either by failed over to directly (losing a little bit of data) or allowed to catch up until just before the point of disaster.&lt;/p&gt;
&lt;h5&gt;Problems&lt;/h5&gt;

&lt;p&gt;Whilst useful, time-delayed replicas add extra costs that might not be needed. They also add additional overheads in configuration and maintenance.&lt;/p&gt;
&lt;h4&gt;No Master Server&lt;/h4&gt;

&lt;p&gt;Not having a master server means that writes can go to any of a number of servers. If one server should go down then writes can continue being performed to the other servers.&lt;/p&gt;
&lt;p&gt;There are many different ways to do this and each database technology has its own of doing it. Needless to say, many SQL implementations support this (e.g. Postgres, MySQL) as well as other technologies that do not use SQL (e.g. Cassandra, Riak).&lt;/p&gt;
&lt;h5&gt;Benefits&lt;/h5&gt;

&lt;p&gt;Having multiple masters makes it easier to keep writes going even in the event of a server failure.&lt;/p&gt;
&lt;h5&gt;Problems&lt;/h5&gt;

&lt;p&gt;If enough servers go down then writes become impossible. Ensuring that servers are in different racks (at the least) or in different data centres reduces the chance of multiple servers going down at the same time.&lt;/p&gt;
&lt;p&gt;In general, the SQL implementations are significantly more complex than normal master-slave replication. The non-SQL technologies that have no master are unfamiliar to most developers and this could slow down your development (once the technologies are learnt you might find you develop faster as downtime issues occupy less time).&lt;/p&gt;
&lt;h3&gt;Checklist&lt;/h3&gt;

&lt;p&gt;As I said earlier, there is a lot more to backups than you might have thought. As with all things in technology there are a lot of trade-offs you will need to consider when designing your recovery processes. To make your decisions a bit easier I've put together a checklist for your backups in rough order of importance.&lt;/p&gt;
&lt;h4&gt;The Bare Minimum&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;Backups are made automatically&lt;/li&gt;
&lt;li&gt;There is a written down restoration process that can be followed by any technical person in the company&lt;/li&gt;
&lt;li&gt;At least one DR test when you think you have the rest of this list done&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;Recommended&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;Periodic DR tests&lt;/li&gt;
&lt;li&gt;There is a one-click restoration script that can be run by any technical person in the company&lt;/li&gt;
&lt;li&gt;Backups are manually checked regularly by restoring to a server&lt;/li&gt;
&lt;li&gt;Backups are automatically checked regularly by restoring to a server&lt;/li&gt;
&lt;li&gt;Hot standbys are available that can be manually switched to&lt;/li&gt;
&lt;li&gt;Restoration can be made to a single point in time&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;Nice to haves&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;Hot standbys are available that are automatically switched to&lt;/li&gt;
&lt;li&gt;Hot standbys are in a different data centre&lt;/li&gt;
&lt;li&gt;At least one time-delayed replica&lt;/li&gt;
&lt;li&gt;There is no master database and loss of one server has no impact on write/read availabilty&lt;/li&gt;
&lt;/ul&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/colinhowe?a=s3bCllXvYCw:eiuNLykA3bU:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/colinhowe?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/colinhowe?a=s3bCllXvYCw:eiuNLykA3bU:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/colinhowe?i=s3bCllXvYCw:eiuNLykA3bU:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/colinhowe?a=s3bCllXvYCw:eiuNLykA3bU:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/colinhowe?i=s3bCllXvYCw:eiuNLykA3bU:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/colinhowe/~4/s3bCllXvYCw" height="1" width="1"/&gt;</description><pubDate>Thu, 05 Apr 2012 13:57:12 +0100</pubDate><guid isPermaLink="false">http://www.colinhowe.co.uk/2012/apr/05/if-database-server-fails-what-happens/</guid><feedburner:origLink>http://www.colinhowe.co.uk/2012/apr/05/if-database-server-fails-what-happens/</feedburner:origLink></item><item><title>Django Sampler 0.6 Released
</title><link>http://feedproxy.google.com/~r/colinhowe/~3/wKD4pnqvXr4/</link><description>&lt;p&gt;I've just uploaded &lt;a rel="nofollow" href="https://github.com/colinhowe/djangosampler"&gt;Django Sampler 0.6&lt;/a&gt; to pypi!&lt;/p&gt;
&lt;p&gt;For those that don't know, Django Sampler allows you to use cost-based sampling of queries on a production site to discover what is consuming the most resources. It's not always the bad queries that need optimising, sometimes its the little ones that run a lot :)&lt;/p&gt;
&lt;p&gt;There is only one new feature but I'm quite excited by it: &lt;b&gt;samples are now split by day&lt;/b&gt;. This allows you to see how your queries change over time. If you perform some optimisations then you should be able to see the difference in the data by choosing which day you want to see. We've found this functionality really useful at Conversocial for assessing how much of an impact different changes have made.&lt;/p&gt;
&lt;p&gt;Enjoy!&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/colinhowe?a=wKD4pnqvXr4:vaNoJ92s-kc:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/colinhowe?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/colinhowe?a=wKD4pnqvXr4:vaNoJ92s-kc:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/colinhowe?i=wKD4pnqvXr4:vaNoJ92s-kc:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/colinhowe?a=wKD4pnqvXr4:vaNoJ92s-kc:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/colinhowe?i=wKD4pnqvXr4:vaNoJ92s-kc:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/colinhowe/~4/wKD4pnqvXr4" height="1" width="1"/&gt;</description><pubDate>Sun, 01 Apr 2012 10:08:55 +0100</pubDate><guid isPermaLink="false">http://www.colinhowe.co.uk/2012/apr/01/djangosampler-0_6/</guid><feedburner:origLink>http://www.colinhowe.co.uk/2012/apr/01/djangosampler-0_6/</feedburner:origLink></item><item><title>Why every Developer should have Redis
</title><link>http://feedproxy.google.com/~r/colinhowe/~3/4LsIXKoW4xM/</link><description>&lt;h4&gt;Queues, counters and polling&lt;/h4&gt;

&lt;p&gt;How many times have you modelled a queue in SQL?&lt;/p&gt;
&lt;p&gt;What about event counters?&lt;/p&gt;
&lt;p&gt;Let's not even get started on polling for changes...&lt;/p&gt;
&lt;p&gt;Since we started using Redis we've noticed that most of our 'hard' modelling problems in SQL will fit into a Redis construct really easily:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Queues - try a list or a sorted set&lt;/li&gt;
&lt;li&gt;Counters - a key and using incr/decr work nicely&lt;/li&gt;
&lt;li&gt;Polling - Pub/sub or blocking pops on lists&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Realising this - and using it - has made a lot of our code far simpler and faster.&lt;/p&gt;
&lt;p&gt;Don't bin SQL - it's great for relational modelling. Be familiar with Redis too - it's far better for when you want non-trivial types (and it's really &lt;a rel="nofollow" href="http://porteightyeight.wordpress.com/2009/11/09/redis-benchmarking-on-amazon-ec2-flexiscale-and-slicehost/"&gt;fast&lt;/a&gt;).&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/colinhowe?a=4LsIXKoW4xM:OK14LLFZezE:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/colinhowe?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/colinhowe?a=4LsIXKoW4xM:OK14LLFZezE:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/colinhowe?i=4LsIXKoW4xM:OK14LLFZezE:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/colinhowe?a=4LsIXKoW4xM:OK14LLFZezE:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/colinhowe?i=4LsIXKoW4xM:OK14LLFZezE:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/colinhowe/~4/4LsIXKoW4xM" height="1" width="1"/&gt;</description><pubDate>Thu, 12 Jan 2012 16:38:22 +0000</pubDate><guid isPermaLink="false">http://www.colinhowe.co.uk/2012/jan/12/why-every-developer-should-have-redis/</guid><feedburner:origLink>http://www.colinhowe.co.uk/2012/jan/12/why-every-developer-should-have-redis/</feedburner:origLink></item><item><title>Getting the Size of a Specific Index in MongoDB
</title><link>http://feedproxy.google.com/~r/colinhowe/~3/7MAk5gy1GEI/</link><description>&lt;p&gt;Spent a little while trying to find the size of a specific index this morning and couldn't find any documentation on how to do it. Eventually stumbled on it in db.collection_name.stats()
&lt;pre&gt;&lt;code class="js"&gt;&amp;gt; db.content.stats()
{
    "ns" : "conversocial.content",
    "sharded" : false,
    "primary" : "main01",
    "ns" : "conversocial.content",
    "count" : 1924859,
    "size" : 1578724996,
    "avgObjSize" : 820.1769563381006,
    "storageSize" : 1746546688,
    "numExtents" : 23,
    "nindexes" : 2,
    "lastExtentSize" : 301682688,
    "paddingFactor" : 1,
    "flags" : 0,
    "totalIndexSize" : 179773888,
    "indexSizes" : {
        "_id_" : 56226352,
        "source_1_puid_1" : 123547536
    },
    "ok" : 1
}
&lt;/code&gt;&lt;/pre&gt;&lt;/p&gt;
&lt;p&gt;Also, db.collection_name.totalIndexSize(true) will list these:
&lt;pre&gt;&lt;code class="js"&gt;&amp;gt; db.content.totalIndexSize(true)
_id_   56226352
source_1_puid_1 123547536
179773888
&lt;/code&gt;&lt;/pre&gt;&lt;/p&gt;
&lt;p&gt;And an easy script to get the indices in mb:
&lt;pre&gt;&lt;code class="js"&gt;s = db.content.stats()['indexSizes']; 
for (key in s) { 
    print(key + ': ' + s[key] / (1024 * 1024))
}&lt;/code&gt;&lt;/pre&gt;&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/colinhowe?a=7MAk5gy1GEI:p1SdSfxpfcs:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/colinhowe?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/colinhowe?a=7MAk5gy1GEI:p1SdSfxpfcs:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/colinhowe?i=7MAk5gy1GEI:p1SdSfxpfcs:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/colinhowe?a=7MAk5gy1GEI:p1SdSfxpfcs:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/colinhowe?i=7MAk5gy1GEI:p1SdSfxpfcs:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/colinhowe/~4/7MAk5gy1GEI" height="1" width="1"/&gt;</description><pubDate>Sat, 24 Sep 2011 08:32:07 +0100</pubDate><guid isPermaLink="false">http://www.colinhowe.co.uk/2011/sep/24/getting-size-of-specific-index-mongodb/</guid><feedburner:origLink>http://www.colinhowe.co.uk/2011/sep/24/getting-size-of-specific-index-mongodb/</feedburner:origLink></item><item><title>Django SQL Sampler becomes Django Sampler (with Mongo support)
</title><link>http://feedproxy.google.com/~r/colinhowe/~3/EeLwzmc4VuA/</link><description>&lt;p&gt;In a &lt;a rel="nofollow" href="http://www.colinhowe.co.uk/2011/sep/04/django-sql-sampling/"&gt;previous post&lt;/a&gt; I described Django SQL Sampler as a tool that helps you find the SQL queries that are consuming the most time on a production site.&lt;/p&gt;
&lt;p&gt;I've now renamed Django SQL Sampler to Django Sampler because it now does much more. It now has a plugin architecture that makes it easier to start sampling things other than SQL queries. Using this I have added support for MongoDB. Soon, I'll be adding support for Django's views and also Celery tasks.&lt;/p&gt;
&lt;p&gt;It's on &lt;a rel="nofollow" href="http://github.com/colinhowe/djangosampler/"&gt;github&lt;/a&gt;, so check it out and let me know what you think!&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/colinhowe?a=EeLwzmc4VuA:rZbC2Bw9ywY:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/colinhowe?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/colinhowe?a=EeLwzmc4VuA:rZbC2Bw9ywY:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/colinhowe?i=EeLwzmc4VuA:rZbC2Bw9ywY:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/colinhowe?a=EeLwzmc4VuA:rZbC2Bw9ywY:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/colinhowe?i=EeLwzmc4VuA:rZbC2Bw9ywY:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/colinhowe/~4/EeLwzmc4VuA" height="1" width="1"/&gt;</description><pubDate>Wed, 21 Sep 2011 09:58:43 +0100</pubDate><guid isPermaLink="false">http://www.colinhowe.co.uk/2011/sep/21/django-sampler/</guid><feedburner:origLink>http://www.colinhowe.co.uk/2011/sep/21/django-sampler/</feedburner:origLink></item><item><title>Why I Want to See Open Source on your CV
</title><link>http://feedproxy.google.com/~r/colinhowe/~3/CR_LKaUqUNo/</link><description>&lt;p&gt;An interview is an opportunity for you to &lt;b&gt;tell&lt;/b&gt; me about your your legendary hacking and awesome communication skills. A contribution to an open source project &lt;b&gt;shows&lt;/b&gt; me this, and more.&lt;/p&gt;
&lt;p&gt;Designers don't turn up to interviews with just their CV, they bring along a portfolio of their work. They &lt;b&gt;show&lt;/b&gt; their interviewer how good they are and demonstrate their style. If you're serious about software, you should do the same.&lt;/p&gt;
&lt;p&gt;By making a single contribution to an open source project you show me that you can:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Code&lt;/li&gt;
&lt;li&gt;Communicate&lt;/li&gt;
&lt;li&gt;and, Analyse
&lt;/ul&gt;

&lt;p&gt;It also demonstrates a commitment to wanting to be a developer.&lt;/p&gt;
&lt;h3&gt;It doesn't have to be a lot&lt;/h3&gt;

&lt;p&gt;I'm not looking for a massive commit. A single line patch, a bug report or a documentation change are all far better than nothing.&lt;/p&gt;
&lt;p&gt;If you are serious, you can do any of this in a day. &lt;/p&gt;
&lt;h3&gt;Don't be afraid&lt;/h3&gt;

&lt;p&gt;Your code isn't perfect. Nobody's is. There isn't any perfect art either.&lt;/p&gt;
&lt;p&gt;If you're just starting out then I expect you to not be as good as someone who has had a lot of experience. By seeing what you can do I can figure out your strengths and weaknesses and decide if they're things that we can work on.&lt;/p&gt;
&lt;p&gt;There are lots of open source projects out there that are begging for commits. Most of them will welcome you with open arms. Remember that annoying little bug you encountered in that tool you used yesterday? You could fix that. &lt;/p&gt;
&lt;h3&gt;What's the best thing to do?&lt;/h3&gt;

&lt;p&gt;There are lots of ways to contribute. Below I've listed some of the ways you can do so in descending order.&lt;/p&gt;
&lt;h4&gt;Fix a complex bug&lt;/h4&gt;

&lt;p&gt;Fixing a complex bug shows that you've understood a complex system. It shows you can work with other people and it shows you can code.&lt;/p&gt;
&lt;h4&gt;Add/improve a feature&lt;/h4&gt;

&lt;p&gt;This is below fixing a complex bug because it's normally easier to add or improve an existing feature than it is to understand a big system.&lt;/p&gt;
&lt;h4&gt;Create an awesome bug report&lt;/h4&gt;

&lt;p&gt;An awesome bug report contains:
&lt;ul&gt;
&lt;li&gt;Detailed reproduction steps&lt;/li&gt;
&lt;li&gt;Expected result&lt;/li&gt;
&lt;li&gt;Actual result&lt;/li&gt;
&lt;li&gt;A test case&lt;/li&gt;
&lt;/ul&gt;
Doing this shows me how you think and that you can express your thoughts clearly. Writing a test case shows that you can actually write automated tests.&lt;/p&gt;
&lt;h4&gt;Fix a simple bug&lt;/h4&gt;

&lt;p&gt;This shows you can work with other people and it shows you can code.&lt;/p&gt;
&lt;h4&gt;Write a documentation change&lt;/h4&gt;

&lt;p&gt;Documentation is crucial to most large projects. It's also often done with less effort than the rest of the project.&lt;/p&gt;
&lt;p&gt;Helping out with documentation shows me how you communicate and that you understand the system you're writing about.&lt;/p&gt;
&lt;h4&gt;Help on mailing lists&lt;/h4&gt;

&lt;p&gt;Subscribe to a projects mailing list. If you see a question come in that you can answer - answer it!&lt;/p&gt;
&lt;h3&gt;Variety is Key&lt;/h3&gt;

&lt;p&gt;Doing a variety of the tasks above shows that you are all-round amazing. Fixing lots and lots of simple bugs is great but I'd rather see one simple bug fix and one documentation change - it eliminates more unknowns if I can see a variety in what you do.&lt;/p&gt;
&lt;h3&gt;Start Small&lt;/h3&gt;

&lt;p&gt;Jumping in to rewriting something is a big task. You'll likely not finish it either if it is your first time. So, start on something small.&lt;/p&gt;
&lt;h3&gt;Get on with it!&lt;/h3&gt;

&lt;p&gt;Stop reading this and get out their and do something. Anything.&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/colinhowe?a=CR_LKaUqUNo:OL2Q5XW8jzk:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/colinhowe?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/colinhowe?a=CR_LKaUqUNo:OL2Q5XW8jzk:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/colinhowe?i=CR_LKaUqUNo:OL2Q5XW8jzk:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/colinhowe?a=CR_LKaUqUNo:OL2Q5XW8jzk:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/colinhowe?i=CR_LKaUqUNo:OL2Q5XW8jzk:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/colinhowe/~4/CR_LKaUqUNo" height="1" width="1"/&gt;</description><pubDate>Mon, 12 Sep 2011 07:32:50 +0100</pubDate><guid isPermaLink="false">http://www.colinhowe.co.uk/2011/sep/12/why-i-want-to-see-open-source-on-your-cv/</guid><feedburner:origLink>http://www.colinhowe.co.uk/2011/sep/12/why-i-want-to-see-open-source-on-your-cv/</feedburner:origLink></item><item><title>Django SQL Sampling
</title><link>http://feedproxy.google.com/~r/colinhowe/~3/VAyYGUlfZ6o/</link><description>&lt;p&gt;Any site of a large size with have lots of different queries going on. If you're having performance trouble it's easy to find and fix the ones that are causing trouble. It's not so easy if you have a query that runs moderately fast but is run a lot. It's not even easy to find such  queries.&lt;/p&gt;
&lt;p&gt;Enter Django SQL Sampler (&lt;a rel="nofollow" href="https://github.com/colinhowe/djangosampler"&gt;github link&lt;/a&gt;). This little tool that I wrote will sample a configurable percentage of SQL queries and group them by stack trace. It then gives you a view over these queries that allows you to find the queries that are taking up the most cumulative time - these are the queries that should be optimised or cached.&lt;/p&gt;
&lt;p&gt;Let me know what you think!&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/colinhowe?a=VAyYGUlfZ6o:-gFkfZMkMMs:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/colinhowe?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/colinhowe?a=VAyYGUlfZ6o:-gFkfZMkMMs:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/colinhowe?i=VAyYGUlfZ6o:-gFkfZMkMMs:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/colinhowe?a=VAyYGUlfZ6o:-gFkfZMkMMs:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/colinhowe?i=VAyYGUlfZ6o:-gFkfZMkMMs:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/colinhowe/~4/VAyYGUlfZ6o" height="1" width="1"/&gt;</description><pubDate>Sun, 04 Sep 2011 19:18:04 +0100</pubDate><guid isPermaLink="false">http://www.colinhowe.co.uk/2011/sep/04/django-sql-sampling/</guid><feedburner:origLink>http://www.colinhowe.co.uk/2011/sep/04/django-sql-sampling/</feedburner:origLink></item><item><title>Profiling Eventlet
</title><link>http://feedproxy.google.com/~r/colinhowe/~3/7ic6GxuCA0c/</link><description>&lt;p&gt;I wanted to profile our system that is using eventlet. I wasn't happy with any of the existing solutions so I rolled my own: &lt;a rel="nofollow" href="https://github.com/colinhowe/eventlet_profiler"&gt;https://github.com/colinhowe/eventlet_profiler&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The existing solutions have a few failures:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;You can't figure out how much time a specific function spends calling another function&lt;/li&gt;
&lt;li&gt;They generally use CPU time used instead of wall clock time (which is useful if your bottleneck is MySQL or similar)&lt;/li&gt;
&lt;li&gt;They don't track how much time is spent doing nothing (i.e. waiting for greenlets to finish)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Feedback is welcome!&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/colinhowe?a=7ic6GxuCA0c:hcTrA1uEIas:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/colinhowe?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/colinhowe?a=7ic6GxuCA0c:hcTrA1uEIas:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/colinhowe?i=7ic6GxuCA0c:hcTrA1uEIas:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/colinhowe?a=7ic6GxuCA0c:hcTrA1uEIas:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/colinhowe?i=7ic6GxuCA0c:hcTrA1uEIas:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/colinhowe/~4/7ic6GxuCA0c" height="1" width="1"/&gt;</description><pubDate>Fri, 26 Aug 2011 16:36:51 +0100</pubDate><guid isPermaLink="false">http://www.colinhowe.co.uk/2011/aug/26/profiling-eventlet/</guid><feedburner:origLink>http://www.colinhowe.co.uk/2011/aug/26/profiling-eventlet/</feedburner:origLink></item><item><title>My Ideal Pair-Programming Setup
</title><link>http://feedproxy.google.com/~r/colinhowe/~3/-7BLG86HANM/</link><description>&lt;p&gt;Whenever I'm pair-programming with someone we often hit a spot where we both want to experiment and try stuff out - different ways of approaching a problem, playing with APIs, whatever. &lt;/p&gt;
&lt;p&gt;Going back to your own computer when you hit this spot isn't great. By moving away from the other person you lose the ability to quickly show each other code and discuss things.&lt;/p&gt;
&lt;p&gt;I think I've finally figured out the ideal setup to solve this problem.&lt;/p&gt;
&lt;p&gt;Three monitors side-by-side. Each person has their own mouse and keyboard. Two chairs reasonably close together.&lt;/p&gt;
&lt;p&gt;The person on the left gets sole control of the left monitor. The person on the right gets sole control of the monitor on the right. The monitor in the middle is shared ground. Either person can drag their windows from their monitor across to the shared one when they have something they want to share, talk about or pair on properly.&lt;/p&gt;
&lt;p&gt;This allows people to pair really quickly and easily. It also allows you to switch back to your own experiments just as easily. You could even take it further and have this setup in a long row where everyone excluding the edge people have two people to pair with at any time. Or, further still, a circle of programming.&lt;/p&gt;
&lt;p&gt;Sounds great to me... but, I don't know if there is any combination of software and hardware already available that would make this work. If anyone has an idea I'd love to hear it!&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/colinhowe?a=-7BLG86HANM:SVr2A0rC1FA:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/colinhowe?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/colinhowe?a=-7BLG86HANM:SVr2A0rC1FA:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/colinhowe?i=-7BLG86HANM:SVr2A0rC1FA:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/colinhowe?a=-7BLG86HANM:SVr2A0rC1FA:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/colinhowe?i=-7BLG86HANM:SVr2A0rC1FA:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/colinhowe/~4/-7BLG86HANM" height="1" width="1"/&gt;</description><pubDate>Wed, 24 Aug 2011 18:39:15 +0100</pubDate><guid isPermaLink="false">http://www.colinhowe.co.uk/2011/aug/24/my-ideal-pair-programming-setp/</guid><feedburner:origLink>http://www.colinhowe.co.uk/2011/aug/24/my-ideal-pair-programming-setp/</feedburner:origLink></item><item><title>MySQL Performance for More Data than Memory
</title><link>http://feedproxy.google.com/~r/colinhowe/~3/cgVtqdP-o6E/</link><description>&lt;p&gt;In a &lt;a rel="nofollow" href="http://www.colinhowe.co.uk/2011/02/23/mongodb-performance-for-data-bigger-than-memor/"&gt;previous post&lt;/a&gt; I looked at the performance hit you get in MongoDB when the working set is larger than the memory available. I thought I'd have a look to see how MySQL fares under similar conditions.&lt;/p&gt;
&lt;p&gt;The experiment was very similar experiment to the &lt;a rel="nofollow" href="http://www.colinhowe.co.uk/2011/02/23/mongodb-performance-for-data-bigger-than-memor/"&gt;experiment for MongoDB&lt;/a&gt;.&lt;/p&gt;
&lt;h3&gt;Setup&lt;/h3&gt;

&lt;p&gt;MySQL server: A large Amazon RDS instance. I chose this as RDS is very well configured for MySQL. I'm more interested in how performance changes so don't want to spend time on configuration.&lt;/p&gt;
&lt;p&gt;MySQL client: An EC2 small instance.&lt;/p&gt;
&lt;p&gt;Code: up on &lt;a rel="nofollow" href="https://github.com/colinhowe/mongo-benchmarks/blob/master/mysql.py"&gt;github&lt;/a&gt;.&lt;/p&gt;
&lt;h3&gt;Test&lt;/h3&gt;

&lt;p&gt;The test will involve inserting X documents to MySQL with the following structure:&lt;/p&gt;
&lt;p&gt;key: n (where n is 0, 1, ... X - 1)
text: 'Mary had a little lamb. ' x 100
There will be an index on key to prevent full scans of the data.&lt;/p&gt;
&lt;p&gt;After the insert there will be 30,000 reads with random keys.&lt;/p&gt;
&lt;p&gt;The expectation is that when the data set gets too large to fit in memory the random reads will become very slow. This will be due to the data no longer fitting in memory.&lt;/p&gt;
&lt;p&gt;When this thrashing of the disk starts happening it will be interesting to see what happens when a subset of the dataset is read from. To investigate this a further test will be run that:&lt;/p&gt;
&lt;p&gt;99% of the time - reads from a random key chosen from only Y% of the keys
1% of the time - reads from any key chosen from the entire dataset
The expectation here is that for small Y the performance will be similar to when the entire data set is in memory - as the pages that contain the subset of data will be in memory already and not need to read from disk.&lt;/p&gt;
&lt;h3&gt;Results&lt;/h3&gt;

&lt;p&gt;A result spreadsheet is available &lt;a rel="nofollow" href="https://spreadsheets0.google.com/spreadsheet/ccc?key=0At_QEIJCB2GbdEI3d3VBR2hFY1AyVTVNY0N6OGh5dXc&amp;hl=en_US#gid=1"&gt;here&lt;/a&gt; (Google Doc).&lt;/p&gt;

&lt;p&gt;The interesting part is when you reach 10,000,000 keys and start reading from subsets:
&lt;pre&gt;    Focus (%)  Read 1 (s)  Read 2 (s)  Read 3 (s)
      100        172.35      135.02      154.77
       10        124.70       95.48       77.29
        1         32.58       24.46       22.59
&lt;/pre&gt;
For comparison, the reads for a database with only 10,000 keys average out to 20.5s.&lt;/p&gt;
&lt;p&gt;Unsurprisingly, this is similar to the results with MongoDB. What I think makes this interesting is that a lot of people don't seem to be aware of this - there are a lot of discussions about how to optimise indices but not many on how to keep your working set small. I know that we've benefited from being more aware of this when optimising MySQL queries. Maybe you will too?&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/colinhowe?a=cgVtqdP-o6E:6ZIhEtZFexA:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/colinhowe?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/colinhowe?a=cgVtqdP-o6E:6ZIhEtZFexA:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/colinhowe?i=cgVtqdP-o6E:6ZIhEtZFexA:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/colinhowe?a=cgVtqdP-o6E:6ZIhEtZFexA:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/colinhowe?i=cgVtqdP-o6E:6ZIhEtZFexA:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/colinhowe/~4/cgVtqdP-o6E" height="1" width="1"/&gt;</description><pubDate>Mon, 22 Aug 2011 17:10:00 +0100</pubDate><guid isPermaLink="false">http://www.colinhowe.co.uk/2011/aug/22/mysql-performance-for-data-bigger-than-memory/</guid><feedburner:origLink>http://www.colinhowe.co.uk/2011/aug/22/mysql-performance-for-data-bigger-than-memory/</feedburner:origLink></item><item><title>Visualising Eventlet
</title><link>http://feedproxy.google.com/~r/colinhowe/~3/v8uUbDNdrCs/</link><description>&lt;p&gt;When using eventlet you typically have a lot of concurrent things all being processed at once. Trying to identify problems in highly concurrent systems can be hard. To solve a problem we had I created &lt;a rel="nofollow" href="https://github.com/colinhowe/eventlet_visualiser"&gt;eventlet visualiser&lt;/a&gt; (on github). This allows you to see the life span of all your eventlets in a program:&lt;/p&gt;
&lt;img src="https://s3.amazonaws.com/colinhowe-colinhowe/eventlet.png" /&gt;

&lt;p&gt;Each block can be clicked on and information is output in the console - such as stack trace and arguments to the function.&lt;/p&gt;
&lt;p&gt;I used this to identify that a certain class of eventlet was slowly taking over the pool and preventing execution of any other eventlet. Hopefully someone else will find it useful too :)&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/colinhowe?a=v8uUbDNdrCs:yBU4eXJ9veE:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/colinhowe?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/colinhowe?a=v8uUbDNdrCs:yBU4eXJ9veE:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/colinhowe?i=v8uUbDNdrCs:yBU4eXJ9veE:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/colinhowe?a=v8uUbDNdrCs:yBU4eXJ9veE:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/colinhowe?i=v8uUbDNdrCs:yBU4eXJ9veE:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/colinhowe/~4/v8uUbDNdrCs" height="1" width="1"/&gt;</description><pubDate>Wed, 17 Aug 2011 09:46:02 +0100</pubDate><guid isPermaLink="false">http://www.colinhowe.co.uk/2011/aug/17/visualising-eventlet/</guid><feedburner:origLink>http://www.colinhowe.co.uk/2011/aug/17/visualising-eventlet/</feedburner:origLink></item><item><title>Bash: Search and replace across multiple files
</title><link>http://feedproxy.google.com/~r/colinhowe/~3/j3Tg1TbDLjM/</link><description>&lt;p&gt;I sometimes need to do a search and replace across lots of files. So, I made a function in my .bashrc to make it easy:
&lt;pre&gt;&lt;code class="bash"&gt;function find_replace {
    FIND=$1
    REPLACE=$2
    echo "Finding $FIND and replacing with $REPLACE"
    grep -rl $FIND .
    grep -rl "$FIND" . | xargs sed -i -e s/$FIND/$REPLACE/
}&lt;/p&gt;
&lt;p&gt;&lt;/code&gt;&lt;/pre&gt;
Usage: find_replace &amp;lt;search&amp;gt; &amp;lt;replace&amp;gt;&lt;br /&gt;
e.g. find_replace hello goodbye&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/colinhowe?a=j3Tg1TbDLjM:BOswUFtLjKg:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/colinhowe?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/colinhowe?a=j3Tg1TbDLjM:BOswUFtLjKg:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/colinhowe?i=j3Tg1TbDLjM:BOswUFtLjKg:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/colinhowe?a=j3Tg1TbDLjM:BOswUFtLjKg:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/colinhowe?i=j3Tg1TbDLjM:BOswUFtLjKg:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/colinhowe/~4/j3Tg1TbDLjM" height="1" width="1"/&gt;</description><pubDate>Tue, 09 Aug 2011 08:17:33 +0100</pubDate><guid isPermaLink="false">http://www.colinhowe.co.uk/2011/aug/09/bash-search-replace-across-multiple-files/</guid><feedburner:origLink>http://www.colinhowe.co.uk/2011/aug/09/bash-search-replace-across-multiple-files/</feedburner:origLink></item><item><title>Solr, Solango and being IO bound
</title><link>http://feedproxy.google.com/~r/colinhowe/~3/AJ6uxGpE_k4/</link><description>&lt;p&gt;We just hit a problem where the indexing performance of our Solr instance dropped massively when re-indexing the entire database. At the start it would be doing 100 docs/second but after an hour or so dropped down to 10/second and carried on falling.&lt;/p&gt;
&lt;p&gt;After looking at iostat I discovered that Solr was IO bound. Specifically, the disk was maxing out writes at 20mb/s (this is on ephemeral storage on a large EC2 instance). However, our total data size at this point was only 350mb. Meaning that we were doing a large amount of rewriting of our indexes.&lt;/p&gt;
&lt;p&gt;We're using Solango (a python library) to talk to Solr. We've set it up to commit in batches of 500. However, by default Solango will invoke the optimize command after each batch instead of using commit. &lt;/p&gt;
&lt;p&gt;Optimize is a heavy-weight command that will remove deleted records from indices, compact indices and generally do all sorts of good stuff. Good stuff that smashes the disk and isn't so necessary during every little step of a re-index. &lt;/p&gt;
&lt;p&gt;So, after changing that to do a commit instead of an optimize we're now indexing roughly 150 docs/second and maintaining that pace :)&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/colinhowe?a=AJ6uxGpE_k4:bRjFLwPAXNw:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/colinhowe?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/colinhowe?a=AJ6uxGpE_k4:bRjFLwPAXNw:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/colinhowe?i=AJ6uxGpE_k4:bRjFLwPAXNw:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/colinhowe?a=AJ6uxGpE_k4:bRjFLwPAXNw:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/colinhowe?i=AJ6uxGpE_k4:bRjFLwPAXNw:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/colinhowe/~4/AJ6uxGpE_k4" height="1" width="1"/&gt;</description><pubDate>Thu, 14 Jul 2011 11:49:56 +0100</pubDate><guid isPermaLink="false">http://www.colinhowe.co.uk/2011/jul/14/solr-solango-iobound/</guid><feedburner:origLink>http://www.colinhowe.co.uk/2011/jul/14/solr-solango-iobound/</feedburner:origLink></item><item><title>Google Paying $150m to Employees - Might not be crazy
</title><link>http://feedproxy.google.com/~r/colinhowe/~3/By5ElEGBAOE/</link><description>&lt;p&gt;According to &lt;a rel="nofollow" href="http://techcrunch.com/2011/04/06/google-said-to-have-high-level-mole-at-twitter-makes-massive-counteroffers-to-retain-employees/"&gt;this article on Techcrunch&lt;/a&gt; Google have offered $150 million in stock grants vesting over four years to keep two key employees. &lt;/p&gt;
&lt;p&gt;I think that this could be a clever move by Google. Google are trying hard to get excellent engineers going to Google. There are so many companies trying to hire these same people that it is incredibly hard to stand out. When you think of it like this, $37.5 million a year might  be a better investment than a load of advertising, it's certainly caught the eye of a number of notable tech sites... plus they get to keep some people they clearly want to keep. &lt;/p&gt;
&lt;p&gt;The counter argument is that if you're an excellent engineer then you likely care far more about the environment you're in and what you're working on. Google &lt;a rel="nofollow" href="http://digitaldaily.allthingsd.com/20110405/exlusive-larry-page-mulls-google-reorg/"&gt;are trying to fix this&lt;/a&gt; - they're currently doing a reorganisation to try and make themselves less bureaucratic and more innovative. Everybody already knows that Google offer a &lt;a rel="nofollow" href="http://computer.howstuffworks.com/googleplex3.htm"&gt;lot of perks&lt;/a&gt;. Coupling all this with some big publicity about how well they reward their heroes might just be a winning strategy.&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/colinhowe?a=By5ElEGBAOE:DQmKpSVt620:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/colinhowe?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/colinhowe?a=By5ElEGBAOE:DQmKpSVt620:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/colinhowe?i=By5ElEGBAOE:DQmKpSVt620:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/colinhowe?a=By5ElEGBAOE:DQmKpSVt620:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/colinhowe?i=By5ElEGBAOE:DQmKpSVt620:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/colinhowe/~4/By5ElEGBAOE" height="1" width="1"/&gt;</description><pubDate>Thu, 07 Apr 2011 16:26:11 +0100</pubDate><guid isPermaLink="false">http://www.colinhowe.co.uk/2011/apr/07/google-150-million-not-so-crazy/</guid><feedburner:origLink>http://www.colinhowe.co.uk/2011/apr/07/google-150-million-not-so-crazy/</feedburner:origLink></item><item><title>Is AWS the right host for us?
</title><link>http://feedproxy.google.com/~r/colinhowe/~3/Uo8BFB9bhAg/</link><description>&lt;p&gt;Short answer: probably not for 80% of sites out there. But, it is right for us. To find out why, read on. For a quick guide, skip to the conclusions ;-)&lt;/p&gt;
&lt;h3 id="question-of-cost"&gt;Question of Cost&lt;/h3&gt;
&lt;h4 id="aws-generally-more-expensive-for-hardware"&gt;AWS: generally more expensive for hardware&lt;/h4&gt;
&lt;p&gt;For the hardware, AWS is expensive compared to traditional hosting (shared or dedicated). However, AWS gives you greater flexibility than a traditional host:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;If you find yourself with too much capacity you can turn off a server or two and stop paying&lt;/li&gt;
&lt;li&gt;If you find yourself with too little... you can turn them back on&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If you have spiky traffic (e.g. slow periods on weekends) then with traditional hosting you will have to pay for spare capacity &lt;em&gt;all the time&lt;/em&gt;. With AWS this isn't the case and it can result it &lt;em&gt;lower&lt;/em&gt; costs for some sites.&lt;/p&gt;
&lt;h4 id="aws-redundancy-is-cheaper"&gt;AWS: Redundancy is cheaper&lt;/h4&gt;
&lt;p&gt;On a traditional host you might need two web servers to cope with your levels of traffic. Your business can't afford to have these go down so you have two backup web servers, always on standby and always costing money.&lt;/p&gt;
&lt;p&gt;On AWS you can fire up a new server in minutes. So, instead of having two standby servers you might have one, or even none and just fire them up when something goes down (AWS can even do this for you). This isn't a luxury you get with most traditional hosts as it can take them some time to set up new servers.&lt;/p&gt;
&lt;h4 id="cost-of-staff"&gt;Cost of Staff&lt;/h4&gt;
&lt;p&gt;Hardware costs aren't the only cost. You also have the cost of staff to consider. With dedicated hardware you might need to employ someone to do your sysops far sooner than with AWS. With AWS you can find far more information out there on how to do things and have your existing team maintain the infrastructure more easily. You can also afford mistakes as you can simply start a new server if you break one ;-)&lt;/p&gt;
&lt;h4 id="testing-is-cheap"&gt;Testing is Cheap&lt;/h4&gt;
&lt;p&gt;Testing new code can be really cheap with AWS. If we have a big feature to roll-out and want to give it a thorough working over before releasing then we can start up an entire farm of servers with the new code. When we're done, we kill the farm. No more paying for test servers to sit around all day, every day, not being used.&lt;/p&gt;
&lt;h4 id="s3-is-cheap"&gt;S3 is Cheap&lt;/h4&gt;
&lt;p&gt;It's worth pointing out that S3 can be cheaper compared to most CDNs. You can use S3 without using any other part of AWS if you wish. S3 is not a CDN itself but does do a good job of serving static content. CloudFront is AWS's CDN that is built on top of S3 and performs much better. CloudFront is more expensive than S3 but is still worth exploring.&lt;/p&gt;
&lt;h4 id="cost-not-a-decider"&gt;Cost not a decider&lt;/h4&gt;
&lt;p&gt;For us, cost was not the decider. It's more expensive to be on AWS but the cost isn't so high that it's not worth the other benefits we get.&lt;/p&gt;
&lt;h3 id="scaling"&gt;Scaling&lt;/h3&gt;
&lt;p&gt;80% of people think that their site needs to scale to epic proportions. Sadly, nearly all of them are wrong. Most sites do not need to cope with a million unique visitors a day. Many sites have fairly slow growth patterns and do not need to make use of AWS to get servers up quickly as they grow.&lt;/p&gt;
&lt;p&gt;Some sites are not going to grow wildly but still need scaling.&lt;/p&gt;
&lt;p&gt;Some sites (such as ours) are rapidly changing what they do and adding new features. In this case it can be incredibly hard to determine what your hardware needs are going to be ahead of time. Having to worry about resource requirements every time you add a substantial new feature can really slow your development. As an example, at Conversocial we added some new features for searching Twitter. This new feature had the potential to double the amount of content we handle each day. Did we worry about the performance impact? Yes, for about 2 minutes. Our answer was that we would roll it out and monitor the performance, if Conversocial slowed down then we'd fire up new servers.&lt;/p&gt;
&lt;h3 id="rds-hosted-mysql"&gt;RDS: Hosted MySQL&lt;/h3&gt;
&lt;p&gt;Amazon recently added RDS to their suite of features. RDS is hosted MySQL. It handles pretty much everything:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Failover&lt;/li&gt;
&lt;li&gt;Backups&lt;/li&gt;
&lt;li&gt;Replication setups&lt;/li&gt;
&lt;li&gt;Upgrades&lt;/li&gt;
&lt;li&gt;Point in time snapshots and recovery&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;It's actually pretty amazing. This was a big deal for us. Not having to worry about all of this freed up a large chunk of our time, it also meant we haven't had to employ someone to look after MySQL for us.&lt;/p&gt;
&lt;h3 id="growing-feature-set"&gt;Growing Feature Set&lt;/h3&gt;
&lt;p&gt;AWS is constantly adding new features. Frankly, the pace is astonishing. There are some features currently lacking that we'd like (e.g. knowing we have SSDs for our disks) but we are confident that these features, or ones we haven't even thought of, will soon come out and make our lives better.&lt;/p&gt;
&lt;h3 id="community-support"&gt;Community Support&lt;/h3&gt;
&lt;p&gt;There is a thriving community around AWS. The abstracted platform makes it really easy for people to share recipes. Want a Mongo server on AWS? There's a &lt;a rel="nofollow" href="http://www.mongodb.org/display/DOCS/Amazon+EC2"&gt;guide for it&lt;/a&gt;. We don't have a dedicated sysadmin (yet) and the community support was a big pull for us.&lt;/p&gt;
&lt;h3 id="availability-zones"&gt;Availability Zones&lt;/h3&gt;
&lt;p&gt;All hosts have downtime for one reason or another (natural disaster, a bomb or someone destroying their wire to the outside world). If you're a business then you probably worry about this a lot. &lt;/p&gt;
&lt;p&gt;AWS has solved this problem by having multiple Availability Zones (AZs) across the world. Each AZ is a data centre that is isolated from all the others. Certain things are shared across multiple AZs, such as machine images and EBS snapshots. EBS is the persistent disk-based storage that you can attach to a running machine.&lt;/p&gt;
&lt;p&gt;If one AZ gets wiped off the face of the Earth then you can quickly restore your entire infrastructure on another AZ using your AMIs and snapshots. Alternatively, you can have hot standbys in different AZs that automatically take over when something goes wrong.&lt;/p&gt;
&lt;h3 id="its-cool"&gt;It's cool&lt;/h3&gt;
&lt;p&gt;I'll be honest, AWS is cool. Many technical folks get excited by it. This did play a small role in our decision making, if we want to hire great people then we want to make sure we're giving them opportunities to learn about interesting things. AWS helps us do this :)&lt;/p&gt;
&lt;h3 id="alternatives"&gt;Alternatives&lt;/h3&gt;
&lt;p&gt;Using AWS doesn't have to be an all-or-nothing undertaking. I know of people using AWS solely for their backend processing and web servers, their Mongo database is hosted on a dedicated server with an SSD raid somewhere with a good connection to AWS. Similarly, some companies use AWS solely for bulk processing of data with Hadoop.&lt;/p&gt;
&lt;h3 id="conclusions"&gt;Conclusions&lt;/h3&gt;
&lt;p&gt;AWS is not for everyone. In particular, it's not for you if:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;You're running just a single server and aren't (honestly) going to need to scale&lt;/li&gt;
&lt;li&gt;You're running a load of servers and have fairly stable hardware needs&lt;/li&gt;
&lt;li&gt;Hardware is a high cost for you (as %age of expenditures)&lt;/li&gt;
&lt;li&gt;Your system hits disk a lot (IO performance on AWS is known to be lacking)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;That said, we went with AWS and we're loving it so far. It can be slow at times and we have had an instance die on us (but we started a new one and nobody noticed anything had happened). That said, at some point in the future when Conversocial is massive and we have a better picture of our hardware needs then we might look at a mixed hosting. &lt;/p&gt;
&lt;p&gt;Here's what we have on AWS at the moment:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Solr for search&lt;/li&gt;
&lt;li&gt;Web servers running Apache&lt;/li&gt;
&lt;li&gt;MySQL (via RDS)&lt;/li&gt;
&lt;li&gt;MongoDB&lt;/li&gt;
&lt;li&gt;Memcached&lt;/li&gt;
&lt;li&gt;Backend servers running Celery&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Hope that helps. If anyone has any questions, please ask!&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/colinhowe?a=Uo8BFB9bhAg:c3nn9rJHAAg:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/colinhowe?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/colinhowe?a=Uo8BFB9bhAg:c3nn9rJHAAg:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/colinhowe?i=Uo8BFB9bhAg:c3nn9rJHAAg:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/colinhowe?a=Uo8BFB9bhAg:c3nn9rJHAAg:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/colinhowe?i=Uo8BFB9bhAg:c3nn9rJHAAg:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/colinhowe/~4/Uo8BFB9bhAg" height="1" width="1"/&gt;</description><pubDate>Tue, 05 Apr 2011 20:24:53 +0100</pubDate><guid isPermaLink="false">http://www.colinhowe.co.uk/2011/apr/05/is-aws-the-right-host-for-us/</guid><feedburner:origLink>http://www.colinhowe.co.uk/2011/apr/05/is-aws-the-right-host-for-us/</feedburner:origLink></item><item><title>How we migrated to AWS
</title><link>http://feedproxy.google.com/~r/colinhowe/~3/-DeFxLPqkbs/</link><description>&lt;p&gt;A few weeks ago we (&lt;a rel="nofollow" href="http://hello.conversocial.com"&gt;Conversocial&lt;/a&gt;) migrated our infrastructure from a shared Solaris host to AWS. I'm going to talk about &lt;em&gt;how&lt;/em&gt; we did our migration, why we chose AWS is a big enough topic for another blog post.&lt;/p&gt;
&lt;p&gt;The two goals for the migration were:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Migrate a customer at a time instead of doing a big bang migration. This meant that we could get a subset of our customers on the new infrastructure to make sure we were 100% happy before moving more and more customers across.&lt;/li&gt;
&lt;li&gt;As seamless as possible for our customers. We wanted all our customers to carry on logging in at the same URL and not really be aware that they were on different servers (in different parts of the world).&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="background-our-infrastructure"&gt;Background - Our Infrastructure&lt;/h3&gt;
&lt;p&gt;Before pressing on it's worth saying a bit about what our infrastructure looked like. The infrastructure was fairly standard as far as Python/Django setups go:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;a MySQL database with all our data in&lt;/li&gt;
&lt;li&gt;some web servers running Apache to handle requests&lt;/li&gt;
&lt;li&gt;some backend servers running Celery to do polling and process background tasks&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;With regards to size, our database wasn't massive but some of our customers did have several gigabytes of data.&lt;/p&gt;
&lt;h3 id="general-approach"&gt;General Approach&lt;/h3&gt;
&lt;p&gt;The general approach we took for each customer was as follows:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Mark the customer account as being currently migrated. This locked them out of the site but it freed us from having to worry about changes whilst we copied data around&lt;/li&gt;
&lt;li&gt;Create an SQL dump of their data&lt;/li&gt;
&lt;li&gt;Import the SQL dump in to the new database&lt;/li&gt;
&lt;li&gt;Mark the customer account as migrated in both the old and new databases&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id="locking-them-out-of-the-site"&gt;Locking them out of the site&lt;/h4&gt;
&lt;p&gt;Locking customers out of the site was done for each customer in turn. In most cases the customer got full access again within five minutes. For our largest customers they were locked out for up to two hours. This wasn't as bad as it sounds - as we could do a customer at a time we were able to identify the ideal time for each customer and migrate them at their convenience and not ours.&lt;/p&gt;
&lt;p&gt;To make things easier for the customer, the lock-down page had an auto-refresh to push them back to the site as soon as their account was migrated.&lt;/p&gt;
&lt;h4 id="dumping-their-data"&gt;Dumping their data&lt;/h4&gt;
&lt;p&gt;To dump the data for each customer we created the &lt;a rel="nofollow" href="https://github.com/colinhowe/mysqlpartialdump"&gt;MySQL Partial Dump&lt;/a&gt; tool. This tool allowed us to describe our schema and how all our tables relate to each other. It was then incredibly easy to create a MySQL dump for each customer. An added bonus is that this has given us an easy way to get data for testing environments (data cleansing is supported in the partial dump tool).&lt;/p&gt;
&lt;h4 id="handling-logins"&gt;Handling Logins&lt;/h4&gt;
&lt;p&gt;Handling logins was probably the trickiest part of the migration. Whilst both infrastructures were live we wanted customers to be able to login at a single URL and be taken to the appropriate infrastructure without noticing anything different. &lt;/p&gt;
&lt;p&gt;To handle this we created an additional subdomain: app.conversocial.com (changing to this was something we wanted to do anyway). Our existing infrastructure was hosting www.conversocial.com and we wanted customers to continue logging in at www.conversocial.com. To do this we altered our code in several ways:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;For logins the email was first checked if it was in the legacy infrastructure&lt;ul&gt;
&lt;li&gt;If the e-mail was found and the account was not migrated then the email/password was checked as normal&lt;/li&gt;
&lt;li&gt;If the e-mail did not exist or the account was migrated then our old servers made a request to the new ones, passing through an encrypted token containing the email/password. If a match was found then a one-time token was created and passed back to the user for use in a redirect response that pushed them on to app.conversocial.com.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;The forgotten password form followed a similar system&lt;/li&gt;
&lt;li&gt;All new sign-ups were directed to the new infrastructure&lt;/li&gt;
&lt;li&gt;The background tasks/pollers would only handle accounts that were on the same infrastructure as themselves&lt;/li&gt;
&lt;li&gt;All page requests went through a Django middleware that checked if the account had become migrated. If so, the user was redirected to the same URL but on app.conversocial.com. This accounted for someone using the site being migrated between requests&lt;/li&gt;
&lt;li&gt;Likewise, all AJAX requests went through a Django middleware. The difference here was that the response had to trigger some Javascript to do the redirection instead of relying on HTTP redirects&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="putting-it-all-together"&gt;Putting it all together&lt;/h3&gt;
&lt;p&gt;All of this was put together using &lt;a rel="nofollow" href="http://docs.fabfile.org/en/1.0.1/index.html"&gt;fabric&lt;/a&gt; so that we could do:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;fab migrate:&amp;lt;account IDs&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Using fabric made it simple to handle connections to several servers for moving data around.&lt;/p&gt;
&lt;h4 id="doing-a-test-run"&gt;Doing a test-run&lt;/h4&gt;
&lt;p&gt;The entire migration went through two test-runs to ensure it would work when we did it for real. It also allowed us to iron out a few kinks.&lt;/p&gt;
&lt;p&gt;There were two differences with the test-run:
&lt;em&gt; the migration flags weren't set for accounts as we migrated them. We didn't want customers suddenly using our new infrastructure before it was ready
&lt;/em&gt; the data was cleansed of all e-mails and sensitive data before copying it. This prevented our new infrastructure from starting to send e-mails to customers (the old infrastructure would also be sending them e-mails)&lt;/p&gt;
&lt;p&gt;Apart from that, it was all the same.&lt;/p&gt;
&lt;h3 id="conclusion"&gt;Conclusion&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;None of our customers noticed any unplanned down-time&lt;/li&gt;
&lt;li&gt;The vast majority of our customers didn't even notice the migration at all&lt;/li&gt;
&lt;li&gt;We didn't work crazy hours&lt;/li&gt;
&lt;li&gt;Nothing went wrong that freaked us out&lt;/li&gt;
&lt;li&gt;Small things did go wrong, but, the slow pace of the migration meant that the problems were small and isolated instead of catastrophic&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Overall, we were very happy with the migration :)&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/colinhowe?a=-DeFxLPqkbs:TM62-052QIM:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/colinhowe?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/colinhowe?a=-DeFxLPqkbs:TM62-052QIM:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/colinhowe?i=-DeFxLPqkbs:TM62-052QIM:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/colinhowe?a=-DeFxLPqkbs:TM62-052QIM:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/colinhowe?i=-DeFxLPqkbs:TM62-052QIM:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/colinhowe/~4/-DeFxLPqkbs" height="1" width="1"/&gt;</description><pubDate>Sun, 03 Apr 2011 13:13:23 +0100</pubDate><guid isPermaLink="false">http://www.colinhowe.co.uk/2011/apr/03/how-we-migrated-to-aws/</guid><feedburner:origLink>http://www.colinhowe.co.uk/2011/apr/03/how-we-migrated-to-aws/</feedburner:origLink></item><item><title>Tea rounds
</title><link>http://feedproxy.google.com/~r/colinhowe/~3/v9c8HXc--rk/</link><description>&lt;p&gt;When starting your first job there is a lot to learn. Especially for a developer. During this learning frenzy it is easy to overlook soft skills that will help you make friends in your new work place. The easiest and most important of these is how to make a tea round (this includes coffee). &lt;/p&gt;
&lt;p&gt;Making tea rounds is an important part of the work place:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;getting to know names becomes easier&lt;/li&gt;
&lt;li&gt;forces you to take a break and clear your head&lt;/li&gt;
&lt;li&gt;people like getting cups of tea made for them&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Sadly, tea round rules vary and it can be a minefield if you get the rules wrong. Fortunately, there is one universal law that will save you from upsetting your colleagues.&lt;/p&gt;
&lt;h2&gt;The universal law of tea rounds&lt;/h2&gt;

&lt;p&gt;&lt;b&gt;If you do not make rounds of tea then do not accept cups from others making them.&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;Those that ignore this rule are seen as people who don't share in the work and think themselves special. That's not what you want.&lt;/p&gt;
&lt;h2&gt;Who to make tea for&lt;/h2&gt;

&lt;p&gt;The rule that varies most is how many people to offer tea to. &lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;In small offices (a handful of people) this is usually everybody in the office. &lt;/li&gt;
&lt;li&gt;In large offices the situation gets more complex: by team? by row of desks? &lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The simplest thing to do is watch and learn. If in doubt, be generous until you've figured out the optimal round size.&lt;/p&gt;
&lt;h2&gt;Remember preferences&lt;/h2&gt;

&lt;p&gt;Most people have a preference on how they take their tea. Remember it. They'll like you more for it.&lt;/p&gt;
&lt;h2&gt;Persistent 'no's&lt;/h2&gt;

&lt;p&gt;You will meet at least one person who always says no to a cup of tea. Ask if they want you to stop asking before you stop offering. The person might want a cup every once in a while or they might just want to feel included.&lt;/p&gt;
&lt;h2&gt;How to make tea&lt;/h2&gt;

&lt;p&gt;There are &lt;a rel="nofollow" href="http://www.google.co.uk/search?q=how+to+make+tea"&gt;plenty of tutorials&lt;/a&gt; out there on how to make tea. The one thing they never mention is to make it too strong instead of too weak. It's easy to fix tea that is too strong and people rarely moan about it. Weak tea, on the other hand, is frowned upon and cannot be fixed.&lt;/p&gt;
&lt;h2&gt;Enjoy!&lt;/h2&gt;

&lt;p&gt;Enjoy making tea for people, no-one likes a grudgingly offered cup of tea.&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/colinhowe?a=v9c8HXc--rk:4fhLbibeLOQ:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/colinhowe?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/colinhowe?a=v9c8HXc--rk:4fhLbibeLOQ:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/colinhowe?i=v9c8HXc--rk:4fhLbibeLOQ:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/colinhowe?a=v9c8HXc--rk:4fhLbibeLOQ:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/colinhowe?i=v9c8HXc--rk:4fhLbibeLOQ:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/colinhowe/~4/v9c8HXc--rk" height="1" width="1"/&gt;</description><pubDate>Sun, 13 Mar 2011 19:32:55 +0000</pubDate><guid isPermaLink="false">http://www.colinhowe.co.uk/2011/mar/13/tea-rounds/</guid><feedburner:origLink>http://www.colinhowe.co.uk/2011/mar/13/tea-rounds/</feedburner:origLink></item><item><title>Goodbye wordpress, hello Mumblr
</title><link>http://feedproxy.google.com/~r/colinhowe/~3/9y1vk8JBaog/</link><description>&lt;p&gt;I've been using a self-hosted Wordpress blog for about two years now. In those two years I've not been very impressed by it. It's easy to extend (sometimes) but it runs so slow and has so much clutter that I felt embarrassed by my blog.&lt;/p&gt;
&lt;p&gt;So, I've moved to &lt;a rel="nofollow" href="https://github.com/hmarr/django-mumblr"&gt;Mumblr&lt;/a&gt; by my colleague &lt;a rel="nofollow" href="http://twitter.com/harrymarr"&gt;Harry Marr&lt;/a&gt;. Mumblr is written in Python and has Mongo as the database. It runs superfast and is easy to edit to have the functionality I need.&lt;/p&gt;
&lt;p&gt;Another big change is that I have moved from my host (&lt;a rel="nofollow" href="http://nearlyfreespeech.net/"&gt;NearlyFreeSpeech&lt;/a&gt;) and on to an Amazon EC2 micro instance. It was really easy to setup and performance has been good so far.&lt;/p&gt;
&lt;p&gt;The end result? A fast blog I can be proud of (barring the design, I'm working on making it neater). &lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/colinhowe?a=9y1vk8JBaog:mrFiNu6uIR8:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/colinhowe?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/colinhowe?a=9y1vk8JBaog:mrFiNu6uIR8:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/colinhowe?i=9y1vk8JBaog:mrFiNu6uIR8:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/colinhowe?a=9y1vk8JBaog:mrFiNu6uIR8:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/colinhowe?i=9y1vk8JBaog:mrFiNu6uIR8:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/colinhowe/~4/9y1vk8JBaog" height="1" width="1"/&gt;</description><pubDate>Tue, 08 Mar 2011 21:47:00 +0000</pubDate><guid isPermaLink="false">http://www.colinhowe.co.uk/2011/mar/08/goodbye-wordpress-hello-mumblr/</guid><feedburner:origLink>http://www.colinhowe.co.uk/2011/mar/08/goodbye-wordpress-hello-mumblr/</feedburner:origLink></item><item><title>MongoDB Performance for more data than memory
</title><link>http://feedproxy.google.com/~r/colinhowe/~3/aXE8CfXs_mA/</link><description>&lt;p&gt;I've recently been having a play around with MongoDB and it's really cool. One of the common messages I see all over is that you should only use it if your dataset fits into memory. I've not yet seen any benchmarks on what happens when it doesn't though. So, here is a benchmark on how MongoDB performs when the data is bigger than the amount of memory available.&lt;/p&gt;
&lt;h3 id="setup"&gt;Setup&lt;/h3&gt;
&lt;p&gt;Mongo server: An EC2 large instance (64 bit) running a Ubuntu 10.10 image from Alestic. Has 7.5gb of memory. Data folder was on the instance and not EBS. &lt;/p&gt;
&lt;p&gt;Mongo client: An EC2 small instance.&lt;/p&gt;
&lt;h3 id="test"&gt;Test&lt;/h3&gt;
&lt;p&gt;The test will involve inserting X documents to MongoDB with the following structure:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;key: n (where n is 0, 1, ... X - 1)
text: 'Mary had a little lamb. ' x 100
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;There will be an index on key to prevent full scans of the data.&lt;/p&gt;
&lt;p&gt;After the insert there will be 30,000 gets with random keys.&lt;/p&gt;
&lt;p&gt;The expectation is that when the data set gets too large to fit in memory the random gets will become very slow. This will be due to MongoDB's memory mapped files no longer fitting in memory and needing to be read from disk. &lt;/p&gt;
&lt;p&gt;When this &lt;i&gt;thrashing&lt;/i&gt; of the disk starts happening it will be interesting to see what happens when a subset of the dataset is read from. To investigate this a further test will be run that:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;99% of the time - reads from a random key chosen from only Y% of the keys&lt;/li&gt;
&lt;li&gt;1% of the time - reads from any key chosen from the entire dataset&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The expectation here is that for small Y the performance will be similar to when the entire dataset is in memory - as the pages that contain the subset of data will be in memory already and not need to read from disk.&lt;/p&gt;
&lt;h3&gt;Results&lt;/h3&gt;

&lt;h4&gt;Basic results&lt;/h4&gt;

&lt;p&gt;A result spreadsheet is available &lt;a rel="nofollow" href="https://spreadsheets.google.com/ccc?key=0At_QEIJCB2GbdEI3d3VBR2hFY1AyVTVNY0N6OGh5dXc&amp;hl=en"&gt;here&lt;/a&gt; (Google Doc).&lt;/p&gt;
&lt;p&gt;Up to 3 million documents the reads were consistent around 17s for 30,000 reads:
&lt;pre&gt;
      Keys  Average time (s)  Memory usage (mb)
    10,000              16.8   forgot to check
   100,000              16.9               547
 1,000,000              18.0              1672
 3,000,000              17.2              4158
10,000,000              74.1              7469 (16.1gb inc. virtual)
&lt;/pre&gt;
Once the dataset got larger than the amount of memory available the read time got slow. It wasn't as slow as it could be in extreme cases as roughly half of the dataset would still have been in memory.&lt;/p&gt;
&lt;p&gt;It's worth noting that at this point inserts started getting slow: 178s for 3 million documents vs 1,102s for 10 million documents (~17k inserts/sec vs ~9k inserts/sec).&lt;/p&gt;
&lt;h4&gt;What about when reading a subset more often?&lt;/h4&gt;

&lt;pre&gt;
Focus (%)  Read 1 (s)  Read 2 (s)  Read 3 (s)
      100        73.1        75.3        73.9
       10        54.3        37.0        29.5
        1        21.1        18.8        18.2
&lt;/pre&gt;

&lt;p&gt;Focus in the above results refers to the %age of the dataset that was chosen for 99% of reads. In this case it was the first Y% of rows to be inserted - meaning that the pages were likely now out of memory by the time we wanted to read them.&lt;/p&gt;
&lt;p&gt;The results show that MongoDB will perform just as fast on a dataset that is too large for memory if a small subset of the data is read from more frequently than the rest.&lt;/p&gt;
&lt;p&gt;It was interesting to see the 10% figure drop over time. I suspect that this figure will get closer to 18s as the number of reads increases - more and more of the pages will be cached by the operating system and not need to be read from disk.&lt;/p&gt;
&lt;h3&gt;Conclusions&lt;/h3&gt;

&lt;p&gt;From doing this it can be seen that the performance of MongoDB can drop by an order of magnitude when the dataset gets too big for memory. However, if the reads are clustered in a subset of the dataset then a large amount of the data will be able to be kept in cache and reads kept quick.&lt;/p&gt;
&lt;p&gt;It's definitely worth noting that it's normal for the performance to drop by an order of magnitude when the database has to start hitting disk. The point of this experiment was to make sure that it was &lt;em&gt;only one&lt;/em&gt; order of magnitude and that if reads were focussed the performance would stay high.&lt;/p&gt;
&lt;h3&gt;Code&lt;/h3&gt;

&lt;p&gt;The code for the benchmark (for improvements and your own testing) is in github: &lt;a rel="nofollow" href="http://github.com/colinhowe/mongo-benchmarks/blob/master/bench.py"&gt;http://github.com/colinhowe/mongo-benchmarks/blob/master/bench.py&lt;/a&gt;&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/colinhowe?a=aXE8CfXs_mA:KfKLbDzs9Pw:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/colinhowe?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/colinhowe?a=aXE8CfXs_mA:KfKLbDzs9Pw:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/colinhowe?i=aXE8CfXs_mA:KfKLbDzs9Pw:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/colinhowe?a=aXE8CfXs_mA:KfKLbDzs9Pw:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/colinhowe?i=aXE8CfXs_mA:KfKLbDzs9Pw:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/colinhowe/~4/aXE8CfXs_mA" height="1" width="1"/&gt;</description><pubDate>Wed, 23 Feb 2011 23:41:40 +0000</pubDate><guid isPermaLink="false">http://www.colinhowe.co.uk/2011/02/23/mongodb-performance-for-data-bigger-than-memor/</guid><feedburner:origLink>http://www.colinhowe.co.uk/2011/02/23/mongodb-performance-for-data-bigger-than-memor/</feedburner:origLink></item><item><title>Getting a slice of live data (MySQL Partial Dump)
</title><link>http://feedproxy.google.com/~r/colinhowe/~3/c-AFQTPlRo4/</link><description>&lt;p&gt;Have you ever needed to get all the data for a single customer from a MySQL database? All their orders, the products they've viewed, their billing preferences, everything.&lt;/p&gt;
&lt;p&gt;I needed to do this for &lt;a rel="nofollow" href="http://hello.conversocial.com"&gt;Conversocial&lt;/a&gt; when debugging a problem a customer was having. More recently, I've needed to do this for a server migration project. To make this easy I've created a tool: &lt;a rel="nofollow" href="http://github.com/colinhowe/mysqlpartialdump"&gt;MySQLPartialDump&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;MySQLPartialDump allows you to describe your database structure using a simple DSL. It will then crawl your database following the relationships you specify and create a dump file that can be imported directly in to MySQL.&lt;/p&gt;
&lt;p&gt;More complex features include:
&lt;ul&gt;
&lt;li&gt;Cleansing of sensitive data - e.g. removing emails&lt;/li&gt;
&lt;li&gt;Creating multiple files for parallel import&lt;/li&gt;
&lt;li&gt;Custom relationships for tables that relate to other tables optionally&lt;/li&gt;
&lt;/ul&gt;&lt;/p&gt;
&lt;p&gt;For full details please see the &lt;a rel="nofollow" href="https://github.com/colinhowe/mysqlpartialdump/blob/master/README.rst"&gt;readme&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;If you find this useful - please let me know :)&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/colinhowe?a=c-AFQTPlRo4:caCkncqcB3k:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/colinhowe?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/colinhowe?a=c-AFQTPlRo4:caCkncqcB3k:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/colinhowe?i=c-AFQTPlRo4:caCkncqcB3k:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/colinhowe?a=c-AFQTPlRo4:caCkncqcB3k:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/colinhowe?i=c-AFQTPlRo4:caCkncqcB3k:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/colinhowe/~4/c-AFQTPlRo4" height="1" width="1"/&gt;</description><pubDate>Sun, 13 Feb 2011 14:37:00 +0000</pubDate><guid isPermaLink="false">http://www.colinhowe.co.uk/2011/02/13/getting-a-slice-of-live-data/</guid><feedburner:origLink>http://www.colinhowe.co.uk/2011/02/13/getting-a-slice-of-live-data/</feedburner:origLink></item><item><title>Celery and Sentry - Recording Errors
</title><link>http://feedproxy.google.com/~r/colinhowe/~3/7XdaJt6QVtw/</link><description>&lt;p&gt;As part of improving our infrastructure for &lt;a rel="nofollow" href="http://hello.conversocial.com"&gt;Conversocial&lt;/a&gt; we wanted to add &lt;a rel="nofollow" href="https://github.com/dcramer/django-sentry"&gt;django-sentry&lt;/a&gt;. This little app groups together similar errors and makes diagnosing problems far easier. It integrates with django seamlessly, but, it needs a bit of work to get celery sending errors to it.&lt;/p&gt;
&lt;p&gt;This is not quite as simple as I first thought. After a bit of experimentation I found the following in our tasks.py worked:&lt;/p&gt;
&lt;pre&gt;&lt;code class="python"&gt;# Hook up sentry to celery's logging
import logging 
from celery.signals import task_failure 
from sentry.client.handlers import SentryHandler

logger = logging.getLogger('task')
logger.addHandler(SentryHandler()) 
def process_failure_signal(exception, traceback, sender, task_id, 
                           signal, args, kwargs, einfo, **kw): 
  exc_info = (type(exception), exception, traceback) 
  logger.error( 
    'Celery job exception: %s(%s)' % (exception.__class__.__name__, exception), 
    exc_info=exc_info, 
    extra={ 
      'data': { 
        'task_id': task_id, 
        'sender': sender, 
        'args': args, 
        'kwargs': kwargs, 
      } 
    } 
  ) 
task_failure.connect(process_failure_signal) 
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;This is based on &lt;a rel="nofollow" href="http://groups.google.com/group/celery-users/browse_thread/thread/95bdffe5a0057ac0#84ad548b971dc851"&gt;some code&lt;/a&gt; from the Celery user group. The main difference is that instead of adding the SentryHandler to the celery logger I define my own logger. I do this because I found numerous issues when trying to add it to the celery logger, including:
&lt;ul&gt;
&lt;li&gt;Double-recording of the errors in Sentry&lt;/li&gt;
&lt;li&gt;The task ID appearing in the message in Sentry - eliminating Sentry's ability to group messages&lt;/li&gt;
&lt;li&gt;Celery's info/warning messages came through to Sentry - we use Splunk for checking our logs so wanted to get just the errors&lt;/li&gt;
&lt;/ul&gt;&lt;/p&gt;
&lt;p&gt;Thought I'd share this nugget for anyone else who tries to get this working and hits problems :)&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/colinhowe?a=7XdaJt6QVtw:P-FQ-Jq77Ec:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/colinhowe?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/colinhowe?a=7XdaJt6QVtw:P-FQ-Jq77Ec:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/colinhowe?i=7XdaJt6QVtw:P-FQ-Jq77Ec:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/colinhowe?a=7XdaJt6QVtw:P-FQ-Jq77Ec:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/colinhowe?i=7XdaJt6QVtw:P-FQ-Jq77Ec:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/colinhowe/~4/7XdaJt6QVtw" height="1" width="1"/&gt;</description><pubDate>Tue, 08 Feb 2011 16:32:00 +0000</pubDate><guid isPermaLink="false">http://www.colinhowe.co.uk/2011/02/08/celery-and-sentry-recording-errors/</guid><feedburner:origLink>http://www.colinhowe.co.uk/2011/02/08/celery-and-sentry-recording-errors/</feedburner:origLink></item></channel></rss>
