This is a presentation I gave at Hungry Academy on making a Rails App fast & scalable. Covers the Front and backend and talks about speed and throughput optimizations.
FASTAdjunct Professor: Richard Schneeman@schneems
View Slide
Hello
$ whoamischneems
MechanicalEngineer
ICode
Sextant Gem
Wicked‘‘Gem
AdjunctProfessor
Good NewsEveryone!
I <3 Tacos
Speed VsThroughput
Speed
Throughput
Both AreImportant
2 commonPatterns
Optimize &Cache forspeed
Optimization:Search forslow
Optimization:Make itfast
Caching:Search forexpensive
Caching:Make itcheap
Addcapacity forthroughput
Speedhelpsthroughput
Speed First
Client SidevsServer Side
Page Load Cycle}Server Side
Back EndSpeed
Measure
PotentialCauses ofslow
InefficientCode
Slow IO(database)
Maybe it’sthelanguage
Tweak GC
How do wefind ourproblem?
Look forN+1Queries
Use Logs
Problem@products = Product.all# ...<% @products.each do |product| %><%= product.name %>$<%= product.price %><%= product.user.name %>...
Problem
Fix WithEagerLoading
Solved@products = Product.includes(:user).all# ...<% @products.each do |product| %><%= product.name %>$<%= product.price %><%= product.user.name %>...
Solved
Look forQueries notUsing anindex
config/production.rbconfig.active_record.auto_explain_threshold_in_seconds =1
AddIndexes
Use AMonitoringSoftware
New Relic
Scoutetc.
CacheExpensiveQueries
Expensive QueryBenchmark.measure doUser.some_expensive_queryend.real=> 20s
UseMemcache&Rails.cache
Cache QueryBenchmark.measure doRails.cache.fetch(“cache_key”) doUser.some_expensive_queryendend.real=> 20.5sSlower first time
Cache QueryBenchmark.measure doRails.cache.fetch(“cache_key”) doUser.some_expensive_queryendend.real=> 0.00001sCrazy fast after that
Namingthings &cacheinvalidation
MethodCacheablegem
Cache QueryUser.cache.some_expensive_query=> 20.5sSlower first time
Cache QueryUser.cache.some_expensive_query=> 0.00001sCrazy fast after that
Compounding Trafficwrites on. Wikipedia
Compounding Trafficedits on. Wikipedia
ViewCaching
Neverworkedwell for me
ViewFragmentCaching
NestedViewFragmentCaching
cache_digests gem
Split upWeb/Workers/Datastores
Web RunsRuby code& handlesrequests
Datastoresrunseparately
Workershandle nonrequestprocessing
Workers:Run resque,send email,etc.
I ran out ofcapacity,now what?
Scale UP
Scale OUT
Scaling upis Easy butlimited
Scaling outis hard butunlimited
EphemeralWebMachines
Heroku$ heroku ps:scale web=4
Amazon (AWS)Provision instances & use chef or othertools to install proper software, and thenconnect to a load balancer
Code Code Code CodeCode
Don’t storestate onserver
Store indatabase
Session
S3 etc.
Workers:Headlessdatacrunchers
Workersrun:Resque
How do wescale datastorage?
Master DBSlave DB Slave DB Slave DB Slave DBWriteCopyReadMaster/Slave
Users inUSAReadShardingWriteUsers inEuropeUsers inAsiaUsers inAfrica
cannot joinagainstshardeddata
Facebookshardsmysql
Instagramshardspostgresql
postgresqlbetter thanmysqlIMHO
NoSQL
Not a magicbullet
Key Value Example> redis = Redis.new> redis.set(“foo”, “bar”)
Key Value Example> redis = Redis.new> redis.set(“foo”, “bar”)> redis.get(“foo”)
Key Value Example> redis = Redis.new> redis.set(“foo”, “bar”)> redis.get(“foo”)=> “bar”
Every datastorewill punch you inthe face“-Adam Keys
Key/ValueStores
Shard onKey
ConsistentHashing
Memcache DistributedBCA
Memcache DistributedB CAEasily add more nodesD
CAPTheorem
Pick Two:ConsistencyAvailabilityPartition-Tolerance
Riak DistributedB CAEventual ConsistencyDData InCopied ToExtraNodes ...Eventually
RIAK hasconfigurableCAP
Client SideSpeed
Page Load Cycle}Client Side
Front EndAssets
LoadingAssets:Slow
DecreaseSize
GZIP
CDN
Speed =Distance/Time
DistanceMatters
ShorterDistance =faster
Your ServerUser
What ifWe could...
CDNContentDistributionNetwork
CDNCDNCDN
CDN ServesImagesCSSJavascript
AkamaiCloudfront
config/production.rbconfig.action_controller.asset_host =ENV["cloudfront_url"]
BrowserCaching
Load on:FirstRequest
Store it
Use localcopy onfuturerequests
ExpiresHeaders
config/production.rbconfig.static_cache_control ="public, max-age=2592000"
Wait, whathappens ifwe makechanges?
Turn onRails Assetfingerprints
config/production.rbconfig.assets.digest = true
HASH a fileto take it’s“fingerprint”
MD5 is analgorithm tofingerprintfiles
headers.css908e25f4bf641868d8683022a5b62f54Run MD5 on this:Produces this:
When filechanges, sodoesfingerprint
headers.cssheaders-908e25f4bf641868d8683022a5b62f54.cssFile w/o fingerprintFile with fingerprint:
Measure toOptimize
Use YSlow
CompressAssets
ServeUsing CDN
AsyncScriptLoading
<br/>
deferguaranteesorder
defer has IEsupport
Don’t blockpageparsing
MeasureEverything
Scale outwith moremachines
Speed up yourdatastore or addcaching
(jobs.heroku.com)
Questions?