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

Aila!.. Caching in Rails.

Aila!.. Caching in Rails.

I will be speaking on why caching is important and how we can scale up the Rails app using different caching strategies like page, action & fragment. I will also speak on how we can improve the performance in the simplest way by using instance variable caching for expensive command’s result.

Santosh Wadghule

August 06, 2016
Tweet

Other Decks in Programming

Transcript

  1. RAILS
    AILA!.. CACHING IN

    View Slide

  2. Santosh Wadghule
    Freelancer
    http://mechanicles.com
    github.com/mechanicles
    twitter.com/mechanicles

    View Slide

  3. Food Lover

    View Slide

  4. Stay under 250ms to feel your app “fast”
    Stay under 1000ms to keep your users

    View Slide

  5. Caching

    View Slide

  6. Caching
    It is a process of storing data so future requests
    for that data can be served faster

    View Slide

  7. Caching
    It is a process of storing data so future requests
    for that data can be served faster
    Improves user experience

    View Slide

  8. Caching
    It is a process of storing data so future requests
    for that data can be served faster
    Saves a lot money and other hardwares
    Improves user experience

    View Slide

  9. Caching
    It is a process of storing data so future requests
    for that data can be served faster
    Saves a lot money and other hardwares
    Keeps you and your users happy :)
    Improves user experience

    View Slide

  10. Main Resource
    Cache Memory
    Client
    A A
    A
    A
    A
    Main Resource
    Cache Memory
    Requester
    A
    A
    A A
    A
    First
    Request
    Future
    Requests

    View Slide

  11. Web Cache

    View Slide

  12. BROWSER
    PROXY
    CDN
    RAILS APP
    REVERSE PROXY
    Components which
    support Web Cache

    View Slide

  13. Stop!
    Caching is not always a good way to make your app fast

    View Slide

  14. Think!
    Why your app is slow?

    View Slide

  15. CACHING IN RAILS
    BEFORE CACHING: FIRST BOOST YOUR RAILS APP
    ▸ Avoid n+1 queries.
    ▸ Index on foreign keys.
    ▸ Instance variable caching
    ▸ Load only required JavaScript files in web pages.
    ▸ Minify and ZIP stylesheets and JavaScript files.

    View Slide

  16. There are only two hard
    things in Computer Science:
    cache invalidation & naming
    things
    Phil Karlton
    CACHING IN RAILS

    View Slide

  17. Caching in Rails

    View Slide

  18. CACHING IN RAILS
    CACHING IN RAILS
    ▸ Basic Caching (Page, Action & Fragment Caching)
    ▸ Low Level Caching
    ▸ Alternative cache stores
    ▸ SQL Caching
    ▸ Conditional GET support

    View Slide

  19. Setting up caching in Rails

    View Slide

  20. CACHING IN RAILS
    SETTING UP CACHING IN RAILS
    ▸ `config.action_controller.perform_caching =
    true` for Rails 4
    ▸ Run `rake dev:cache` for Rails 5

    View Slide

  21. Page Caching

    View Slide

  22. CACHIN IN RAILS.
    PAGE CACHING
    ▸ Needs `actionpack-page_caching` gem

    View Slide

  23. APACHE
    PUMA
    CLIENT http:/example.com/posts/
    Without Caching

    View Slide

  24. APACHE
    PUMA
    CLIENT http:/example.com/posts/
    /public/posts.html
    Page Caching

    View Slide

  25. APACHE
    PUMA
    CLIENT http:/example.com/posts/
    /public/posts.html
    Page Caching

    View Slide

  26. View Slide

  27. class PagesController < ApplicationController
    def contact_us
    @contact = Contact.new
    end
    def about
    render
    end
    end

    View Slide

  28. ABOUT US PAGE RESPONSE WITHOUT CACHING
    Views have taken half a second

    View Slide

  29. class PagesController < ApplicationController
    caches_page :about
    def contact_us
    @contact = Contact.new
    end
    def about
    render
    end
    end

    View Slide

  30. ABOUT US PAGE RESPONSE WITHOUT CACHING
    Cached entire file in file system

    View Slide

  31. Response got served by web server for ‘About us’ page

    View Slide

  32. CACHING IN RAILS
    INVALIDATING PAGE CACHING
    ▸ Non model pages need to be deleted manually.
    ▸ expire_page helper method
    ▸ expire_page action: “index”
    ▸ expire_page action: “show”, id: @post
    ▸ ‘Sweepers’ also helps to invalidate the cache

    View Slide

  33. Action Caching

    View Slide

  34. CACHIN IN RAILS
    ACTION CACHING
    ▸ Needs `actionpack-action_caching` gem
    ▸ It uses fragment caching internally

    View Slide

  35. View Slide

  36. class HomeController < ApplicationController
    before_action :log_it
    def index
    if request.format.json?
    render json: { text: "Home Page" }
    else
    render
    end
    end
    private
    def log_it
    logger.info("")
    logger.info(">>>> BEFORE ACTION CODE <<<<")
    logger.info("")
    end
    end

    View Slide

  37. Home page response without caching

    View Slide

  38. class HomeController < ApplicationController
    before_action :log_it
    caches_action :index, if: Proc.new { !request.format.json? },
    expires_in: 30.seconds
    def index
    if request.format.json?
    render json: { text: "Home Page" }
    else
    render
    end
    end
    private
    def log_it
    logger.info("")
    logger.info(">>>> BEFORE ACTION CODE <<<<")
    logger.info("")
    end
    end

    View Slide

  39. Home page response while caching
    Fragment: views/localhost:3000/home/index
    Cache key : ‘views’ + ‘localhost:3000/home/index’

    View Slide

  40. Home page response with caching

    View Slide

  41. CACHING IN RAILS
    INVALIDATING ACTION CACHING
    ▸ expire_action helper method

    View Slide

  42. Fragment Caching

    View Slide

  43. View Slide




  44. Name
    Amount



    <% @products.each do |product| %>
    <% cache(product) do %>


    <%= product.name %>


    <%= product.amount %>


    <%= link_to 'Edit ', edit_product_path(product) %>


    <% end %>
    <% end %>


    View Slide

  45. views/products/1-20160731074448006461000/2534a326647f6ea1c765472157da361f
    Cache Key
    timestamp (updated_at)
    product-id Cache Digest for dashboard page
    ‘cache(product) #=> product.cache_key’

    View Slide

  46. CACHIN IN RAILS.
    FRAGMENT CACHING
    ▸ `expire_fragment(‘name_of_cache’)` is also
    responsible to expire the fragment.
    ▸ `cache_if` & `cache_unless` for conditional fragment
    caching.

    View Slide

  47. Russian Doll Caching

    View Slide

  48. # product view template
    <% cache product do %>
    name: <%= product.name %>
    <%= render product.comments %>
    <% end %>
    # comment view template
    <% cache comment do %>
    <%= render comment %>
    <% end %>

    View Slide

  49. class Product < ActiveRecord::Base
    has_many :comments
    end
    class Comment < ActiveRecord::Base
    belongs_to :product, touch: true
    end

    View Slide

  50. Low Level Caching

    View Slide

  51. CACHIN IN RAILS.
    LOW LEVEL CACHING
    ▸ Uses `Rails.cache`API.

    View Slide

  52. App is damn slow when we fire request to Spark service to get the spark
    listings and we also sometimes get Spark API limit error.
    Issue

    View Slide

  53. class Listing < ApplicationRecord
    LIMIT = 20
    def self.spark_listings(page)
    SparkApi::ListingCart.listings(pagination: 1, page: page, limit: LIMIT)
    end
    end

    View Slide

  54. class Listing < ApplicationRecord
    LIMIT = 20
    def self.spark_listings(page)
    Rails.cache.fetch("spark_listing/page=#{page}", expires_in: 24.hours) do
    SparkApi::ListingCart.listings(pagination: 1, page: page, limit: LIMIT)
    end
    end
    end

    View Slide

  55. class Listing < ApplicationRecord
    LIMIT = 20
    def self.spark_listings(page)
    Rails.cache.fetch("spark_listing/page=#{page}", expires_in: 12.hours) do
    SparkApi::ListingCart.listings(pagination: 1, page: page, limit: LIMIT)
    end
    end
    end
    class Listing < ApplicationRecord
    LIMIT = 20
    def self.spark_listings(page)
    Rails.cache.fetch("spark_listing/page=#{page}", expires_in: 12.hours) do
    SparkApi::ListingCart.listings(pagination: 1, page: page, limit: LIMIT)
    end
    end
    end
    class Listing < ApplicationRecord
    LIMIT = 20
    def self.spark_listings(page)
    Rails.cache.fetch("spark_listing/page=#{page}", expires_in: 12.hours) do
    SparkApi::ListingCart.listings(pagination: 1, page: page, limit: LIMIT)
    end
    end
    end
    class Listing < ApplicationRecord
    LIMIT = 20
    def self.spark_listings(page)
    Rails.cache.fetch("spark_listing/page=#{page}", expires_in: 12.hours) do
    SparkApi::ListingCart.listings(pagination: 1, page: page, limit: LIMIT)
    end
    end
    end
    Saved money

    View Slide

  56. class Listing < ApplicationRecord
    LIMIT = 20
    def self.spark_listings(page)
    Rails.cache.fetch("spark_listing/page=#{page}", expires_in: 12.hours) do
    SparkApi::ListingCart.listings(pagination: 1, page: page, limit: LIMIT)
    end
    end
    end
    class Listing < ApplicationRecord
    LIMIT = 20
    def self.spark_listings(page)
    Rails.cache.fetch("spark_listing/page=#{page}", expires_in: 12.hours) do
    SparkApi::ListingCart.listings(pagination: 1, page: page, limit: LIMIT)
    end
    end
    end
    Saved money
    Saved response time as well

    View Slide

  57. class Listing < ApplicationRecord
    LIMIT = 20
    def self.spark_listings(page)
    Rails.cache.fetch("spark_listing/page=#{page}", expires_in: 12.hours) do
    SparkApi::ListingCart.listings(pagination: 1, page: page, limit: LIMIT)
    end
    end
    end
    Saved money
    Saved response time as well
    Awesome No?

    View Slide

  58. SQL Caching

    View Slide

  59. CACHIN IN RAILS.
    SQL CACHING
    ▸ Inbuilt Rails Feature
    ▸ SQL Caching will be valid per request
    ▸ DON’T NEED TO ANYTHING, Rails will take care of it

    View Slide

  60. Cache Stores

    View Slide

  61. CACHIN IN RAILS.
    CACHE STORES
    ▸ Cache stores - memory_store, file_store,
    mem_cache_store, null store, and user defined custom
    store
    ▸ By default file_store is enabled

    View Slide

  62. Cache Keys

    View Slide

  63. CACHIN IN RAILS.
    CACHE KEYS
    ▸ Hold any object which responds to either cache_key or
    to_param
    ▸ e.g. Rails.cache.read(site: "mysite", owners:
    [owner_1, owner_2])

    View Slide

  64. Conditional Get Support

    View Slide

  65. CACHIN IN RAILS.
    CONDITIONAL GET SUPPORT.
    ▸ Needs these headers HTTP_IF_NONE_MATCH &
    HTTP_IF_MODIFIED_SINCE to be set
    ▸ Sever matches these headers against Etag &
    last_modified respectively
    ▸ Rails provides `stale?` & `fresh_when` helpers

    View Slide

  66. class ProductsController < ApplicationController
    def show
    @product = Product.find(params[:id])
    fresh_when last_modified: @product.updated_at.utc, etag: @product
    end
    end

    View Slide

  67. This is all about Caching!

    View Slide

  68. Take Away
    At least use Fragment Caching and Conditional Get Support

    View Slide

  69. Thank You!
    [email protected]
    @mechanicles

    View Slide