Skip to content

Commit

Permalink
Instrument resque without forking (#514)
Browse files Browse the repository at this point in the history
Co-authored-by: Mitch Hartweg <[email protected]>
  • Loading branch information
mitchh456 and Mitch Hartweg authored Dec 23, 2024
1 parent 392dbb8 commit 6a3133e
Show file tree
Hide file tree
Showing 4 changed files with 46 additions and 30 deletions.
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

0 comments on commit 6a3133e

Please sign in to comment.