@@ -8,21 +8,36 @@ module DelayedJob
88 begin
99 require "delayed/plugin"
1010
11+ UNKNOWN = "<Delayed::Job Unknown>"
12+
1113 class Plugin < ::Delayed ::Plugin
1214 callbacks do |lifecycle |
1315 lifecycle . around ( :perform ) { |worker , job , &block | sk_instrument ( worker , job , &block ) }
14-
1516 lifecycle . after ( :error ) { |_worker , _job | Skylight . trace &.segment = "error" }
17+ lifecycle . after ( :failure ) { |_worker , _job | Skylight . trace &.segment = "error" }
1618 end
1719
1820 class << self
1921 include Skylight ::Util ::Logging
2022
23+ # This is called quite early in Delayed::Worker
24+ #
25+ # Typically, the `:perform` lifecycle hook is called before the
26+ # `payload_object` has been deserialized, so we can't name the
27+ # trace yet.
28+ #
29+ # If we call `payload_object` here, we would move the work of
30+ # loading the object ahead of where it naturally happens, which
31+ # means the database load time won't be instrumented. On the other
32+ # hand, should the deserialization fail, we would have moved the
33+ # timing of the error as well. Crucially – it would have moved it
34+ # outside of the spot where these errors are normally caught and
35+ # reported by the worker.
36+ #
37+ # See https://github.com/skylightio/skylight-ruby/issues/491
2138 def sk_instrument ( _worker , job )
22- endpoint = Skylight ::Probes ::DelayedJob . handler_name ( job )
23-
2439 Skylight . trace (
25- endpoint ,
40+ UNKNOWN ,
2641 "app.delayed_job.worker" ,
2742 "Delayed::Worker#run" ,
2843 component : :worker ,
@@ -41,15 +56,6 @@ def sk_instrument(_worker, job)
4156 $stderr. puts "[SKYLIGHT] The delayed_job probe was requested, but Delayed::Plugin was not defined."
4257 end
4358
44- UNKNOWN = "<Delayed::Job Unknown>"
45-
46- def self . handler_name ( job )
47- payload_object =
48- job . respond_to? ( :payload_object_without_sk ) ? job . payload_object_without_sk : job . payload_object
49-
50- payload_object_name ( payload_object )
51- end
52-
5359 def self . payload_object_name ( payload_object )
5460 if payload_object . is_a? ( ::Delayed ::PerformableMethod )
5561 payload_object . display_name
@@ -77,18 +83,27 @@ def self.payload_object_source_meta(payload_object)
7783
7884 class InstrumentationProxy < SimpleDelegator
7985 def perform
80- source_meta = Skylight ::Probes ::DelayedJob . payload_object_source_meta ( __getobj__ )
81-
82- opts = {
83- category : "app.delayed_job.job" ,
84- title : format_source ( *source_meta ) ,
85- meta : {
86- source_location_hint : source_meta
87- } ,
88- internal : true
89- }
90-
91- Skylight . instrument ( opts ) { __getobj__ . perform }
86+ if ( trace = Skylight . instrumenter &.current_trace )
87+ if trace . endpoint == UNKNOWN
88+ # At this point, deserialization was, by definition, successful.
89+ # So it'd be safe to set the endpoint name based on the payload
90+ # object here.
91+ trace . endpoint = Skylight ::Probes ::DelayedJob . payload_object_name ( __getobj__ )
92+ end
93+
94+ source_meta = Skylight ::Probes ::DelayedJob . payload_object_source_meta ( __getobj__ )
95+
96+ opts = {
97+ category : "app.delayed_job.job" ,
98+ title : format_source ( *source_meta ) ,
99+ meta : {
100+ source_location_hint : source_meta
101+ } ,
102+ internal : true
103+ }
104+
105+ Skylight . instrument ( opts ) { __getobj__ . perform }
106+ end
92107 end
93108
94109 # Used by Delayed::Backend::Base to determine Job#name
0 commit comments