Fragment_exist for Memcache does not find cached info - ruby-on-rails

I am using Rails, Dalli gem, friendly_id and Memcachier on Heroku.
My problem is similar to an issue I have had previously but that stopped working after I started using Memcache instead of the default Rails cache. It should be noted that I am not very familiar with Rails caching and it is quite likely that I do many things wrong (or fail to consider simple things).
production.rb
config.action_controller.perform_caching = true
config.cache_store = :dalli_store, 'mc2.ec2.memcachier.com:11211', { :namespace => 'my_app_name', :expires_in => 40.days, :compress => true, :username => 'asdf', :password => 'asdf' }
Gift#show - controller
unless fragment_exist?("gift-show--" + #gift.slug)
# Perform slow database fetches etc
end
Gift#show - view
<% cache('gift-show--' + #gift.slug) do %>
# Create HTML with lots of images and such
<% end %>
This worked fine before I started using Memcachier on Heroku. My guess is that fragment_exist? does not check in Memcachier but rather in "default Rails cache" (if there is a difference). I have tried to use Rails.cache.exist?("gift-show--" + #gift.slug) instead of fragment_exist? but it doesn't work.
I have loaded the particular gift#show-view a few times to make sure it is cached. In the logs I can also see Read fragment views/gift-show--cash-stash (1.3ms) (after the controller) which I believe is a proof for that a fragment actually exist. It is just that it is going through the slow (4 seconds) gift#show-controller when it is not necessary.
If I enter the console on Heroku and type "Rails.cache.read('gift-show--cash-stash')" I get a nil response.
Another peculiar things is that if do the following in the console:
irb(main):014:0> Rails.cache.write("foo", "bar")
=> true
irb(main):015:0> Rails.cache.read("foo")
=> nil
That is odd, isn't it?
So, what should I use, instead of fragment_exist? in order to make this work?

I am not 100% sure this is the solution but I added the 'memcachier' gem (which I didn't have) and altered my production.rb to:
config.cache_store = :dalli_store
This actually also solved another, completely different issue, to my great surprise!

Related

Rails ActiveStorage returns 404 even tough attachment file exists

I'm having 404's trying to use ActiveStorage. Here's the code:
class Model
has_many_attached :attachments, dependent: :destroy
# In form
<%= form.file_field :attachments, multiple: true %>
# In controller
def model_request_params
params.require(:model_name).permit(:title, attachments: [])
end
# In config/storage.yml
local:
service: Disk
root: <%= Rails.root.join("storage") %>
# In development.rb
config.active_storage.service = :local
# In view
<%#= image_tag #model_instance.attachments.first %>
When I open the browser, the generated HTML is like this:
http://localhost:3000/rails/active_storage/blobs/eyJfcmFpbHMiOnsibWVzc2FnZSI6IkJBaHBDZz09IiwiZXhwIjpudWxsLCJwdXIiOiJibG9iX2lkIn19--3814080e8c7e26964c927840a18034d727c61d87/file.jpg, but this returns a 404 not found.
The weird thing is that I have a different Rails 5.2.0 project and as soon as this GET is fired, the server console shows Precessing by ActiveStorage::Blobs Controller.
On this project however, I only see this:
Started GET "/rails/active_storage/blobs/eyJfcmFpbHMiOnsibWVzc2FnZSI6IkJBaHBDZz09IiwiZXhwIjpudWxsLCJwdXIiOiJibG9iX2lkIn19--3814080e8c7e26964c927840a18034d727c61d87/file.jpg" for 127.0.0.1 at 2018-07-23 12:15:36 -0300
It is NOT followed by anything else, not indicating any controller or whatever, but the browser gets a 404 and the image is never loaded.
I can confirm the attachment is uploaded, because not only model_instance.attachments[0] returns a ActiveStorage::Attachment, but the file is also present in my project's storage/ folder.
System configuration
Rails version: 5.2.0 (not brand new, updated - I don't see this bug in a brand new Rails 5.2.0 app)
Ruby version: 2.3.6
Here's the culprit, I had this at the very beginning of my routes.rb file:
if Rails.env.development?
scope format: true, constraints: { format: /jpg|png|gif|PNG/ } do
get '/*anything', to: proc { [404, {}, ['']] }
end
end
I had totally forgotten about this hack. Here's some context:
We frequently dump the production database to our dev machines to have an up-to-date dev environment. But we do NOT copy the entire public/uploads folder because it's crazy huge.
This makes so that many of the pages that link to uploaded assets are VERY slow to load, because each image hits the dev application server which errors out with 404.
After this slowing us down for years, we found this excellent solution in a makandra card:
When you load a dump for development, records may reference images
that are not available on your machine. Requests to those images may
end up on your application, e.g. if a catch-all route is defined that
leads to a controller doing some heavy lifting. On pages with lots of
missing images, this slows down development response times. You can
fix that by defining a Rails route like this:
(code above)
Images are (usually) served directly from public or assets and won't hit your controllers/routes, as long as the files exist. If files are missing, the request will be handled by the above route which instantly responds with an empty HTTP 404 response.
Needless to say, that hacky fix was hijacking the /rails routes used by ActiveStorage because they satisfied the constraint.
I can't comment on makandra cards, but I hope Google will bring people here.
In the meantime, I changed the hack to this:
if Rails.env.development?
scope format: true, constraints: { format: /jpg|png|gif|PNG/ } do
get '/*anything', to: proc { [404, {}, ['']] }, constraints: lambda { |request| !request.path_parameters[:anything].start_with?('rails/') }
end
end
My problem also seemed to be that my queue adapter wasn't running in development, so the images could not be processed
config.active_job.queue_adapter = :inline in development.rb fixed that for me

Rails render :json => [1,2,3] behaving differently in production (json root)

I'm seeing some weird behaviour in my Rails app. I'm running ruby 1.9.2-p290, and I have this sort of controller:
class NumbersController < ApplicationController
def index
render :json => [1,2,3]
end
end
Imagine I run the servers like this to demonstrate the problem:
$ rails s # This one runs in "development" on port 3000
$ RAILS_ENV=production rails s -p 2999 # This one runs in "production" on port 2999
In development or test mode, my result would be
$ curl localhost:3000/numbers # development
{numbers: [1,2,3]} # The root is being included in the json, as inferred from the controller name.
$ curl localhost:2999/numbers # production
[1,2,3] # No root in the JSON
I've been over the app with a fine tooth comb and there are no obvious configuration differences that look like they would affect json between development and production. Also, no lines like "if Rails.env === 'production'"
I'm guessing that different gems are being required, e.g. for assets, which are changing the behaviour of render :json => ... . I've inspected the versions of the "json" and "multi_json" gems from within the running apps, and they are the same (1.7.5 and 1.3.6 respectively, and multi_json uses the same adapter.). How do I find out exactly which gems are required from within the app while it is running? Also, does anyone have any alternative explanations?
EDIT: I'm running Rails 3.1.1 and the assets part of my Gemfile is:
group :assets do
gem "ember-rails"
gem "jquery-rails"
gem "less", "2.0.7"
gem "less-rails", "2.0.2"
gem 'uglifier'
end
I found a workaround here: include_root_in_json from controller
The solution:
render :json => {:numbers => [1,2,3]}, :root => false # If you want the root
:root => true isn't being respected in production. I suspect the as_json or to_json has been overridden incorrectly in that environment.
I'm still not happy though, because I can't rely on render :json => [1, 2, 3].
This behavior is controlled by this setting in config/initializers/wrap_parameters.rb for new rails apps.
# Disable root element in JSON by default.
ActiveSupport.on_load(:active_record) do
self.include_root_in_json = false
end
It could be that because your controller doesn't reference any AR (ActiveRecord) classes, AR isn't yet loaded due to lazy loading in development mode. Hence the setting is true.
You could test that theory by putting ActiveRecord::Base in your index action to force it to load.
I'd be looking for the following configuration:
ActiveRecord::Base.include_root_in_json
It produces exactly the difference you are getting between the JSON representations of object: if set to true the Object name is added as a root key of the produced JSON.
How do I find out exactly which gems are required from within the app
while it is running?
The Gemfile.lock file specifies this.

Accessing a Model from js.erb file when precompiling

I have a Rails 3 app where I am trying to populate a javascript variable with every Nation in my database (less than 300 nations) as a JSON object. This is the relevant line in my nations.js.erb file:
_this.nations = <%= Nation.all.to_json :only => [:id], :methods => :text %>;
When I call my js file in a browser, /assets/users.js which does a require of the nations file, the _this.nations variable is populated perfectly. When I try to do a precompile I get the following:
$> rake assets:precompile
$> rake aborted!
uninitialized constant Nation (in nations.js.erb)
So my question is this: is it possible to reference the Nation model, or any model, from within the js.erb file for precompiling? I also tried using my NationsHelper but my error just changed to uninitialized constant NationsHelper.
I'm fairly new to RoR so if relevant information is needed that I haven't provided, please just ask.
If you have config.assets.initialize_on_precompile set to false somewhere then try enabling it
config.assets.initialize_on_precompile = true
Why don't you make the call in the controller
controller
#nations = Nation.all
nations.js.erb
_this.nations = <%= #nations.all.to_json :only => [:id], :methods => :text %>;

How to suppress/disable "cache miss" messages when running rspec tests in rails 3.1

I'm starting to see the following output when running request rspec specs:
cache: [GET /login] miss
cache: [GET /javascripts/jquery.min.js?1317513028] miss
Normaly I would get green dots for passing tests and red Fs with some info for error messages.
Is there a way to disable the cache miss messages from the output?
I think this has nothing to do with rspec and that rspec is just printing out what is in the rails log. I think this post by Brian Weaver on the Phusion Passenger discussion group might answer your question:
Do you have rack-cache installed? I spent a good day+ tracing through Passenger/Rails/Rack and lots of other gems to find out why lines similar to that one was appearing.
Add the following to your production.rb or development.rb file to get rid of the 'cache: ....' line
config.action_dispatch.rack_cache = {:metastore => "rails:/", :entitystore => "rails:/", :verbose => false}
I pulled that line from within the "middleware" configuration of Rails an just changed the 'verbose' from true to false.
In your case, I guess you want to add it your your test environment file.
In addition to #RyanTM you also need to turn caching on for the test environment so that DragonFly does not configure its own Rack::Cache (with :verbose => true) but instead uses the one setup by Rails.
# set Rack::Cache verbose to false to prevent logging to rspec output
config.action_controller.perform_caching = true
config.action_dispatch.rack_cache = {:metastore => "rails:/", :entitystore => "rails:/", :verbose => false}
I couldn't get either of the above workarounds to work for me, but adding this to my 'config/initializers/dragonfly.rb' worked:
if Rails.env.test?
Rails.application.middleware.delete Rack::Cache
Rails.application.middleware.insert 0, Rack::Cache, {
:verbose => false,
:metastore => URI.encode("file:#{Rails.root}/tmp/dragonfly/cache/meta"), # URI encoded in case of spaces
:entitystore => URI.encode("file:#{Rails.root}/tmp/dragonfly/cache/body")
}
end

will_paginate works on local box, gives "undefined method to_i" on server but both point to the same database

We just ran into this problem the other day all of a sudden. Our Rails app is using will_paginate, and I have it defined as follows in my controller:
# contacts_controller.rb
def index
# ...
#search = #current_user.contacts.search(params[:search])
#contacts = #search.all.paginate({:page => params[:page], :per_page => 20})
end
Both development and staging (this is a staging box not production) point to the same database. The above code works fine on my local machine, however on staging I get the following error:
undefined method 'to_i' for {:per_page=>20, :page=>nil}:Hash
The code is identical on both computers. Any idea why it works fine on one and not on the other?
EDIT: On the staging server I was using the plugin while on my local box I was using the gem, however I removed the plugin and installed the gem and now I get an error that says:
uninitialized constant Array::WillPaginate
Evidently it is still not solved.. after doing a deploy on the staging server I'm getting that error again, despite the fact that it was working before fine. Anyone have any ideas at all on this? I'm totally clueless.
If the page param is nil, it's often a good idea to default to a value of 1. This covers cases where no url parameter for page is passed. Where a parameter is passed, it will be used in place of the default:
{:page => params[:page] || 1, :per_page => 20})
Full example:
# contacts_controller.rb
def index
# ...
#search = #current_user.contacts.search(params[:search])
#contacts = #search.all.paginate({:page => params[:page] || 1, :per_page => 20})
end
Well, I seem to have solved it:
I had to explicitly type require 'will_paginate' in the environment after I uninstalled the plugin and installed the gem. However I'm still not sure why the plugin was working on my local machine but not on staging.

Resources