Post on 06-May-2015
description
Vincent SpehnerTwitter: @vzmindBlog: vzmind.tumblr.com
Caching with Rails
Janvier 2011
Vzmind aka Ruby on Rails creator
Almost 20 000 Vidéos
2 000 000 pages viewed
https://github.com/thinkdry/blank-application
Why caching ?Culture Pub
Cache is everywhere• Almost at each step beetween the request and the data itself, a cache can be added
• Rails caching keyworld:• Pages • Action• Fragment• Sweepers
Proxy(Squid,
Varnish)
Web Server(Apache,
Nginx)
Rails Stack
(Rack, App)
DB (PostgreSQL,
MySQL)Browser
Cache as earlier as you can
config.action_controller.perform_caching = true
app/config/environments/production.rb
Pages caching• Simpliest and most disconnected from Rails Cache
• Easy to implement but monotlithic and limited
• Stored inside the File System only
config.action_controller.page_cache_directory = RAILS_ROOT + “/…”config/environment.rb
class Articles < ContentController # Add cache on articles index page caches_page :index
app/controllers/articles_controller.rb
public/cache/articles.html
ProxyWeb Serve
r
Rails Stack DBBrow
ser
Action caching• A bit flexible with parameters and path builder
• Easy to implement
• Careful with filters (Action caching is an around filter itself)
•Careful with cache path
• Choose your Cache Store
config.action_controller.cache_store = :file_store, RAILS_ROOT + /public/cache“config/environment.rb
class Articles < ContentController caches_action :show, :cache_path => Proc.new{ |controller| if controller.request.mobile_device? controller.params.merge(:mobile=>controller.request.mobile_device?) else controller.params end}
app/controllers/articles_controller.rb
expire_action :action => :showApp/sweepers/article_sweeper.rb
public/cache/articles.html
ProxyWeb Serve
r
Rails Stack DBBrow
ser
Fragment caching
• Required for dynamic websites
• Atomic caching (result/string/object)
• Choose your Cache Store
- cache(:action => ‘list', :action_suffix => 'all_articles’) do = All articles: -@articles.each do |article| = article.title
app/views/articles/index.haml.html
expire_fragment(:controller => ‘articles', :action => ‘list', :action_suffix => 'all_articles')
app/sweepers/article_sweeper.rb
public/cache/articles.html
ProxyWeb Serve
r
Rails Stack DBBrow
ser
Overview of Cache Stores
• ActiveSupport::Cache::MemoryStore• Default memory storage• Store everything (even object)• Not thread safe
•ActiveSupport::Cache::FileStore• Just for my HTML output• Old school• Simple
• ActiveSupport::Cache::MemCacheStore• Most popular memory cache store• Multi servers• Time-based expiry support
Keep a clean cache• Corrupted cache is dangerous
• Maintaining a cache is costly and add complexity • Time based flushing if using memcached
class ArticleSweeper < ActionController::Caching::Sweeper # This sweeper observe articles model and delete cache on save observe :article def after_save(article) clear_article_cache(article) end
def clear_article_cache(article) expire_page index_path, client endEnd
app/sweepers/article_sweeper.rb
Bypass the Rails Stack
• Pages caching need you to serve the file « manually »
• Nginx/apache rewrite rules • Hack for subdomain if any
# Check index CACHE set $requested_page $request_uri;
# Initialize variables set $cache_path "/cache/views/$http_host/static/";
if ($request_uri ~ ^/$){ set $requested_page "index"; }
# Check if the page is in the cache. if (-f $document_root/$cache_path/$requested_page.html) { rewrite (.*) $cache_path/$requested_page.html break; }
Who is caching?
• Classic caching ?
• Using Memcached ?
• Using Rack::Cache ?
• Never cached anything ?
memcached Power
• Avoiding cache cleaning is possible !!
• :expire_in > 30.minutes
• Intelligent keys
- cache(“#{article.id}-#{article.update_at.to_i}” -data) do = My heavy stuffs to cache
# => /cache/3-1234567-data
#Or just writing- cache(article) do = My heavy stuffs to cache
# => /cache/articles/3-1234567
#And if related to a specific user- cache([article,user]) do = My heavy stuffs to cache
# => /cache/articles/3-1234567/users/23-12345678
Thanks you folks !!