Skip to content

Commit 4c3f0a6

Browse files
authored
fix: ensure instrumentation durations are unique during threaded executions (#658)
1 parent 360697f commit 4c3f0a6

File tree

3 files changed

+51
-5
lines changed

3 files changed

+51
-5
lines changed

lib/honeybadger/backend/test.rb

+5-1
Original file line numberDiff line numberDiff line change
@@ -42,13 +42,17 @@ def check_ins
4242
self.class.check_ins
4343
end
4444

45+
def events
46+
self.class.events
47+
end
48+
4549
def notify(feature, payload)
4650
notifications[feature] << payload
4751
super
4852
end
4953

5054
def event(payload)
51-
events << payload
55+
events.concat(payload.dup)
5256
super
5357
end
5458

lib/honeybadger/notification_subscriber.rb

+3-4
Original file line numberDiff line numberDiff line change
@@ -6,17 +6,16 @@ class NotificationSubscriber
66
include Honeybadger::InstrumentationHelper
77

88
def start(name, id, payload)
9-
@start_time = ::Process.clock_gettime(::Process::CLOCK_MONOTONIC)
9+
payload[:_start_time] = ::Process.clock_gettime(::Process::CLOCK_MONOTONIC)
1010
end
1111

1212
def finish(name, id, payload)
13-
@finish_time = ::Process.clock_gettime(::Process::CLOCK_MONOTONIC)
14-
13+
finish_time = ::Process.clock_gettime(::Process::CLOCK_MONOTONIC)
1514
return unless process?(name, payload)
1615

1716
payload = {
1817
instrumenter_id: id,
19-
duration: ((@finish_time - @start_time) * 1000).round(2)
18+
duration: ((finish_time - payload.delete(:_start_time)) * 1000).round(2)
2019
}.merge(format_payload(payload).compact)
2120

2221
record(name, payload)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
require_relative "../rails_helper"
2+
require "honeybadger/notification_subscriber"
3+
4+
class TestSubscriber < Honeybadger::NotificationSubscriber; end
5+
6+
describe "Rails Insights Notification Subscribers", if: RAILS_PRESENT, type: :request do
7+
load_rails_hooks(self)
8+
9+
before do
10+
Honeybadger.config[:"insights.enabled"] = true
11+
Honeybadger.config[:"events.batch_size"] = 0
12+
13+
Honeybadger::Backend::Test.events.clear
14+
end
15+
16+
it "records correct durations for concurrent notifications" do
17+
mutex, sequence = Mutex.new, 1
18+
allow(Process).to receive(:clock_gettime).with(Process::CLOCK_MONOTONIC) do
19+
if caller_locations.any? { |loc| loc.base_label == "finish" }
20+
10
21+
else
22+
mutex.synchronize { sequence += 1 }
23+
end
24+
end
25+
26+
ActiveSupport::Notifications.subscribe("test.timing", TestSubscriber.new)
27+
28+
# Create multiple threads that will fire notifications
29+
threads = 5.times.map do
30+
Thread.new do
31+
ActiveSupport::Notifications.instrument("test.timing") do
32+
sleep(0.1)
33+
end
34+
end
35+
end
36+
37+
threads.each(&:join)
38+
# Wait for all events to be processed
39+
sleep(0.2)
40+
41+
expect(Honeybadger::Backend::Test.events.map { |e| e[:duration] }.uniq.length).to eq(5)
42+
end
43+
end

0 commit comments

Comments
 (0)