Sasha
Sasha

Reputation: 6466

Rails Controller - Return and then perform complex action (without worker)

I want to create an API endpoint in my app that takes data and performs a complex, time-consuming operation with it, but only after returning that the data has been received.

As in, I'd love to be able to do something like this:

def action
  render json: { data_received: params[:whatever].present? }
  perform_calculations( params[:whatever] )
end

Unfortunately, as I understand it, Ruby/Rails is synchronous, and requires that a controller action end in a render/redirect/head statement of some sort.

Ordinarily, I'd think of accomplishing this with a background worker, like so:

 def action
   DelayedJobActionPerformer.perform_later(params[:whatever])
   render { data_received: params[:whatever].present? }
 end

But a worker costs (on Heroku) a fair amount of monthly money for a beginning app like this, and I'm looking for alternatives. Is there any alternative to background workers you can think of to return from the action and then perform the behavior?

I'm thinking of maybe creating a separate Node app or something that can start an action and then respond, but that's feeling ridiculous. I guess the architecture in my mind would involve a main Rails app which performs most of the behavior, and a lightweight Node app that acts as the API endpoint, which can receive a request, respond that it's been received, and then send on the data to be performed by that first Rails app, or another. But it feels excessive, and also like just kicking the problem down the road.

At any rate, whether or not I end up having to buy a worker or few, I'd love to know if this sort of thing is feasible, and whether using an external API as a quasi-worker makes sense (particularly given the general movement towards breaking up application concerns).

Upvotes: 0

Views: 260

Answers (2)

Sharvy Ahmed
Sharvy Ahmed

Reputation: 7405

Heard about sucker_punch, you can give it a try. This will run in single webprocess but the downside is that if the web processes is restarted and there are jobs that haven't yet been processed, they will be lost. So not recommended for critical background tasks.

Upvotes: 1

Kamen
Kamen

Reputation: 699

Not really...

Well you can spawn a new thread:

thread = Thread.new { perform_calculations( params[:whatever] ) }

And not call thread.join, but that is highly unreliable, because that thread will be killed if the main thread terminates.

I don't know how things with cron jobs are in Heroku, but another option is to have a table with pending jobs where you save params[:whatever] and have a rake task that is triggered with cron periodically to check and perform any pending tasks. This solution is a (really) basic worker implementation.

Upvotes: 2

Related Questions