Tell Rails to not cache on specific controller - ruby-on-rails

I've googled for the past hour still couldn't find a command that tells Rails that it should not cache anything on that controller.
Not even a single fragment, every query should be performed ...
Any suggestions?
Notes:
Rails: 4.0.9
caching: dalli
Reason:
I use heavy Javascript within that controller - when a user does an action Javascript fetches the newest result from a JSON feed.
example:
root_url/events/all-events.json therefore it's important that Rails does not cache.

You can do it by adding a parameter to the url
just use :
root_url/events/all-events.json?t=#{Time.now.to_i}
when you fetch it, and it will be fetched uncached
also, you can try try the action controller method expires_now
add it to a :before_filter

Related

Is it possible to track views of a model view, if the view is cached?

I have a PostsController and my Show action, will be cached.
But, I want to record the number of times each post was viewed/loaded (i.e. how many times the PostsController#Show action was invoked.
If the router is always serving a cached file, how do I update the post.view_count counter_cache on every load? Is that even possible without breaking the functionality/purpose of caching?
You can Use the Impressionist gem
https://github.com/charlotte-ruby/impressionist

Cache warming with RABL for JSON templates

Okay, this post is a bit wordy before I got to the actual question, so the abridged version basically pertains to cache warming using RABL templates. When calling Rabl.render vs API calls, the caches generated do not have the same cache-keys. When using Rabl.render directly should I expect cache-keys to match, when the same template is called via an API?
K, now the wind-up..
I have a Rails API server on Heroku. I've done a lot of optimization with RABL using russian doll caching to improve the reuse of the underlying objects in collections. Though, I am still left with collection caches, that when generated by the user on first request, is a burden on the experience ( e.g. 1+ second api calls).
When debugging a sample API call, I get the following cache actions on a given object.
...api/v1/activities/26600 :
Cache read: rabl/activities/26600-20140423170223588554000//hash/d30440d18014c72014a05319af0626f7
Cache generate: rabl/activities/26600-20140423170223588554000//hash/d30440d18014c72014a05319af0626f7
Cache write: rabl/activities/26600-20140423170223588554000//hash/d30440d18014c72014a05319af0626f7
so for the same object when calling ...api/v1/activities ( after above call ) I get the desired cache hit:
Cache read: rabl/activities/26600-20140423170223588554000//hash/d30440d18014c72014a05319af0626f7
Cache fetch_hit: rabl/activities/26600-20140423170223588554000//hash/d30440d18014c72014a05319af0626f7
This works great. The next step is to avoid having the first/any API call spend time generating the cache.
So I have been pursuing cache warming techniques to generate these collections prior to the user accessing them. One suggestion is using wget as a way to hit the API directly ( see https://stackoverflow.com/a/543988/451488 ). But this adds a load on the Heroku web dynos, so I want to background cache warming via sidekiq workers.
RABL provides a way to render a template from code directly ( https://github.com/nesquena/rabl#rendering-templates-directly ) which definitely seems like the right approach for this use case. Therefore my intent is to call the RABL engine through some event prior to an API call (for example - a User login event).
So for the above API example, I'd call the following in rails console and expect a cache hit.
irb(main):002:0> #activity = Activity.find(26600)
irb(main):003:0> Rabl.render(#activity, 'api/v2/activities/show_no_root', :view_path => 'app/views', :format => :json)
Cache read: rabl/activities/26600-20140423170223588554000//hash
Cache generate: rabl/activities/26600-20140423170223588554000//hash
Cache write: rabl/activities/26600-20140423170223588554000//hash
Unexpectedly, I did not get a cache hit, but it is obvious that the cache-keys are not the same since the trailing hash signature is missing. I'm not sure why the cache-keys would be different in this case. I am left with no way to warm caches for RABL templates.
UPDATE
Turns out the hash in the trailing cache key is the template hash.
Cache digest for api/v1/activities/_show.rabl: d30440d18014c72014a05319af0626f7
Though this tells me the source of that hash, its still not clear why calling Rabl::Renderer directly would not use this as well.
I wasn't able to use Rabl::Render due to the missing template digest hash in the cache-key. However by creating a sidekiq worker as below, I am able to warm the cache by calling the api as a background process, which works nicely.
class CacheWarmApi
include Sidekiq::Worker
sidekiq_options :queue => :warmers
def perform( url_helper, args, params={},method='get')
if method == 'get'
session = ActionDispatch::Integration::Session.new(Rails.application)
session.get(session.send(url_helper, *args), params)
end
end
end
For example :
CacheWarmApi.perform_async( :api_v2_expensiveapi_url, args_array , params_hash)
I think this is a bit too heavy of a solution, and still think there is a solution out there with Rabl::Render.
I've managed to accomplish this by calling Rabl.render(ref) directly, storing the result in cache directly (Redis in my case) and using that from the request handler in the controller:
Worker:
#... do some long work to calculate object_to_render_with
#.
#.
#.
#render the result using Rabl
render_result= Rabl.render(object_to_render_with, rabl_view_relative_path,
view_path: File.join(Rails.root, 'app/views'),
format: :json) #already the default, being explicit
Redis.current.set(cache_key, render_result, ex: (DEFAULT_CACHE_EXPIRY))
Controller:
def request
#check if we have a recent result
res = Redis.current.get cache_key
if res.nil?
# No result in cache, start the worker
Worker.perform_async
end
# use that cached result or an empty result
render json: res || {status: 'in_progress'}
end
Bonus: also added a layer of tracking progress in the same manner (using other key and other request, updating the Redis manually along the progress of work).
HTH

Disable Session Lookup for a single controller action

I want to disable sessions completely for a controller action, because I want this single controller action (it's an autocomplete action on thousands of values, so speed matters) to be blazingly fast.
I tried using session_off, but it just sets the session variable to nil, an still looks up the users session in the database.
Is it possible to completely disable the Rails::SessionStore middleware, but only for a single controller action or URL?
I am running rails 3.2.17.
The answer is: handle this endpoint in a Rack middleware of your own, and insert it into the stack as early as possible!
You can achieve this in config/routes.rb just by routing to the middleware object:
match 'my_autocomplete_endpoint', to: AutocompleteMiddleware
then just return a response from the middleware and don't go up the stack.
You can put this wherever you want in the stack in config/application.rb with:
config.middleware.insert_before(SomeOtherMiddleware, AutocompleteMiddleware)
e.g., perhaps insert it before Rails::SessionStore.
Rails 5+ solution (maybe before, not sure when this became available).
Add this to your controller. You can specify which actions should not touch/update the session using the only: option.
after_action -> { request.session_options[:skip] = true }, only: :my_action_name
This will make the response not include the set_cookie response header. I found this particularly useful when dealing with a race condition in multiple AJAX requests, whereas one contained a very important session (cookie data) update and the other the session was not used, but Rails still sent back an updated cookie for the session. The race condition could cause the updated session data from the important action to be overwritten from the one I didn't care about.

rails the best way to saving page duration and page loading speed

Hi I'm a beginner of rails and I'm not good at English. so if there is some total nonsense please understand..
I'm trying to record loading speed and page duration in every pages.
I made a database "pages" and method "savepage" in my "Page" model.
To save in every page I put "savepage" method in application controller.
Page.rb
def self.savepage
.
.
.
end
application_controller.rb
before_filter :dosave
def dosave
Page.savepage
end
these kind of format..
My question is
1. am I doing correct? using before_filter to do save in very first of loading process?
2. to save after loading all the contents in a page what should I use?
3. to save after user leave this page what should I use?
I saw before_destroy and after_filter, but I can't find what it is... what filter means.... what action means destroy....
thank you in advance!
before_filter is the first thing which loads before giving request to controller.But your need is completely different . Fundamentally filter are used boolean checking.If certain method is true,it will run otherwise it may not. This filter are further extended and we put code into that filters.(And Even sometimes it is consider as best practice) .
Now, before_filter :dosave might be right but is it not true way of knowing page(UI) loading process. I suggest you to use javascript call or use some manually created helper methods and place it into view .erb files.
May be this will interest you
https://github.com/grosser/record_activities
Log user activities in ROR
what action means ?
Action Controller is the C in MVC. After routing has determined which controller to use for a request, your controller is responsible for making sense of the request and producing the appropriate output. Luckily, Action Controller does most of the groundwork for you and uses smart conventions to make this as straightforward as possible.
Source : http://guides.rubyonrails.org/action_controller_overview.html
I highly suggest you to read above documentation. It is very necessary for you and it covers topic which you asked here.`
And one more thing,
what is action destroy ?
This is simply an action method just like new. Since, rails follow Convention over configuration ( and its developer too) so they put code which do some delete destroy or some destruction. This make thing simple,otherwise more configuration will require which is against rails policy.

Rails - Expiring Cached Actions from Resque (a Delayed Job alternative)

I have Resque queue that processes a particular model to do some post processing. Once this model is updated I would like to expire the cached view action.
I have a Sweeper setup and working, but it only observes changes made to the models in Controller Actions.
I know it is not very MVC to be expiring cached items from my model, but all my post processing logic is contained in my models.
Ideally I would like to not double up on my cache busting code, so if I could get my existing sweeper to watch model changes that would be ideal.
Alternatively I would settle for expriing the action cache from inside my model OR a model observer.
p.s.: I can expire cached fragments from within a model observer I have setup, but not actions.
I'd really like to know what the best practice is here. I'm sure I am not the only one who has this requirment.
Thanks.
I'm wondering if something like this is the way to go:
http://dev.mensfeld.pl/2011/07/rails-wykorzystywanie-sweeperow-poza-kontrolerami-na-samych-modelach/
Since I wasn't able to add my comment on that site you linked to. I'll put it here:
In Rails 3.1.3, if you instantiate the controller and then try calling expire_fragment, you'll get errors about trying to call host on NilClass.
After some experimenting, I remembered that functional tests can instantiate your controller. So I changed the instantiation code to:
#controller ||= ApplicationController.new
if #controller.request.nil?
#controller.request = ActionDispatch::TestRequest.new
end
This seems to work, even in production, even using rails console.
In the end I came up with the following solution:
Added my existing sweeper to the list of object observers in application.rb:
config.active_record.observers = :app_sweeper
Added code to the sweeper methods to instantiate the #controller object if it was missing in the sweeper (which is what happens when coming via a object instead of a controller).
#controller ||= ActionController::Base.new
I could then use the expire_fragment method to expire the cached action, with the following tidbit.
expire_fragment("#{ActionMailer::Base.default_url_options[:host]}/items/#{item.id}")
That said, mj1531's answer might prove to be a nicer solution if it means that I can use the expire_action method instead of faking it with the expire fragment. I will report back when I have test it out and select the best answer.

Resources