Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Instrument resque without forking #514

Merged
merged 12 commits into from
Dec 23, 2024
1 change: 1 addition & 0 deletions CHANGELOG.markdown
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# Unreleased
- Fix undeclared logger in grape instruments (#510)
- Drop guaranteed support for Rubies <= 2.4
- Instrument Resque without relying on forking per job (#514)

# 5.4.0
* Add support for GoodJob (#506)
Expand Down
2 changes: 1 addition & 1 deletion lib/scout_apm/agent.rb
Original file line number Diff line number Diff line change
Expand Up @@ -208,7 +208,7 @@ def start_error_service_background_worker

@error_service_background_worker = ScoutApm::BackgroundWorker.new(context, ERROR_SEND_FREQUENCY)
@error_service_background_worker_thread = Thread.new do
@error_service_background_worker.start {
@error_service_background_worker.start {
periodic_work.run
}
end
Expand Down
40 changes: 13 additions & 27 deletions lib/scout_apm/background_job_integrations/resque.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,7 @@ def name

def present?
defined?(::Resque) &&
::Resque.respond_to?(:before_first_fork) &&
::Resque.respond_to?(:after_fork)
::Resque.respond_to?(:before_first_fork)
end

# Lies. This forks really aggressively, but we have to do handling
Expand All @@ -19,45 +18,29 @@ def forking?
end

def install
install_before_fork
install_after_fork
install_before_first_fork
install_instruments
end

def install_before_fork
def install_before_first_fork
::Resque.before_first_fork do
begin
# Don't check fork_per_job here in case some workers fork_per_job and some don't.
if ScoutApm::Agent.instance.context.config.value('start_resque_server_instrument')
ScoutApm::Agent.instance.start
ScoutApm::Agent.instance.context.start_remote_server!(bind, port)
else
logger.info("Not starting remote server due to 'start_resque_server_instrument' setting")
end
rescue Errno::EADDRINUSE
ScoutApm::Agent.instance.context.logger.warn "Error while Installing Resque Instruments, Port #{port} already in use. Set via the `remote_agent_port` configuration option"
logger.warn "Error while Installing Resque Instruments, Port #{port} already in use. Set via the `remote_agent_port` configuration option"
rescue => e
ScoutApm::Agent.instance.context.logger.warn "Error while Installing Resque before_first_fork: #{e.inspect}"
logger.warn "Error while Installing Resque before_first_fork: #{e.inspect}"
end
end
end

def install_after_fork
::Resque.after_fork do
begin
ScoutApm::Agent.instance.context.become_remote_client!(bind, port)
inject_job_instrument
rescue => e
ScoutApm::Agent.instance.context.logger.warn "Error while Installing Resque after_fork: #{e.inspect}"
end
end
end

# Insert ourselves into the point when resque turns a string "TestJob"
# into the class constant TestJob, and insert our instrumentation plugin
# into that constantized class
#
# This automates away any need for the user to insert our instrumentation into
# each of their jobs
def inject_job_instrument
def install_instruments
::Resque::Job.class_eval do
def payload_class_with_scout_instruments
klass = payload_class_without_scout_instruments
Expand All @@ -80,9 +63,12 @@ def port
end

def config
@config || ScoutApm::Agent.instance.context.config
@config ||= ScoutApm::Agent.instance.context.config
end

def logger
@logger ||= ScoutApm::Agent.instance.context.logger
end
end
end
end

33 changes: 31 additions & 2 deletions lib/scout_apm/instruments/resque.rb
Original file line number Diff line number Diff line change
@@ -1,6 +1,15 @@
module ScoutApm
module Instruments
module Resque
def before_perform_become_client(*args)
# Don't become remote client if explicitly disabled or if forking is disabled to force synchronous recording.
if config.value('start_resque_server_instrument') && forking?
ScoutApm::Agent.instance.context.become_remote_client!(bind, port)
else
logger.debug("Not becoming remote client due to 'start_resque_server_instrument' setting or 'fork_per_job' setting")
end
end

def around_perform_with_scout_instruments(*args)
job_name = self.to_s
queue = find_queue
Expand All @@ -17,7 +26,6 @@ def around_perform_with_scout_instruments(*args)
started_queue = true
req.start_layer(ScoutApm::Layer.new('Job', job_name))
started_job = true

yield
rescue => e
req.error!
Expand All @@ -33,7 +41,28 @@ def find_queue
return queue if self.respond_to?(:queue)
return "unknown"
end

private

def bind
config.value("remote_agent_host")
end

def port
config.value("remote_agent_port")
end

def config
@config ||= ScoutApm::Agent.instance.context.config
end

def logger
@logger ||= ScoutApm::Agent.instance.context.logger
end

def forking?
@forking ||= ENV["FORK_PER_JOB"] != "false"
end
end
end
end

Loading