"Stupid Easy" Scaling Tweaks and Settings AKA Scaling for the Lazy
I'm Lazy (and proud of it)
The Benefits of "Lazy" Efficiency is king Dislike repetition Avoid spending a lot of time on things
A Lazy Approach to Scaling Changes shouldn't take more than ~1h Change once, and "forget" Skims the surface, but goes the distance
The Design
Identify the problems Single User Some Users Over 24 hours 1-23 hours tweets() 10s Real Time Everyone
Dealing with Blocks A "block" is anything which holds up processing (usually I/O operations): Network connections 3rd party APIs Disk reads/writes Some blocks bigger than others (i.e. DB write is a bigger 'block' than a read) For big 'blocks', push off to Celery
Async vs Sync Sync Processes in order Multiple processes running Default Behavior Long I/O blocks = Bad (use Celery) This is approach we're addressing Async Usually green threading Spawns new thread on I/O block Fewer processes running Blocks on Native C libraries Special case use
The Server
Cache EVERYTHING Use a cache for everything you can A lot of this can be done 'auto-magically' 1. apt-get install memcached libmemcached-dev 2. pip install pylibmc 3. Configure with ketama CACHES = { 'default': { 'BACKEND': 'django.core.cache.backends.memcached.pylibmccache', 'LOCATION': '127.0.0.1:11211', 'TIMEOUT': 3600, 'VERSION': 1, 'OPTIONS': { # Maps to pylibmc "behaviors" 'tcp_nodelay': True, 'ketama': True }, }, } [1] Set up Cache
Cache Sessions Django default of session in DB = unnecessary DB reads/writes SESSION_ENGINE = "django.contrib.sessions.backends.cache" SESSION_CACHE_ALIAS = "sessions" (new in 1.5) Concerned about session persistence? Maybe use redis as a session backend instead of Django docs "...backends.cached_db" Django docs for session backends
Cached Template Loader Everyone knows Django Templates are slow, right? Slower than Jinja2, etc. Bollocks. This can cut down your django templates to 1/10th the rendering time: Use the cached template loader: TEMPLATE_LOADERS = (('django.template.loaders.cached.loader', ( 'django.template.loaders.filesystem.loader', 'django.template.loaders.app_directories.loader', )), )
Cache Pages Add Django's Cache Middleware to automatically cache the whole page served. I suggest adding the setting: CACHE_MIDDLEWARE_ANONYMOUS_ONLY = True
Upstream Cache Even better - let's not touch Django at all! Use an upstream cache to serve static files at a breakneck speed. Use @ensure_csrf_cookie to prevent caching in upstream uwsgi_cache_key $scheme:site$request_uri:$request_method; uwsgi_cache_bypass $cookie_sessionid; # Don't serve cache if session uwsgi_cache_bypass $cookie_csrftoken; # Don't serve cache if session uwsgi_no_cache $cookie_sessionid; # Don't cache response w/ session uwsgi_no_cache $cookie_csrftoken; # Don't cache response w/ session Gist of full configuration settings for uwsgi + nginx upstream cache
Database
Johnny Cache Automagically caches DB queries, and invalidates all queries dependent on a table if the table has a write. Use the blacklist for tables written more often than read. Set "'TIMEOUT': 0" in settings for infinite cache Beware external writes to DB!!!!!
Connection Pooling Source
The Client
Use a CDN Speeds up requests, reduces server load. Also, use max 'Expires' (and just version updates)
PJAX Hi, Josh! You have 22 notifications. Most recent message: "I forgot the graph..." Let's Only Content Update This All the social media updates you can't do without.
How PJAX works jquery plugin that uses a "new" technique to change only part of the page, but update the history and URL. If browser doesn't support it, loads normally. Otherwise, adds a "PJAX" header to request. Just add html attributes to links and containers, and then run the plugin. (Needs server-side integration though)
django-pjax Adds decorators and class mixins (for classbased-views) to specify different templates if the PJAX header exists. Easiest way: @pjaxtend() --- creates 'parent' context variable which defaults to base.html if not PJAX, or pjax.html if PJAX In template: {% extends parent %} I suggest using my fork until pull request merged: My fork
Final Thoughts You could add all these features in a single weekend. If you're not using any of them at all, you'll see gains of about 20% to 10,000% of what you have now (depending on the specific case) These don't work in ALL cases, but do work in MOST cases.
Oh, and btw... So for this I polled you guys on what talks you'd like to see. Here were the results:
Q&A Josh Lovison jlovison@gmail.com