mirror of
https://github.com/openfoodfoundation/openfoodnetwork
synced 2026-03-04 02:31:33 +00:00
Isolate report rendering in separate child process
Sidekiq doesn't have any features to limit memory usage or execution time. We need a separate process for this. Forking avoids the boot time of a fresh process and copy-on-write ensures minimal memory overheads.
This commit is contained in:
@@ -695,6 +695,7 @@ Rails/ApplicationJob:
|
||||
- 'app/jobs/report_job.rb'
|
||||
- 'app/jobs/subscription_confirm_job.rb'
|
||||
- 'app/jobs/subscription_placement_job.rb'
|
||||
- 'spec/services/job_processor_spec.rb'
|
||||
|
||||
# Offense count: 1
|
||||
# This cop supports unsafe autocorrection (--autocorrect-all).
|
||||
|
||||
@@ -55,10 +55,11 @@ module Admin
|
||||
|
||||
def render_report_as(format)
|
||||
if OpenFoodNetwork::FeatureToggle.enabled?(:background_reports, spree_current_user)
|
||||
job = ReportJob.perform_later(
|
||||
job = ReportJob.new
|
||||
JobProcessor.perform_forked(
|
||||
job,
|
||||
report_class, spree_current_user, params, format
|
||||
)
|
||||
sleep 1 until job.done?
|
||||
|
||||
# This result has been rendered by Rails in safe mode already.
|
||||
job.result.html_safe # rubocop:disable Rails/OutputSafety
|
||||
|
||||
17
app/services/job_processor.rb
Normal file
17
app/services/job_processor.rb
Normal file
@@ -0,0 +1,17 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
# Forks into a separate process to contain memory usage and timeout errors.
|
||||
class JobProcessor
|
||||
def self.perform_forked(job, *args)
|
||||
fork do
|
||||
Process.setproctitle("Job worker #{job.job_id}")
|
||||
job.perform(*args)
|
||||
|
||||
# Exit is not a good idea within a Rails process but Rubocop doesn't know
|
||||
# that we are in a forked process.
|
||||
exit # rubocop:disable Rails/Exit
|
||||
end
|
||||
|
||||
Process.waitall
|
||||
end
|
||||
end
|
||||
31
spec/services/job_processor_spec.rb
Normal file
31
spec/services/job_processor_spec.rb
Normal file
@@ -0,0 +1,31 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
|
||||
class TestJob < ActiveJob::Base
|
||||
def initialize
|
||||
@file = Tempfile.new("test-job-result")
|
||||
super
|
||||
end
|
||||
|
||||
def perform(message)
|
||||
@file.write(message)
|
||||
end
|
||||
|
||||
def result
|
||||
@file.rewind
|
||||
@file.read
|
||||
end
|
||||
end
|
||||
|
||||
describe JobProcessor do
|
||||
describe ".perform_forked" do
|
||||
let(:job) { TestJob.new }
|
||||
|
||||
it "executes a job" do
|
||||
JobProcessor.perform_forked(job, "hello")
|
||||
|
||||
expect(job.result).to eq "hello"
|
||||
end
|
||||
end
|
||||
end
|
||||
Reference in New Issue
Block a user