In order to boost the performance of my Rails 4.0.2 app, I would like to cache the output of some of my static pages:
class PagesController < ApplicationController
def home
end
def about_us
end
def contact
end
end
In the Rails Guide on Caching it says that "Page Caching has been removed from Rails 4" and moved into a gem. In the gem description it says, however, that it will be maintained only until Rails 4.1. Some other observers also advise against using Page Caching and endorse Russian doll caching instead.
So what's the best way to cache a bunch of static pages that will never actually hit the database and only ever change (slightly) if a user signs in?
Thanks for any suggestions.
You can still using fragment caching for your static pages, although the benefits are obviously more visible with dynamic / DB-driven pages. It's worth considering doing this should you have a lot of partial being rendered or costly view logic. Just wrap your page's template with:
# about_us.html.erb
<% cache 'about_us' do %>
...
<% end %>
the first time you hit the page in an environment where config.action_controller.perform_caching = true, it'll generate the fragment (which in this case is your whole page), and it'll serve that the next time you reload it. The cache digest will be invalidated when the template is changed:
The template digest that's added to the cache key is computed by
taking an md5 of the contents of the entire template file. This
ensures that your caches will automatically expire when you change the
template file.
http://api.rubyonrails.org/classes/ActionView/Helpers/CacheHelper.html
Related
My app uses global variables like logos, app name, etc retrieved from the database and shown on different controllers and views. I put it in ApplicationController to be available to all, but I find that the individual controllers repeat the same query sometimes.
class ApplicationController < ActionController::Base
$image = Setting.find_by_name('image').value
$city = Setting.find_by_name('city').value
$currency = Setting.find_by_name('currency').value
end
Is there a way to make the same variables available to all controllers (and users) with just a one-time query with the variables saved on memory, such as when the app starts up?
You could attempt to use initializers.
Rail will load all files in config/initializers/ folder when the server starts up. This can work as a place to initialize application-wide variables. We could create a file inventory.rb file in the initializers directory:
at config/initializers/inventory.rb
module Inventory
class Count
Orders = Order.all
end
end
Inventory::Count::Orders
# => "[your orders will show here]"
These will only be loaded once when the server is started or restarted. As such this works well if the values you need won't change. If they will change I don't think there is a good way to avoid running multiple queries.
Whats about caching? Rails is using SQL Caching and you can use Low Level Caching. See the guides (1.7 and 1.8): https://guides.rubyonrails.org/caching_with_rails.html#low-level-caching
I have a Rails 3.2.11 application running on Unicorn and is set up for file_store caching to a specific folder outside of the project.
I am also using the gem rails/cache_digests for automatic expiration.
In a certain page I'm doing fragment caching without setting time expiration.
When a fragment has expired, I see the new fragment created in the cache folder, but I also see the expired fragment as well. How it will be deleted from the cache folder by the cache management mechanism without doing it manually? If it doesn't get deleted then the cache folder will be bloated with garbage, expired fragments that are not used.
You can try to use this cleanup function to delete all the expired fragments. And you may set up a script to run this command periodically.
You can use ActionController::Caching::Sweeping to expire fragment cache.
See below example:
class ProductSweeper < ActionController::Caching::Sweeper
observe Product
#expire fragment after model update
def after_save
expire_fragment('all_available_products')
end
#expire different cache after controller method modifying state is called.
def after_products_update_some_state
expire_action(:controller => 'products', :action => 'index')
end
#can also use before:
def before_products_update_some_state
#do something before.
end
end
This url is also help you
So we use the same controllers to serve both mobile and desktop views of our site. We also use action caching heavily to cache the html for a page in memcache. I've been trying to figure out a way to globally change the caching prefix for all mobile requests to "views-mobile/" instead of the standard "views/". That way the mobile and and desktop pages will be saved under a different namespace so there are no conflicts in memcache.
We could do this per caches_action method by creating a custom cache_path using the controller variable for is_mobile?, but we'd prefer to do it globally somehow. Any suggestions? I imagine this would require monkey-patching ActionController::Caching but I can't figure out where it generates the "views/" prefix.
I'm sorry, I was Rails nubie, so I don't really understand about your question, but if it right, is this what you mean?
This is on my routes.rb:
scope "/administrator" do
resources :users
end
I changed my users_path 'prefix' to administrator. Sorry if wrong :D
I actually ended up figuring this out myself. Basically ActionController::Base uses a function called fragment_cache_key to generate the cache key for a specific fragment (which is what ActionCaching uses deep down). So you basically override that method and include your own logic for how to generate the prefix. This is how my method override looks:
# Monkey patch fragment_cache_key
def fragment_cache_key(key)
ActiveSupport::Cache.expand_cache_key(key.is_a?(Hash) ? url_for(key).split("://").last : key, mobile_device? ? "views-mobile" : "views")
end
Where mobile_device? is my own function that figures out whether the user is requesting the mobile or desktop version of the site.
I have an application that uses caches_page for certain controllers/actions. To expire the cache, I use a sweeper. All in all, it's a standard solution.
However, because some changes may cause a bit of a rush of requests on the server (because push notifications are sent out and may trigger client devices to fetch new data), I'd like to be able to pre-render the cache, so it's ready before the requests roll in. I could just wait for the first request to automatically write the cache, of course, but in this case, I know that the requests will come, that there might be many, and that they may be near-simultaneous. So I'd like to have the cache ready.
To add some complexity, the updates are done via a normal web page and handled in a standard, mostly scaffolded controller, while the "page" I want to cache is the JSON response for an entirely different controller that serves as an API.
So, how do I, from a sweeper (or from the controller handling the cache-expiring update) trigger a new page cache to be written immediately?
Another way to put it might be: How do I make an internal request from one controller to another?
Edit: Ended up doing something like what you see below. It's not terribly elegant, but it is effective
class ModelSweeper < ActionController::Caching::Sweeper
observe Model
def after_create(model)
expire_pages_for(model)
end
def after_update(model)
expire_pages_for(model)
end
def after_destroy(model)
expire_pages_for(model)
end
protected
def expire_pages_for(model)
# expire index page
expire_and_bake(models_url)
# expire show page
expire_and_bake(model_url(model))
end
def expire_and_bake(url)
# extract the path from the URL
path = url.sub(%r{\Ahttp://[^/]+}, "")
# expire the cache
expire_page(path)
# request the url (writes a new cache)
system "curl '#{url}' &> /dev/null &"
end
end
Warming a server's cache may fall outside of the realm of your application logic. I have implemented a cache warming system before using a rake task that wrapped the curl command and looped through all the areas in the website.
# lib/tasks/curl.rake
desc "curl"
task :curl do
paths.each do |path|
`curl #{path}`
end
end
You can call this task by issuing "rake curl" from inside your Rails project root.
Alternately, you could invoke this rake task (which wraps curl) from inside your sweeper method after you expire the cache. Check out the Railscast Ryan Bates did on invoking rake tasks in the background from inside your Rails application code: http://railscasts.com/episodes/127-rake-in-background
More information on curl here: http://curl.haxx.se/docs/manpage.html
Alternatively, I can write a bunch of Time.now and print out the subtractions but is there a standard way it can be done by gems or by Rails' standard and conventional method?
You might want to take a look at Railscast 98 and 161.
I've used NewRelic's hosted plan and it's definitely a great tool if the basic plan meets your needs or you can afford a paid plan. I haven't played around with the developer mode yet but it looks pretty good, especially for being free. Rack::Bug look nice too I might just have to take it for a spin.
You could add a statment on the top/end of your application.html.erb?
# application_controller.rb
class ApplicationController < ActionController::Base
before_filter :set_start_timer
def set_start_timer
#start_time = Time.now
end
end
# application.html.haml
=#start_time # this is the overall processing time
=Time.now # this is the start view render time
=yield
=Time.now # this is the view render time after processing content