My RoR application triggers data treatment scripts (SAS technology) from Linux operating system. An execute method in the Scheduler::ProductionExecutionsController drives the interactions with the scripts.
Scheduler::ProductionExecutionsController
# POST /production_executions/1/execute
def execute
@production_execution = ProductionExecution.find(params[:id])
@production_execution.update_attributes(started_at: Time.now,
status_id: statuses.find { |x| x["code"] == "RUNNING" }.id)
--- Scripts interaction ---
@production_execution.update_attributes(ended_at: Time.now,
status_id: statuses.find { |x| x["code"] == "FINISHED" }.id,
source_records_count: global_input_count,
processed_count: global_output_count,
error_message: nil
)
respond_to do |format|
format.html { redirect_back fallback_location: @production_execution, notice: @msg }
format.js
end
This method is called using this syntax: link_to "Go!", execute_scheduler_production_execution_path(execution)
. The route is defined and it works as expected.
As some scripts can take over a minute to execute, I need to execute the scripts in dedicated jobs, and sometimes schedule them. So I installed Sidekiq and defined a Scheduler::ScriptWorker :
class Scheduler::ScriptWorker
include Sidekiq::Worker
sidekiq_options queue: :default, tags: ['script']
def perform(execution_id)
puts "Sidekiq job start"
puts execution_id
redirect_to execute_scheduler_production_execution_path(execution_id) and return
end
end
The executions are queued with Scheduler::ScriptWorker.perform_async(@execution.id)
Sidekiq operates correctly, but each time the worker is invoked, it raise the following error:
WARN: NoMethodError: undefined method `execute_scheduler_production_execution_path' for #<Scheduler::ScriptWorker:0x000000000b15ded8>
Is it the right way to do this, and how can I solve this issue? Thanks for you help!
The short answer is: you can't
You can't perform a redirect from an asynchronous Sidekiq worker. When the user clicks "Go!", an HTTP request is made to your Rails web application. Rails hands the request off to a controller action, which in your case spawns an async sidekiq job. The controller action logic continues and Rails completes the HTTP request with an HTTP response, even if your sidekiq worker is still running. You cannot broadcast a message from a sidekiq worker back to the user who initiated the task
I'm sorry this does not resolve your issue. You'll need to take a different approach.
As stated by Sean Huber, converting a front-end job into a backend job requires a touch of architecture. Through refactoring, the experimental execute method was moved to a helper and renamed execute_ssh . A new run_once method in the controller fires the helper's execute_ssh method and reloads the page.
script_worker.rb
class Scheduler::ScriptWorker
include Sidekiq::Worker
include SchedulerExecutionHelper
include ParametersHelper
sidekiq_options queue: :default, tags: ['script'], retry: false
def perform(execution_id)
puts "Sidekiq job start"
puts execution_id
execute_ssh execution_id
end
end
scheduler_execution_helper.rb
def execute_ssh(execution_id)
puts "--- Switched to helper"
@production_execution = ProductionExecution.find(execution_id)
puts @production_execution.id
@production_execution.update_attributes(started_at: Time.now, status_id: statuses.find { |x| x["code"] == "RUNNING" }.id)
--- Scripts interaction ---
@production_execution.update_attributes(ended_at: Time.now,
status_id: statuses.find { |x| x["code"] == "FINISHED" }.id,
source_records_count: global_input_count,
processed_count: global_output_count,
error_message: nil
)
end
production_schedules_controller
def run_once
@job = @production_schedule.parent
@execution = @job.production_executions.build(playground_id: @job.playground_id,
production_job_id: @job.id,
environment_id: @production_schedule.environment_id,
owner_id: current_user.id,
status_id: options_for('Statuses', 'Scheduler').find { |x| x["code"] == "READY" }.id || 0)
if @execution.save
@job.production_events.where(production_execution_id: nil).each do |event|
execution_event = event.dup
execution_event.production_execution_id = @execution.id
execution_event.return_value = 0
execution_event.status_id = statuses.find { |x| x["code"] == "READY" }.id
execution_event.save
end
Scheduler::ScriptWorker.perform_async(@execution.id)
redirect_to scheduler_production_job_path(@job)
else
end
end
This way, the controller remains thin, the worker can be easily reused, and the logic is in the helper module.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.