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.

5d651a82a00305208be613eed6d1fe49?s=128

Santosh Wadghule

August 06, 2016
Tweet

Transcript

  1. RAILS AILA!.. CACHING IN

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

  3. Food Lover

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

    1000ms to keep your users
  5. Caching

  6. Caching It is a process of storing data so future

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

    requests for that data can be served faster Improves user experience
  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
  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
  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
  11. Web Cache

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

    Web Cache
  13. Stop! Caching is not always a good way to make

    your app fast
  14. Think! Why your app is slow?

  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.
  16. There are only two hard things in Computer Science: cache

    invalidation & naming things Phil Karlton CACHING IN RAILS
  17. Caching in Rails

  18. CACHING IN RAILS CACHING IN RAILS ▸ Basic Caching (Page,

    Action & Fragment Caching) ▸ Low Level Caching ▸ Alternative cache stores ▸ SQL Caching ▸ Conditional GET support
  19. Setting up caching in Rails

  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
  21. Page Caching

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

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

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

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

  26. None
  27. class PagesController < ApplicationController def contact_us @contact = Contact.new end

    def about render end end
  28. ABOUT US PAGE RESPONSE WITHOUT CACHING Views have taken half

    a second
  29. class PagesController < ApplicationController caches_page :about def contact_us @contact =

    Contact.new end def about render end end
  30. ABOUT US PAGE RESPONSE WITHOUT CACHING Cached entire file in

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

  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
  33. Action Caching

  34. CACHIN IN RAILS ACTION CACHING ▸ Needs `actionpack-action_caching` gem ▸

    It uses fragment caching internally
  35. None
  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
  37. Home page response without caching

  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
  39. Home page response while caching Fragment: views/localhost:3000/home/index Cache key :

    ‘views’ + ‘localhost:3000/home/index’
  40. Home page response with caching

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

  42. Fragment Caching

  43. None
  44. <table> <thead> <tr> <th width="150">Name</th> <th width="150">Amount</th> </tr> </thead> <tbody>

    <% @products.each do |product| %> <% cache(product) do %> <tr> <td width="150"> <%= product.name %> </td> <td width="150"> <%= product.amount %> </td> <td> <%= link_to 'Edit ', edit_product_path(product) %> </td> </tr> <% end %> <% end %> </tbody> </table>
  45. views/products/1-20160731074448006461000/2534a326647f6ea1c765472157da361f Cache Key timestamp (updated_at) product-id Cache Digest for dashboard

    page ‘cache(product) #=> product.cache_key’
  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.
  47. Russian Doll Caching

  48. # product view template <% cache product do %> name:

    <%= product.name %> <%= render product.comments %> <% end %> # comment view template <% cache comment do %> <%= render comment %> <% end %>
  49. class Product < ActiveRecord::Base has_many :comments end class Comment <

    ActiveRecord::Base belongs_to :product, touch: true end
  50. Low Level Caching

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

  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
  53. class Listing < ApplicationRecord LIMIT = 20 def self.spark_listings(page) SparkApi::ListingCart.listings(pagination:

    1, page: page, limit: LIMIT) end end
  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
  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
  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
  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?
  58. SQL Caching

  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
  60. Cache Stores

  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
  62. Cache Keys

  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])
  64. Conditional Get Support

  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
  66. class ProductsController < ApplicationController def show @product = Product.find(params[:id]) fresh_when

    last_modified: @product.updated_at.utc, etag: @product end end
  67. This is all about Caching!

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

    Support
  69. Thank You! santosh.wadghule@gmail.com @mechanicles