$30 off During Our Annual Pro Sale. View Details »

Fast & Scalable

Fast & Scalable

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.

Richard Schneeman

July 16, 2012
Tweet

More Decks by Richard Schneeman

Other Decks in Education

Transcript

  1. FAST
    Adjunct Professor: Richard Schneeman
    @schneems

    View Slide

  2. Hello

    View Slide

  3. $ whoami
    schneems

    View Slide

  4. Mechanical
    Engineer

    View Slide

  5. I
    Code

    View Slide

  6. Sextant Gem

    View Slide

  7. Wicked


    Gem

    View Slide

  8. View Slide

  9. Adjunct
    Professor

    View Slide

  10. Good News
    Everyone!

    View Slide

  11. I <3 Tacos

    View Slide

  12. Speed Vs
    Throughput

    View Slide

  13. Speed

    View Slide

  14. Throughput

    View Slide

  15. Both Are
    Important

    View Slide

  16. 2 common
    Patterns

    View Slide

  17. Optimize &
    Cache for
    speed

    View Slide

  18. Optimization:
    Search for
    slow

    View Slide

  19. Optimization:
    Make it
    fast

    View Slide

  20. Caching:
    Search for
    expensive

    View Slide

  21. Caching:
    Make it
    cheap

    View Slide

  22. Add
    capacity for
    throughput

    View Slide

  23. Speed
    helps
    throughput

    View Slide

  24. Speed First

    View Slide

  25. Client Side
    vs
    Server Side

    View Slide

  26. Page Load Cycle
    }
    Server Side

    View Slide

  27. Back End
    Speed

    View Slide

  28. Measure

    View Slide

  29. Potential
    Causes of
    slow

    View Slide

  30. Inefficient
    Code

    View Slide

  31. Slow IO
    (database)

    View Slide

  32. Maybe it’s
    the
    language

    View Slide

  33. Tweak GC

    View Slide

  34. How do we
    find our
    problem?

    View Slide

  35. Look for
    N+1
    Queries

    View Slide

  36. Use Logs

    View Slide

  37. Problem
    @products = Product.all
    # ...
    <% @products.each do |product| %>
    <%= product.name %>
    $<%= product.price %>
    <%= product.user.name %>
    ...

    View Slide

  38. Problem

    View Slide

  39. Fix With
    Eager
    Loading

    View Slide

  40. Solved
    @products = Product.includes(:user).all
    # ...
    <% @products.each do |product| %>
    <%= product.name %>
    $<%= product.price %>
    <%= product.user.name %>
    ...

    View Slide

  41. Solved

    View Slide

  42. Look for
    Queries not
    Using an
    index

    View Slide

  43. Use Logs

    View Slide

  44. config/production.rb
    config.
    active_record.
    auto_explain_threshold_in_seconds =
    1

    View Slide

  45. Add
    Indexes

    View Slide

  46. Use A
    Monitoring
    Software

    View Slide

  47. New Relic

    View Slide

  48. Scout
    etc.

    View Slide

  49. Cache
    Expensive
    Queries

    View Slide

  50. Expensive Query
    Benchmark.measure do
    User.some_expensive_query
    end.real
    => 20s

    View Slide

  51. Use
    Memcache
    &
    Rails.cache

    View Slide

  52. Cache Query
    Benchmark.measure do
    Rails.cache.fetch(“cache_key”) do
    User.some_expensive_query
    end
    end.real
    => 20.5s
    Slower first time

    View Slide

  53. Cache Query
    Benchmark.measure do
    Rails.cache.fetch(“cache_key”) do
    User.some_expensive_query
    end
    end.real
    => 0.00001s
    Crazy fast after that

    View Slide

  54. Naming
    things &
    cache
    invalidation

    View Slide

  55. Method
    Cacheable
    gem

    View Slide

  56. Cache Query
    User.cache.some_expensive_query
    => 20.5s
    Slower first time

    View Slide

  57. Cache Query
    User.cache.some_expensive_query
    => 0.00001s
    Crazy fast after that

    View Slide

  58. Throughput

    View Slide

  59. Compounding Traffic
    writes on. Wikipedia

    View Slide

  60. Compounding Traffic
    edits on. Wikipedia

    View Slide

  61. View
    Caching

    View Slide

  62. Never
    worked
    well for me

    View Slide

  63. View
    Fragment
    Caching

    View Slide

  64. Nested
    View
    Fragment
    Caching

    View Slide

  65. cache_digests gem

    View Slide

  66. Split up
    Web/
    Workers/
    Datastores

    View Slide

  67. Web Runs
    Ruby code
    & handles
    requests

    View Slide

  68. Datastores
    run
    separately

    View Slide

  69. Workers
    handle non
    request
    processing

    View Slide

  70. Workers:
    Run resque,
    send email,
    etc.

    View Slide

  71. I ran out of
    capacity,
    now what?

    View Slide

  72. Scale UP

    View Slide

  73. View Slide

  74. Scale OUT

    View Slide

  75. View Slide

  76. Scaling up
    is Easy but
    limited

    View Slide

  77. Scaling out
    is hard but
    unlimited

    View Slide

  78. Ephemeral
    Web
    Machines

    View Slide

  79. Heroku
    $ heroku ps:scale web=4

    View Slide

  80. Amazon (AWS)
    Provision instances & use chef or other
    tools to install proper software, and then
    connect to a load balancer

    View Slide

  81. Code Code Code Code
    Code

    View Slide

  82. Don’t store
    state on
    server

    View Slide

  83. Store in
    database

    View Slide

  84. Session

    View Slide

  85. S3 etc.

    View Slide

  86. Workers:
    Headless
    data
    crunchers

    View Slide

  87. Workers
    run:
    Resque

    View Slide

  88. How do we
    scale data
    storage?

    View Slide

  89. Master DB
    Slave DB Slave DB Slave DB Slave DB
    Write
    Copy
    Read
    Master/Slave

    View Slide

  90. Users in
    USA
    Read
    Sharding
    Write
    Users in
    Europe
    Users in
    Asia
    Users in
    Africa

    View Slide

  91. cannot join
    against
    sharded
    data

    View Slide

  92. Facebook
    shards
    mysql

    View Slide

  93. Instagram
    shards
    postgresql

    View Slide

  94. postgresql
    better than
    mysql
    IMHO

    View Slide

  95. NoSQL

    View Slide

  96. Not a magic
    bullet

    View Slide

  97. Key Value Example
    > redis = Redis.new
    > redis.set(“foo”, “bar”)

    View Slide

  98. Key Value Example
    > redis = Redis.new
    > redis.set(“foo”, “bar”)
    > redis.get(“foo”)

    View Slide

  99. Key Value Example
    > redis = Redis.new
    > redis.set(“foo”, “bar”)
    > redis.get(“foo”)
    => “bar”

    View Slide

  100. Every datastore
    will punch you in
    the face

    -Adam Keys

    View Slide

  101. Key/Value
    Stores

    View Slide

  102. Shard on
    Key

    View Slide

  103. Consistent
    Hashing

    View Slide

  104. Memcache Distributed
    B
    C
    A

    View Slide

  105. Memcache Distributed
    B C
    A
    Easily add more nodes
    D

    View Slide

  106. CAP
    Theorem

    View Slide

  107. Pick Two:
    Consistency
    Availability
    Partition-
    Tolerance

    View Slide

  108. Riak Distributed
    B C
    A
    Eventual Consistency
    D
    Data In
    Copied To
    Extra
    Nodes ...
    Eventually

    View Slide

  109. RIAK has
    configurable
    CAP

    View Slide

  110. Client Side
    Speed

    View Slide

  111. Page Load Cycle
    }
    Client Side

    View Slide

  112. Front End
    Assets

    View Slide

  113. Loading
    Assets:
    Slow

    View Slide

  114. Decrease
    Size

    View Slide

  115. GZIP

    View Slide

  116. CDN

    View Slide

  117. Speed =
    Distance/
    Time

    View Slide

  118. Distance
    Matters

    View Slide

  119. Shorter
    Distance =
    faster

    View Slide

  120. Your Server
    User

    View Slide

  121. View Slide

  122. What if
    We could...

    View Slide

  123. View Slide

  124. CDN

    View Slide

  125. CDN
    Content
    Distribution
    Network

    View Slide

  126. CDN
    CDN
    CDN

    View Slide

  127. CDN Serves
    Images
    CSS
    Javascript

    View Slide

  128. Akamai
    Cloudfront

    View Slide

  129. config/production.rb
    config.action_controller.asset_host =
    ENV["cloudfront_url"]

    View Slide

  130. Browser
    Caching

    View Slide

  131. Load on:
    First
    Request

    View Slide

  132. Store it

    View Slide

  133. Use local
    copy on
    future
    requests

    View Slide

  134. Expires
    Headers

    View Slide

  135. config/production.rb
    config.static_cache_control =
    "public, max-age=2592000"

    View Slide

  136. Wait, what
    happens if
    we make
    changes?

    View Slide

  137. Turn on
    Rails Asset
    fingerprints

    View Slide

  138. config/production.rb
    config.assets.digest = true

    View Slide

  139. HASH a file
    to take it’s
    “fingerprint”

    View Slide

  140. MD5 is an
    algorithm to
    fingerprint
    files

    View Slide

  141. headers.css
    908e25f4bf641868d86
    83022a5b62f54
    Run MD5 on this:
    Produces this:

    View Slide

  142. When file
    changes, so
    does
    fingerprint

    View Slide

  143. headers.css
    headers-
    908e25f4bf641868d86
    83022a5b62f54.css
    File w/o fingerprint
    File with fingerprint:

    View Slide

  144. Measure to
    Optimize

    View Slide

  145. Use YSlow

    View Slide

  146. View Slide

  147. Compress
    Assets

    View Slide

  148. Serve
    Using CDN

    View Slide

  149. Async
    Script
    Loading

    View Slide

  150. <br/>

    View Slide

  151. <br/>

    View Slide

  152. defer
    guarantees
    order

    View Slide

  153. defer has IE
    support

    View Slide

  154. Don’t block
    page
    parsing

    View Slide

  155. Measure
    Everything

    View Slide

  156. Scale out
    with more
    machines

    View Slide

  157. Speed up your
    datastore or add
    caching

    View Slide

  158. (jobs.heroku.com)

    View Slide

  159. View Slide

  160. Questions?

    View Slide