Rails: How to send json response immediately for long running job - ruby-on-rails

I am processing long running jobs. i am invoking long running method from controller. i want to send json response immediately to the user. below is my code but its not working.
def process
p = Proces.new(:status => "in-progress")
render :json => {:id => process.id}
long_running_job()
end
How can i send json response immediately?

You need to run your long_running_job in a background job queue. This will make your long running task occur outside of the web request cycle, allowing you to return the JSON immediately, while your long running processes continues elsewhere. Look into ActiveJob if you're on Rails 4.2+ and/or Sidekiq.

Related

How to call method after render?

I need to do request on remote service after rendering the page
My controller:
after_filter :remote_action, only: :update
def update
#res = MyService.do_action foo, bar
return render json: #res[:json], status: #res[:status] unless #res[:success]
end
def remote_action
# There is remote http request
end
I need to call remote_action method after rendering the page
after_filter is run after the template has been converted into html, but before that html is sent as a response to the client. So, if you're doing something slow like making a remote http request, then that will slow your response down, as it needs to wait for that remote request to finish: in other words, the remote request will block your response.
To avoid blocking, you could fork off a different thread: have a look at
https://github.com/tra/spawnling
Using this, you would just change your code to
def remote_action
Spawnling.new do
# There is remote http request
end
end
The remote call will still be triggered before the response is sent back, but because it's been forked off into a new thread, the response won't wait for the remote request to come back, it will just happen straight away.
You could also look at https://github.com/collectiveidea/delayed_job, which puts jobs into a database table, where a seperate process will pull them out and execute them.

How to stop action execution with another action in rails?

I have following actions in a Test controller:
def run
while true do
# code
end
end
def stop
# stop the run action
end
how can stop action be implemented to halt the run action?
Because a client will wait for a response from a server, you can't have a loop in an endpoint that waits for another endpoint to be called.
In this case, a client will visit /test/run, but since the server won't return anything until the loop finishes, the client will just keep waiting.
This means (unless you specifically configured your webserver to do so), that another connection can't be made to the server to reach the test/stop endpoint.
If you must have a "job" that runs and be cancel-able by an endpoint, turn it into an actual background task.

Lengthy operation in Ruby-on-Rails

I am face to face with the following situation.
A user clicks on a link in order to generate a text or XML. I have a method generateXMLfile in my controller which reads data from a db table and creates a hash or array. After reading and creating are finished I send data using send_file method.
The file generating process may take time between 5 and 25 seconds (huge data), so what I want to do is to display the "Please wait" message with waiting gif animation while the request is being processed, and display the success message upon successful completion.
I know how to implement similar operations such as, for example, a file upload using pure AJAX, but I don't know how to do it in Rails.
Has anyone dealt with the similar problem? What is the best practice or Rails way to perform this operation? Any suggestions or recommendations?
UPDATE:
def generateXMLfile
#lengthy operation
(1..100000000).each do
end
sample_string = "This is a sample string\ngenerated by generateXML method.\n\n Bye!"
send_data sample_string,
:type => 'charset=utf-8; header=present',
:disposition => "attachment; filename=sample.txt"
end
You can bind call like this using UJS.
<%= link_to "send file",generateXMLfile_path, :remote => true, :id => "send_file" %>
$('#send_file').bind('ajax:beforeSend', function() {
$('#please wait').show();
});
$('#send_file').bind('ajax:complete', function() {
$('#please_wait').hide();
$('flash').show();
});
You can also use generateXMLfile.js.erb for complete action.
It's not a good idea to have 5-25 seconds requests.
It can render your application unresponsive when multiple users start uploading files simultaneously. Or you can hit a timeout limit on your web server.
You should use some background processing tool, here are some options:
delayed job (https://github.com/collectiveidea/delayed_job)
sidekiq (http://sidekiq.org/)
resque (http://resquework.org/)
Delayed job is the simplest one, Sidekiq and Resque are a little bit more complex and they require you to install Redis.
When background processing finishes you can use some Websocket-based tool to send a message to your frontend. Pusher (http://pusher.com/) is one of such tools.

How to Make the Controller wait for a Delayed Job while the rest of the App continues on?

(This question is a follow-up to How do I handle long requests for a Rails App so other users are not delayed too much? )
A user submits an answer to my Rails app and it gets checked in the back-end for up to 10 seconds. This would cause delays for all other users, so I'm trying out the delayed_job gem to move the checking to a Worker process. The Worker code returns the results back to the controller. However, the controller doesn't realize it's supposed to wait patiently for the results, so it causes an error.
How do I get the controller to wait for the results and let the rest of the app handle simple requests meanwhile?
In Javascript, one would use callbacks to call the function instead of returning a value. Should I do the same thing in Ruby and call back the controller from the Worker?
Update:
Alternatively, how can I call a controller method from the Worker? Then I could just call the relevant actions when its done.
This is the relevant code:
Controller:
def submit
question = Question.find params[:question]
user_answer = params[:user_answer]
#result, #other_stuff = SubmitWorker.new.check(question, user_answer)
render_ajax
end
submit_worker.rb :
class SubmitWorker
def check
#lots of code...
end
handle_asynchronously :check
end
Using DJ to offload the work is absolutely fine and normal, but making the controller wait for the response rather defeats the point.
You can add some form of callback to the end of your check method so that when the job finishes your user can be notified.
You can find some discussion on performing notifications in this question: push-style notifications simliar to Facebook with Rails and jQuery
Alternatively you can have your browser periodically call a controller action that checks for the results of the job - the results would ideally be an ActiveRecord object. Again you can find discussion on periodic javascript in this question: Rails 3 equivalent for periodically_call_remote
I think what you are trying to do here is little contradicting, because you use delayed_job when do done want to interrupt the control flow (so your users don't want to want until the request completes).
But if you want your controller to want until you get the results, then you don't want to use background processes like delayed_job.
You might want to think of different way of notifying the user, after you have done your checking, while keeping the background process as it is.

Asynchronous GET request in Rails

I'm working on a Ruby on Rails app that relies on my app making some simple URL calls for user metrics. For part of the tracking I need to make a server-side call prior to the rendering of my index page. This is achieved by calling a specially formatted URL. Currently I'm achieving this in the following way:
url = URI.parse('https://example.tracking.url')
result = Net::HTTP.start(url.host, use_ssl: true, verify_mode: OpenSSL::SSL::VERIFY_NONE) do
|http| http.get url.request_uri, 'User-Agent' => 'MyLib v1.2'
end
The loading of my page seems to be, at times, somewhat delayed. Short of it being a Database latency issue I assume it's just that sometimes the URL takes a extra time to respond and that this is a synchronous request. What is the best way to make asynchronous requests in Rails, Threads maybe? Thanks.
Have you looked into using a delayed job or Thread.new?
I would move it to a helper method and then call Thread.new on the helper method. Personally, I like using delayed_job for handling things that may present a delay with the user interface.

Resources