Skip to content

Commit 141bfc9

Browse files
author
Willem Homan
committed
refactor(platform): PAYMENTS-11567 JobPayload.for never raises so worker instrumentation needs no guards
1 parent 851f233 commit 141bfc9

3 files changed

Lines changed: 26 additions & 15 deletions

File tree

lib/bigcommerce/prometheus/integrations/resque/job_metrics.rb

Lines changed: 10 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -137,26 +137,21 @@ def install_hooks
137137
# - perform duration: after super
138138
# The duration timer starts immediately before super so the
139139
# observation matches the documented fork-to-waitpid window and
140-
# excludes payload parsing and the queue-latency push. The ensure
141-
# is guarded so instrumentation failures can never mask a job
142-
# exception.
140+
# excludes payload parsing and the queue-latency push.
141+
# JobPayload.for never raises and the record_* methods rescue
142+
# internally, so instrumentation cannot break the job or mask
143+
# its exceptions.
143144
module WorkerInstrumentation
144145
def perform_with_fork(job, &block)
145-
payload = begin
146-
JobPayload.for(job)
147-
rescue StandardError
148-
nil
149-
end
150-
JobMetrics.record_queue_latency(payload) if payload
146+
payload = JobPayload.for(job)
147+
JobMetrics.record_queue_latency(payload)
151148
started_at = Process.clock_gettime(Process::CLOCK_MONOTONIC)
152149
super
153150
ensure
154-
if payload && started_at
155-
JobMetrics.record_perform_duration(
156-
payload,
157-
Process.clock_gettime(Process::CLOCK_MONOTONIC) - started_at
158-
)
159-
end
151+
JobMetrics.record_perform_duration(
152+
payload,
153+
Process.clock_gettime(Process::CLOCK_MONOTONIC) - started_at
154+
)
160155
end
161156
end
162157
end

lib/bigcommerce/prometheus/integrations/resque/job_payload.rb

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,10 @@ class Resque
4141
module JobPayload
4242
class << self
4343
##
44+
# Never raises: instrumentation must not break job execution, so
45+
# a payload object is always returned. Unexpected failures
46+
# degrade to a vanilla payload labelled 'unknown'.
47+
#
4448
# @param [Resque::Job] resque_job
4549
# @return [ActiveJobPayload, VanillaResquePayload]
4650
#
@@ -50,6 +54,8 @@ def for(resque_job)
5054

5155
inner = activejob_inner(payload)
5256
inner ? ActiveJobPayload.new(inner) : VanillaResquePayload.new(payload)
57+
rescue StandardError
58+
VanillaResquePayload.new({})
5359
end
5460

5561
private

spec/bigcommerce/prometheus/integrations/resque/job_payload_spec.rb

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,5 +110,15 @@ def active_job_payload(job_class: 'MyJob', enqueued_at: nil, scheduled_at: nil)
110110
it "normalizes a non-Hash payload so the label falls back to 'unknown'" do
111111
expect(described_class.for(resque_job_double(nil)).job_class).to eq('unknown')
112112
end
113+
114+
it "returns a VanillaResquePayload labelled 'unknown' when payload access raises" do
115+
job = double('Resque::Job')
116+
allow(job).to receive(:payload).and_raise(StandardError, 'boom')
117+
118+
payload = described_class.for(job)
119+
120+
expect(payload).to be_a(Bigcommerce::Prometheus::Integrations::Resque::VanillaResquePayload)
121+
expect(payload.job_class).to eq('unknown')
122+
end
113123
end
114124
end

0 commit comments

Comments
 (0)