Intermittent page loads in Ruby on Rails app - ruby-on-rails

This one's got me stumped.
I've got a ruby on rails site here (rails 2.0.2, ruby 1.8.6, running on thins (0.8.2) sitting on top of nginx), and when I hit a certain action I sometimes get the correct response, other times I get a 500 error.
The code in this view goes as follows:
def show
#item = Item.find_by_url_slug(params[:id])
filename = "#{RAILS_ROOT}/public/#{#item.index.url}"
#data = File.open(filename, 'rb') { |f| f.read } # read data via File.
render :layout => false
end
The view is rather simplistic, with show.html.erb containing
<%= #data %>
I've suspected caching, and have tried setting a variety of different headers (cache-control, expires etc), and also tried the expires_now() method, to no avail.
Out of interest I've also tried adding a sleep(2) call in the controller, and looking at the network requests it's obvious on the 500 pages that this method isn't being called - none of the above code in the show action is.
Furthermore, there's no evidence of the call in the production.log when the page doesnt load. So, no errors to debug, just the occasional 500 (happening say 2/4 page loads).
I should add that this code works fine locally, and on our staging server - it's only in production that this is an issue.
Any ideas??

Related

Rails is rendering a javascript .js.erb file in the view

I have a situation in Rails 5.2 where a controller is rendering a .js.erb file to the view literally, i.e. showing the javascript code in the view itself.
How can I get Rails to run the js.erb file instead of showing it as if it were a DOM file? It is taking the user away from a page I want the user to stay on.
# controller
def submit_custom_data
...perform some functions...
respond_to do |format|
format.js
end
end
# submit_custom_data.js.erb
console.log('submit_custom_data...');
... Other javascript ...
Additional Info
By preserving the browser console logs on Chrome, I see this:
Resource interpreted as Document but transferred with MIME type text/javascript: "http://localhost:3000/submit_custom_data/1068.js?c=1&d=10&db=1&s=8&tm=&w=3".
Navigated to http://localhost:3000/submit_custom_data/1068.js?c=1&d=10&db=1&s=8&tm=&w=3
This issue did NOT occur on Rails 4.2.7. After updating to Rails 5.2.7 (and updating the bundle), I'm now seeing these issues.

Do I need to generate a controller for a Ruby on Rails Controller call?

I'm setting a session variable to a user's geographical state. I have to use a session variable because I run code on the server specific to that user on page load and I need to know where they are. This code is set up to just update the session variable.
states_controller.rb
class StatesController < ApplicationController
def loc
session[:location] = params[:location]
end
end
routes.rb
post "states/loc" => "states#loc"
The code routes properly and the session variable is updated.
However, when the process is complete I get a 500 error in the console "Missing Template" in the views directory. I haven't seen any tutorials tell users to call the command "rails generate controller" and I'm in the unique situation where I can't call this command.
What possible side effect are there to ignoring this 500 error?
*I'm running an older version of ruby and rails.
What possible side effect are there to ignoring this 500 error?
Each request is crashing your rails server. Thats not good. Since it means that some cases it may have to restart after every failed request - that eats resources like Homer Simpson at a buffet.
Your app should not be raising uncaught exceptions that cause 500 errors - thats just decent professional practice.
So how do I fix it?
Simple, if you don't want the default behavior of rendering a view tell rails to do something else:
class StatesController < ApplicationController
def loc
session[:location] = params[:location]
head :created
end
end
This sends an empty response with the 201 - CREATED http header.
See Rails Guides - Layouts and Rendering in Rails

Getting full URL as displayed in browser URL bar

I have a rails app where I want to send people an email when they sign up. The email has a link to their photos portal so they can get started adding photos, etc..
class MyMailer < ActionMailer::Base
def welcome_email
# ...
link = photos_url # => www.myapp.com/photos
# ...
end
end
The problem is that when I push my code to Heroku and run it live, that link doesn't generate as expected.
The photos_url returns the URL relative to the localhost and ends up generating myapp.herokuapp.com/photos, which is incorrect.
What's even stranger is that if I pause the code at that point with binding.pry and try to see what photos_url is returning, it correctly returns www.myapp.com/photos as expected.
Any thoughts on how to resolve this? I'd hate to have to construct the URL myself from scratch, because that means I have to do it for every environment (localhost, staging, production, etc...)
Thanks!
ActionMailer isn't tied to the request/response cycle, thus it doesn't know what's the host the app is currently running on. Actually, emails are typically sent by some background worker processes which know nothing about the current request URL.
So to make it work you need to set the ActionMailer default_url_options.host option.
Add this into you config/environments/production.rb:
config.action_mailer.default_url_options = { host: 'www.yourapp.com' }

Rails JSONP Parsing error on receiving end

I have a very specific issue...
I have 2 rails apps:
App 1 makes a request to App 2 and gets a valid JSONP response.
App 2 has been outfitted with Rack JSONP Middleware so it returns with the
correct wrapped callback (i.e. JQuery1919191([{[{"find_item":{"geo_lon":-74....etc)
For some reason App 1 throws the following error in javascript console:
JSONP Error: parsererror Error: jQueryXXXXXX was not called.
It works fine, however, when testing this in a non-rails context. See my fiddle for an example: http://jsfiddle.net/pBDyW/16/ This code (that works in this example) is the exact same code I'm using in my rails app.
What I'm confused about is HOW am I supposed to configure App 1 (the receiving app) to handle an incoming JSONP resuqest properly. I'm convinced it must be something with Rails, as calling this from a generic page, served in Apache, works fine.
Please HELP!
It turns out that there are (for whatever reason) subtleties in the way the rails app (App 1) seems to process the incoming request. I modified my approach, which was to render the JSON response in my controller with:
respond_to :json
def json_result
#blah = [stuff from database]
respond_with #blah
end
methods (recommended with the Rack JSON Middlware approach, and restored my JSON response to:
render :json => #find_results, :callback => params[:callback]
The difference here is an odd one. The rails app JSONP call will only succeed if I execute the response like this:
success: callbackFunction
and NOT
success: function(){//do stuff}
However, the latter example works in the JS fiddle. Weird. I guess it's just some kinda formatting discrepancy in Rails 3+.
Anyway, hope this was helpful to anyone who comes across it!

Ruby on Rails 3: Streaming data through Rails to client

I am working on a Ruby on Rails app that communicates with RackSpace cloudfiles (similar to Amazon S3 but lacking some features).
Due to the lack of the availability of per-object access permissions and query string authentication, downloads to users have to be mediated through an application.
In Rails 2.3, it looks like you can dynamically build a response as follows:
# Streams about 180 MB of generated data to the browser.
render :text => proc { |response, output|
10_000_000.times do |i|
output.write("This is line #{i}\n")
end
}
(from http://api.rubyonrails.org/classes/ActionController/Base.html#M000464)
Instead of 10_000_000.times... I could dump my cloudfiles stream generation code in there.
Trouble is, this is the output I get when I attempt to use this technique in Rails 3.
#<Proc:0x000000010989a6e8#/Users/jderiksen/lt/lt-uber/site/app/controllers/prospect_uploads_controller.rb:75>
Looks like maybe the proc object's call method is not being called? Any other ideas?
Assign to response_body an object that responds to #each:
class Streamer
def each
10_000_000.times do |i|
yield "This is line #{i}\n"
end
end
end
self.response_body = Streamer.new
If you are using 1.9.x or the Backports gem, you can write this more compactly using Enumerator.new:
self.response_body = Enumerator.new do |y|
10_000_000.times do |i|
y << "This is line #{i}\n"
end
end
Note that when and if the data is flushed depends on the Rack handler and underlying server being used. I have confirmed that Mongrel, for instance, will stream the data, but other users have reported that WEBrick, for instance, buffers it until the response is closed. There is no way to force the response to flush.
In Rails 3.0.x, there are several additional gotchas:
In development mode, doing things such as accessing model classes from within the enumeration can be problematic due to bad interactions with class reloading. This is an open bug in Rails 3.0.x.
A bug in the interaction between Rack and Rails causes #each to be called twice for each request. This is another open bug. You can work around it with the following monkey patch:
class Rack::Response
def close
#body.close if #body.respond_to?(:close)
end
end
Both problems are fixed in Rails 3.1, where HTTP streaming is a marquee feature.
Note that the other common suggestion, self.response_body = proc {|response, output| ...}, does work in Rails 3.0.x, but has been deprecated (and will no longer actually stream the data) in 3.1. Assigning an object that responds to #each works in all Rails 3 versions.
Thanks to all the posts above, here is fully working code to stream large CSVs. This code:
Does not require any additional gems.
Uses Model.find_each() so as to not bloat memory with all matching objects.
Has been tested on rails 3.2.5,
ruby 1.9.3 and heroku using unicorn, with single dyno.
Adds a GC.start at every 500 rows, so as not to blow the heroku dyno's
allowed memory.
You may need to adjust the GC.start depending on your Model's memory footprint. I have successfully used this to stream 105K models into a csv of 9.7MB without any problems.
Controller Method:
def csv_export
respond_to do |format|
format.csv {
#filename = "responses-#{Date.today.to_s(:db)}.csv"
self.response.headers["Content-Type"] ||= 'text/csv'
self.response.headers["Content-Disposition"] = "attachment; filename=#{#filename}"
self.response.headers['Last-Modified'] = Time.now.ctime.to_s
self.response_body = Enumerator.new do |y|
i = 0
Model.find_each do |m|
if i == 0
y << Model.csv_header.to_csv
end
y << sr.csv_array.to_csv
i = i+1
GC.start if i%500==0
end
end
}
end
end
config/unicorn.rb
# Set to 3 instead of 4 as per http://michaelvanrooijen.com/articles/2011/06/01-more-concurrency-on-a-single-heroku-dyno-with-the-new-celadon-cedar-stack/
worker_processes 3
# Change timeout to 120s to allow downloading of large streamed CSVs on slow networks
timeout 120
#Enable streaming
port = ENV["PORT"].to_i
listen port, :tcp_nopush => false
Model.rb
def self.csv_header
["ID", "Route", "username"]
end
def csv_array
[id, route, username]
end
It looks like this isn't available in Rails 3
https://rails.lighthouseapp.com/projects/8994/tickets/2546-render-text-proc
This appeared to work for me in my controller:
self.response_body = proc{ |response, output|
output.write "Hello world"
}
In case you are assigning to response_body an object that responds to #each method and it's buffering until the response is closed, try in in action controller:
self.response.headers['Last-Modified'] = Time.now.to_s
Just for the record, rails >= 3.1 has an easy way to stream data by assigning an object that respond to #each method to the controller's response.
Everything is explained here: http://blog.sparqcode.com/2012/02/04/streaming-data-with-rails-3-1-or-3-2/
Yes, response_body is the Rails 3 way of doing this for the moment: https://rails.lighthouseapp.com/projects/8994/tickets/4554-render-text-proc-regression
This solved my problem as well - I have gzip'd CSV files, want to send to the user as unzipped CSV, so I read them a line at a time using a GzipReader.
These lines are also helpful if you're trying to deliver a big file as a download:
self.response.headers["Content-Type"] = "application/octet-stream"
self.response.headers["Content-Disposition"] = "attachment; filename=#{filename}"
In addition, you will have to set the 'Content-Length' header by your self.
If not, Rack will have to wait (buffering body data into memory) to determine the length.
And it will ruin your efforts using the methods described above.
In my case, I could determine the length.
In cases you can't, you need to make Rack to start sending body without a 'Content-Length' header.
Try to add into config.ru "use Rack::Chunked" after 'require' before the 'run'. (Thanks arkadiy)
I commented in the lighthouse ticket, just wanted to say the self.response_body = proc approach worked for me though I needed to use Mongrel instead of WEBrick to succeed.
Martin
Applying John's solution along with Exequiel's suggestion worked for me.
The statement
self.response.headers['Last-Modified'] = Time.now.to_s
marks the response as non-cacheable in rack.
After investigating further, I figured one could also use this :
headers['Cache-Control'] = 'no-cache'
This, to me, is just slightly more intuitive. It conveys the message to any1 else who may be reading my code. Also, in case a future version of rack stops checking for Last-Modified , a lot of code may break and it may be a while for folks to figure out why.

Resources