My understanding was that to use http caching in Rails 3, all I had to do was add something like this to the action
expires_in(10.seconds, :public => true)
but when I do this, it tries to write to tmp/cache in addition to performing http caching in the browser (same user request for that action doesn't ever hit the server, as expected)
Why is this? How do I stop Rails from writing to the cache directory for http caching?
PS: I'm using nginx+passenger
The things that writes to /tmp/cache is Rack::Cache::FileStore that is configurable via config.cache_store; It's a rails3 native proxy-cache;
if you want to disable it:
config.action_dispatch.rack_cache = nil
Another solution is to not use the :public => true setting in expires_in. It appears that this setting is the reason why Rack::Cache writes the response to cache. If you set it to private => true (the default), this doesn't happen
Related
I have a Rails 6 API-only application and I'm using Nginx and passenger.
When I try to do GET or POST in the Postman application or other places, I get only cached responses.
For instance, if I try to get user 1, movies, I'll get the correct ones, but if I change the JWT to user 2, I still see the previous response which was for user 1.
I have tried a few options such as:
In production.rb I added:
config.action_controller.perform_caching = false
config.cache_store = :null_store
I have also tried the controller level by adding: expires_now which in the header shows no-cache and also with expires_in 5.seconds, public: false which shows max-age=5, private as the header, but I still get the same response for any user.
you can use a command to disable cache in rails app: rails dev:cache
Our Rails app has a custom 404 and 500 pages:
match '/500', :to => 'errors#internal_server_error', :via => :all
And we have some specs to make sure it works.
In test.rb:
Rails.application.configure do
config.consider_all_requests_local = false
config.action_dispatch.show_exceptions = true
end
However, during development exceptions are swallowed making it difficult figure out what is happening.
So, for some tests we need the above config, and other specs different config. But the config is set before the test runs so not able to update.
How to update the config for a single spec in a before block?
Rails 6
EDIT: What I've tried:
Shira mentioned mocking, but that does not appear to do anything.
Rails.application.config.consider_all_requests_local = false
Rails.application.config.action_dispatch.show_exceptions = true
This does work, but only before the first request. After the first request it does not do anything.
Rails.application.config.consider_all_requests_local = ->{ ENV['...'] }
This does not work.
It seems that the issue is that these configs are used in the middleware and once the app is configured changes are not reflected.
I tried to find a way to re-initialize the Rails app, but it appears that there is no way to do it.
I guess the only way to do it is to monkey patch the middleware to use a Proc with an ENV...
This does work:
Rails.application.env_config['action_dispatch.show_exceptions'] = true
Rails.application.env_config['action_dispatch.show_detailed_exceptions'] = false
Thanks to Eliot Sykes https://www.eliotsykes.com/realistic-error-responses
I am not sure if it is possible to actually change the configuration but you can mock this configuration instead:
allow(Rails.application.config).to receive(:consider_all_requests_local).and_return(true)
Given that Heroku Cedar doesn't have http caching provided by Varnish I would like to use Rack::Cache.
I have been told that rails 3.1.1 have Rack::Cache active by default, I just need to make sure to have in the configuration:
config.action_controller.perform_caching = true
and I need to pick a cache store, for this experiment I'm using:
config.cache_store = :memory_store
In the action of the page I want to cache I've added the following lines:
response.header['Cache-Control'] = 'public, max-age=300'
response.header['Expires'] = CGI.rfc1123_date(Time.now + 300)
This code used to work fine with Varnish, the first request would return a 200 and the subsequent (for 5 mins) would return a 304.
This doesn't happen with Rails 3.1 and Heroku Cedar Stack.
I do get those headers in the response but subsequent requests returns 200 instead of 304.
What am I doing wrong? Thank you.
As you noted, the Cedar stack doesn't use Varnish. That means a web request will always hit the ruby server.
With that in mind, Rack::Cache will respect your headers and serve the cached content.
However, since the request is actually going past the http layer into the rails app, the response will always be 200 since the cache doesn't happen at the http layer anymore.
To confirm this is true, insert this in one of your cached actions:
<%= Time.now.to_i %>
Then, reload the page several times and you'll notice the timestamp won't change.
I'm implementing some caching by using the nifty Rails.cache.fetch. However, in one particular instance, sometimes I encounter an exception:
TypeError in EmloController#index
Emlo can't be referred to
app/controllers/emlo_controller.rb:320:in `get_employees'
app/controllers/emlo_controller.rb:356:in `prepare_json_response'
app/controllers/emlo_controller.rb:23:in `block (2 levels) in index'
app/controllers/emlo_controller.rb:15:in `index'
It seems the fetch will always explode (with the above) on the first try, and then work fine as long as the fetch is within the expiration. I know I'm missing something, so a fresh pair of eyes would be nice.
Here's the method which invokes the cache fetch:
def get_employees
# This is for a AJAX refresh loop, so a 5-second cache actually helps quite a bit
Rails.cache.fetch('emlo_all', :expires_in => 5.seconds, :race_condition_ttl => 1) do
conditions = (params[:id]) ? {:user_id => params[:id]} : nil
selections = [
'employee_locations.id AS emlo_id',
'employee_locations.status_id',
'employee_locations.notes',
'employee_locations.until',
'employee_locations.updated_at',
'employee_locations.user_id',
'location_states.id AS state_id',
'location_states.title AS status_string',
'location_states.font_color',
'location_states.bg_color',
'users.displayname',
'users.email',
'users.mobile',
'users.department',
'users.extension',
'users.guid',
'users.dn'
].join(', ')
Emlo.all(
:select => selections,
:joins => 'LEFT JOIN users ON employee_locations.user_id=users.id LEFT JOIN location_states ON employee_locations.status_id=location_states.id',
:conditions => conditions,
:order => 'users.displayname ASC'
)
end
end
This problem arises in development mode when config.action_controller.perform_caching = true AND config.cache_classes = false -- it seems ActiveRecord objects cannot be stored with Rails.cache.
But if you need to enable config.action_controller.perform_caching in development mode for testing caching, then you must also enable config.cache_classes. This would be temporary, though, because then you'd have to restart the development server after changing classes or files in the asset pipeline.
With caching disabled, I would use Rails.cache.write(some_name, some_value) if Rails.env.production? to prevent caching from blowing up in development. Rails.cache.read() doesn't seem to be affected.
I encountered something similar storing an array of instances of a class via Rails.cache.fetch(...) do - it'd work fine until I changed any code (causing the app to be reloaded), then throw "...can't be referred to".
As #Matthew Clark mentions above it seems to be to do with the class being auto-reloaded because config.cache_classes is false in dev mode - this causes the objects stored with Rails.cache.fetch to be stale since they're effectively of a different class to the newly reloaded one.
In my case the class was a trivial one so I was able to move it to the lib directory so it's not auto-reloaded and require it in my code. If this wasn't feasible, perhaps you could do something like Rails.cache.fetch(... force: !Rails.configuration.cache_classes) if you didn't mind the extra overhead in dev mode.
Today, you can write a wrokarround following the idea of rails guides:
https://guides.rubyonrails.org/classic_to_zeitwerk_howto.html#before-remove-const
Add into a initializer file the following code:
if Rails.env.development?
unless Rails.application.config.cache_classes
Rails.autoloaders.main.on_unload do |klass, _abspath|
Rails.cache.clear
end
end
end
When some class must be reloaded then you clear the cache, and it will not fails, the only side effect will be a milliseconds more each time you change your code, and with every change, cache must be reloaded too.
Depending on the structure of your application, you might get an error in development like this:
TypeError (User can't be referred)
This error is caused by some caching-reloading madness: The middleware implanted by some gem is cached. But in development, your classes usually aren't. Thus some classes may not be available under certain circumstances, e.g. if you are using before filters for user authentication provided by some engine. You should be able to get rid of the error above by turning on class caching. Try it (and restart the server afterwards):
development.rb
config.cache_classes = true
If the error is gone, you're lucky. But since it is not feasible to cache classes in development, turn off class caching again and explicitly require the class that couldn't be referred. I.E.:
top of development.rb
require 'app/models/user'
I was just wondering and didn't find explicit response on what in the model class (ActiveRecord) is cached when setting config.cache_classes to true ?
Could someone tell me or point me to the doc I didn't found ?
Thanks
It determines whether or not your application classes are reloaded on each request. If it's true, you have to restart your server for code changes to take effect (i.e. you set it to true in production, false in development.)
Documentation is here.
What is cached when using config.cache_classes = true
It responsible for two thing in rails 4
1. It prevent class reloading between requests.
2. It ensure Rack::Lock in not included in middleware stack, so
that your thread don't get locked.