scalefail

28
ScaleFail (and MPs’ expenses)

Upload: simon-willison

Post on 07-Nov-2014

1.893 views

Category:

Technology


0 download

DESCRIPTION

A 5 minute presentation for UK ScaleCamp 2009

TRANSCRIPT

Page 1: ScaleFail

ScaleFail(and MPs’ expenses)

Page 2: ScaleFail

Copyright © Steve Bell 2009

Page 3: ScaleFail
Page 4: ScaleFail
Page 5: ScaleFail
Page 6: ScaleFail
Page 7: ScaleFail
Page 8: ScaleFail
Page 9: ScaleFail

How we built it

Page 10: ScaleFail
Page 11: ScaleFail

Crash #1: more Apache children than MySQL connections

Page 12: ScaleFail
Page 13: ScaleFail
Page 14: ScaleFail

unreviewed_count = Page.objects.filter( votes__isnull = True).distinct().count()

Page 15: ScaleFail

SELECT COUNT(DISTINCT `expenses_page`.`id`)FROM `expenses_page` LEFT OUTER JOIN `expenses_vote` ON ( `expenses_page`.`id` = `expenses_vote`.`page_id` ) WHERE `expenses_vote`.`id` IS NULL

Page 16: ScaleFail

unreviewed_count = cache.get('homepage:unreviewed_count')if unreviewed_count is None: unreviewed_count = Page.objects.filter( votes__isnull = True ).distinct().count() cache.set('homepage: unreviewed_count', unreviewed_count, 60)

Page 17: ScaleFail

• With 70,000 pages and a LOT of votes...

• DB takes up 135% of CPU

• Cache the count in memcached...

• DB drops to %35 of CPU

Page 18: ScaleFail

unreviewed_count = Page.objects.filter( votes__isnull = True ).distinct().count()

reviewed_count = Page.objects.filter( votes__isnull = False ).distinct().count()

Page 19: ScaleFail

unreviewed_count = Page.objects.filter( is_reviewed = False ).count()

Page 20: ScaleFail

Migrating to InnoDB on a separate server

Page 21: ScaleFail

ssh mps-live "mysqldump mp_expenses" |sed 's/ENGINE=MyISAM/ENGINE=InnoDB/g' |

sed 's/CHARSET=latin1/CHARSET=utf8/g' |ssh mysql-big "mysql -u root mp_expenses"

Page 22: ScaleFail

“next” button

Page 23: ScaleFail

def next_global(request): # Next unreviewed page from the whole site all_unreviewed_pages = Page.objects.filter( is_reviewed = False ).order_by('?') if all_unreviewed_pages: return Redirect( all_unreviewed_pages[0].get_absolute_url() ) else: return HttpResponse( 'All pages have been reviewed!' )

Page 24: ScaleFail

def next_global(request): # Next unreviewed page from the whole site all_unreviewed_pages = Page.objects.filter( is_reviewed = False ).order_by('?') if all_unreviewed_pages: return Redirect( all_unreviewed_pages[0].get_absolute_url() ) else: return HttpResponse( 'All pages have been reviewed!' )

Page 25: ScaleFail

import random

def next_global_from_cache(request): page_ids = cache.get('unreviewed_page_ids') if page_ids: return Redirect( '/page/%s/' % random.choice(page_ids) ) else: return next_global(request)

Page 26: ScaleFail

Next time, I’ll use redis

Page 27: ScaleFail

Read-only mode saved our bacon

Page 28: ScaleFail

Copyright © Martin Rowson 2008