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

Why I hate Caching. And the Things I'm Trying to do to Change it.

Why I hate Caching. And the Things I'm Trying to do to Change it.

Ivayr Farah Netto

September 24, 2015
Tweet

More Decks by Ivayr Farah Netto

Other Decks in Technology

Transcript

  1. WHY I HATE CACHING
    AND THE THINGS I'M TRYING TO DO TO
    CHANGE IT
    @nettofarah
    1 / 65

    View Slide

  2. nettofarah
    github.com/nettofarah
    @nettofarah
    http://bit.ly/kashmir-netto
    2 / 65

    View Slide

  3. Senior Product Engineer @ IFTTT
    3 / 65

    View Slide

  4. IFTTT
    Connect the Apps you Love
    4 / 65

    View Slide

  5. IFTTT
    Millions of Users
    Website (desktop & mobile)
    8 Apps (IF & DO)
    Billions of Requests and Transactions
    every day
    5 / 65

    View Slide

  6. Rails doesn't Scale
    Said someone in the Past. For whatever
    reason.
    6 / 65

    View Slide

  7. Scaling Web (Rails) Apps
    You can do it!
    7 / 65

    View Slide

  8. Background Jobs
    8 / 65

    View Slide

  9. With a lot of Processes
    or
    With Threads
    9 / 65

    View Slide

  10. Scaling Web (Rails) Apps
    with
    Rails.cache
    10 / 65

    View Slide

  11. Easy and beautiful at first
    11 / 65

    View Slide

  12. Incredibly tricky after some time
    12 / 65

    View Slide

  13. Caching is like Cocaine
    13 / 65

    View Slide

  14. What is Caching Anyway?
    14 / 65

    View Slide

  15. The 2 Hardest Problems in
    Computer Science
    15 / 65

    View Slide

  16. Cache Invalidation
    and
    Naming Things
    16 / 65

    View Slide

  17. Ok, so you're still not convinced
    17 / 65

    View Slide

  18. Oops, we forgot to remove it from
    the cache
    18 / 65

    View Slide

  19. Just ask them to clear the browser
    cache
    19 / 65

    View Slide

  20. Oh, that number is off because of
    our cache
    20 / 65

    View Slide

  21. No worries, our cache should be
    clear in about 4 hours
    21 / 65

    View Slide

  22. Oh shoot! I forgot to add the line
    that removes it from the cache
    22 / 65

    View Slide

  23. Which leads to frustration...
    23 / 65

    View Slide

  24. 24 / 65

    View Slide

  25. 25 / 65

    View Slide

  26. 26 / 65

    View Slide

  27. class: fake-middle, no-logo, image
    background-image: url(images/cache1.png)
    27 / 65

    View Slide

  28. And Some other design problems
    like
    28 / 65

    View Slide

  29. Cache Invalidation in callback hooks
    29 / 65

    View Slide

  30. Inconsistent TTLs
    30 / 65

    View Slide

  31. Inconsistent Cache Keys
    31 / 65

    View Slide

  32. Nested Caching
    32 / 65

    View Slide

  33. N+1 Queries
    from cache
    33 / 65

    View Slide

  34. Unrealistic Experience
    34 / 65

    View Slide

  35. What happens if your cache box
    dies?
    35 / 65

    View Slide

  36. I'm not saying you should abandon
    caching altogether
    But maybe there is a better way
    36 / 65

    View Slide

  37. What if I told you
    That some times looking up memcached
    could be slower than performing a lookup in
    your database
    37 / 65

    View Slide

  38. Step 0
    Understand your bottlenecks
    38 / 65

    View Slide

  39. Most performance challenges are IO
    bound
    39 / 65

    View Slide

  40. That might not be true sometimes,
    though
    40 / 65

    View Slide

  41. Step 1
    Understand your Database
    41 / 65

    View Slide

  42. EXPLAIN and SLOW LOG
    are your best friends
    42 / 65

    View Slide

  43. Spend some time analyzing your
    indexes
    specially compound ones
    43 / 65

    View Slide

  44. Search Engines (SOLR,
    ElasticSearch)
    Try and understand how indexes are
    constructed. When commit operations are
    performed, analyze your indexes at
    write and read time.
    44 / 65

    View Slide

  45. Redis
    SLOWLOG can save you
    45 / 65

    View Slide

  46. Step 2
    Use the best tool for the job
    46 / 65

    View Slide

  47. Still not convinced?
    47 / 65

    View Slide

  48. How to Cache?
    48 / 65

    View Slide

  49. HTTP Caching
    My favorite kind. If used wisely.
    Headers
    Varnish / Instart Logic
    49 / 65

    View Slide

  50. Template Caching
    IMO, Doesn't sound like a great practice.
    50 / 65

    View Slide

  51. Controller Caching
    Very tricky and when it comes to state.
    51 / 65

    View Slide

  52. Model Caching
    Hard to design
    52 / 65

    View Slide

  53. 53 / 65

    View Slide

  54. class Person
    include Kashmir
    def initialize(name, age)
    @name = name
    @age = age
    end
    representations do
    rep :name
    rep :age
    end
    end
    Person.new('Netto Farah', 27).represent(:name, :age)
    => {:name=>"Netto Farah", :age=>"27"}
    54 / 65

    View Slide

  55. class Recipe < OpenStruct
    include Kashmir
    representations do
    rep(:title)
    rep(:chef)
    end
    end
    class Chef < OpenStruct
    include Kashmir
    representations do
    rep(:name)
    end
    end
    netto = Chef.new(
    name: 'Netto Farah'
    )
    beef_stew = Recipe.new(
    title: 'Beef Stew',
    chef: netto
    )
    beef_stew.represent(
    :title,
    { :chef => :name }
    )
    => {
    :title => "Beef Stew",
    :chef => {
    :name => 'Netto Farah'
    }
    }
    55 / 65

    View Slide

  56. And Many More Examples
    https://github.com/IFTTT/kashmir
    56 / 65

    View Slide

  57. Cache Layers
    57 / 65

    View Slide

  58. 58 / 65

    View Slide

  59. Database Query Planner
    Levarages
    ActiveRecord::Associations::Preloader
    59 / 65

    View Slide

  60. Prevents N+1 Queries
    60 / 65

    View Slide

  61. Chef.all.each do |chef|
    chef.recipes.to_a
    end
    SELECT * FROM chefs
    SELECT "recipes".*
    FROM "recipes" WHERE "recipes"."chef_id" = 1
    SELECT "recipes".*
    FROM "recipes" WHERE "recipes"."chef_id" = 2
    ...
    SELECT "recipes".*
    FROM "recipes" WHERE "recipes"."chef_id" = N
    61 / 65

    View Slide

  62. Chef.all.represent([:recipes])
    SELECT "chefs".* FROM "chefs"
    SELECT "recipes".* FROM "recipes"
    WHERE "recipes"."chef_id" IN (1, 2)
    62 / 65

    View Slide

  63. Expiration
    Trickiest part.
    63 / 65

    View Slide

  64. Advanced Use Cases
    Rest APIs
    GraphQL like endpoints
    64 / 65

    View Slide

  65. Questions?
    65 / 65

    View Slide