Upgrade to Pro — share decks privately, control downloads, hide ads and more …

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 full-size slide

  2. $ whoami
    schneems

    View full-size slide

  3. Mechanical
    Engineer

    View full-size slide

  4. Wicked


    Gem

    View full-size slide

  5. Adjunct
    Professor

    View full-size slide

  6. Good News
    Everyone!

    View full-size slide

  7. Speed Vs
    Throughput

    View full-size slide

  8. Both Are
    Important

    View full-size slide

  9. 2 common
    Patterns

    View full-size slide

  10. Optimize &
    Cache for
    speed

    View full-size slide

  11. Optimization:
    Search for
    slow

    View full-size slide

  12. Optimization:
    Make it
    fast

    View full-size slide

  13. Caching:
    Search for
    expensive

    View full-size slide

  14. Caching:
    Make it
    cheap

    View full-size slide

  15. Add
    capacity for
    throughput

    View full-size slide

  16. Speed
    helps
    throughput

    View full-size slide

  17. Client Side
    vs
    Server Side

    View full-size slide

  18. Page Load Cycle
    }
    Server Side

    View full-size slide

  19. Back End
    Speed

    View full-size slide

  20. Potential
    Causes of
    slow

    View full-size slide

  21. Inefficient
    Code

    View full-size slide

  22. Slow IO
    (database)

    View full-size slide

  23. Maybe it’s
    the
    language

    View full-size slide

  24. How do we
    find our
    problem?

    View full-size slide

  25. Look for
    N+1
    Queries

    View full-size slide

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

    View full-size slide

  27. Fix With
    Eager
    Loading

    View full-size slide

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

    View full-size slide

  29. Look for
    Queries not
    Using an
    index

    View full-size slide

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

    View full-size slide

  31. Use A
    Monitoring
    Software

    View full-size slide

  32. Cache
    Expensive
    Queries

    View full-size slide

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

    View full-size slide

  34. Use
    Memcache
    &
    Rails.cache

    View full-size slide

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

    View full-size slide

  36. 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 full-size slide

  37. Naming
    things &
    cache
    invalidation

    View full-size slide

  38. Method
    Cacheable
    gem

    View full-size slide

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

    View full-size slide

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

    View full-size slide

  41. Compounding Traffic
    writes on. Wikipedia

    View full-size slide

  42. Compounding Traffic
    edits on. Wikipedia

    View full-size slide

  43. Never
    worked
    well for me

    View full-size slide

  44. View
    Fragment
    Caching

    View full-size slide

  45. Nested
    View
    Fragment
    Caching

    View full-size slide

  46. cache_digests gem

    View full-size slide

  47. Split up
    Web/
    Workers/
    Datastores

    View full-size slide

  48. Web Runs
    Ruby code
    & handles
    requests

    View full-size slide

  49. Datastores
    run
    separately

    View full-size slide

  50. Workers
    handle non
    request
    processing

    View full-size slide

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

    View full-size slide

  52. I ran out of
    capacity,
    now what?

    View full-size slide

  53. Scaling up
    is Easy but
    limited

    View full-size slide

  54. Scaling out
    is hard but
    unlimited

    View full-size slide

  55. Ephemeral
    Web
    Machines

    View full-size slide

  56. Heroku
    $ heroku ps:scale web=4

    View full-size slide

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

    View full-size slide

  58. Code Code Code Code
    Code

    View full-size slide

  59. Don’t store
    state on
    server

    View full-size slide

  60. Store in
    database

    View full-size slide

  61. Workers:
    Headless
    data
    crunchers

    View full-size slide

  62. Workers
    run:
    Resque

    View full-size slide

  63. How do we
    scale data
    storage?

    View full-size slide

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

    View full-size slide

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

    View full-size slide

  66. cannot join
    against
    sharded
    data

    View full-size slide

  67. Facebook
    shards
    mysql

    View full-size slide

  68. Instagram
    shards
    postgresql

    View full-size slide

  69. postgresql
    better than
    mysql
    IMHO

    View full-size slide

  70. Not a magic
    bullet

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

  74. Every datastore
    will punch you in
    the face

    -Adam Keys

    View full-size slide

  75. Key/Value
    Stores

    View full-size slide

  76. Consistent
    Hashing

    View full-size slide

  77. Memcache Distributed
    B
    C
    A

    View full-size slide

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

    View full-size slide

  79. Pick Two:
    Consistency
    Availability
    Partition-
    Tolerance

    View full-size slide

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

    View full-size slide

  81. RIAK has
    configurable
    CAP

    View full-size slide

  82. Client Side
    Speed

    View full-size slide

  83. Page Load Cycle
    }
    Client Side

    View full-size slide

  84. Front End
    Assets

    View full-size slide

  85. Loading
    Assets:
    Slow

    View full-size slide

  86. Decrease
    Size

    View full-size slide

  87. Speed =
    Distance/
    Time

    View full-size slide

  88. Distance
    Matters

    View full-size slide

  89. Shorter
    Distance =
    faster

    View full-size slide

  90. Your Server
    User

    View full-size slide

  91. What if
    We could...

    View full-size slide

  92. CDN
    Content
    Distribution
    Network

    View full-size slide

  93. CDN Serves
    Images
    CSS
    Javascript

    View full-size slide

  94. Akamai
    Cloudfront

    View full-size slide

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

    View full-size slide

  96. Browser
    Caching

    View full-size slide

  97. Load on:
    First
    Request

    View full-size slide

  98. Use local
    copy on
    future
    requests

    View full-size slide

  99. Expires
    Headers

    View full-size slide

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

    View full-size slide

  101. Wait, what
    happens if
    we make
    changes?

    View full-size slide

  102. Turn on
    Rails Asset
    fingerprints

    View full-size slide

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

    View full-size slide

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

    View full-size slide

  105. MD5 is an
    algorithm to
    fingerprint
    files

    View full-size slide

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

    View full-size slide

  107. When file
    changes, so
    does
    fingerprint

    View full-size slide

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

    View full-size slide

  109. Measure to
    Optimize

    View full-size slide

  110. Compress
    Assets

    View full-size slide

  111. Serve
    Using CDN

    View full-size slide

  112. Async
    Script
    Loading

    View full-size slide

  113. defer
    guarantees
    order

    View full-size slide

  114. defer has IE
    support

    View full-size slide

  115. Don’t block
    page
    parsing

    View full-size slide

  116. Measure
    Everything

    View full-size slide

  117. Scale out
    with more
    machines

    View full-size slide

  118. Speed up your
    datastore or add
    caching

    View full-size slide

  119. (jobs.heroku.com)

    View full-size slide