scout_apm 6.2.1 → 6.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.gitignore +1 -0
- data/CHANGELOG.markdown +8 -0
- data/gems/instruments-ruby4.gemfile +1 -0
- data/gems/instruments.gemfile +5 -0
- data/gems/typhoeus.gemfile +1 -0
- data/lib/scout_apm/instruments/grape.rb +32 -22
- data/lib/scout_apm/request_manager.rb +14 -2
- data/lib/scout_apm/tracked_request.rb +22 -4
- data/lib/scout_apm/version.rb +1 -1
- data/test/unit/instruments/grape_test.rb +25 -0
- data/test/unit/instruments/httpx_test.rb +36 -5
- data/test/unit/instruments/typhoeus_test.rb +17 -1
- data/test/unit/request_manager_test.rb +70 -0
- metadata +3 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 97692f03252aa5c2af4a4f87092742319b9088dace2f3dbcb4c48af766dfb848
|
|
4
|
+
data.tar.gz: 30bc39d151abec5d26723b2de0626319cf80685548a0f68da48074e283c72515
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 0c6a06a5e08a3a753a7110568f44d9c9c6b0163aeeabf3be281a2de9df6a2357f14515de7419c9dedd79e8f7e8266dca67cbf58b3dbac2e6efb54f283ba4d433
|
|
7
|
+
data.tar.gz: 13bacea25583bf396a3e4d0edee2e957a756296a3c4fcf6083a5a42b5e0bb2e57c90a9751dd6b04f7d86f0315d8c30222ba4d3d0c9c679aa2d73eb1eb38b2eb3
|
data/.gitignore
CHANGED
data/CHANGELOG.markdown
CHANGED
|
@@ -1,5 +1,13 @@
|
|
|
1
1
|
# Pending
|
|
2
2
|
|
|
3
|
+
# 6.3.1
|
|
4
|
+
|
|
5
|
+
- Capture route names for Grape 4 (#635)
|
|
6
|
+
|
|
7
|
+
# 6.3.0
|
|
8
|
+
|
|
9
|
+
- Fix span leak on `ActionController::Live` streaming requests (#631)
|
|
10
|
+
|
|
3
11
|
# 6.2.1
|
|
4
12
|
|
|
5
13
|
- Fix `SystemStackError` with `grape >= 3.3.0` by falling back to `prepend` instrumentation when Grape owns `Endpoint#run` itself (#624)
|
data/gems/instruments.gemfile
CHANGED
|
@@ -7,4 +7,9 @@ gem 'moped'
|
|
|
7
7
|
gem 'actionpack'
|
|
8
8
|
gem 'actionview'
|
|
9
9
|
gem 'httpx'
|
|
10
|
+
gem 'webmock'
|
|
10
11
|
gem 'grape'
|
|
12
|
+
# json 3.0 (released 2026-09) dropped the `quirks_mode` keyword that Grape 3's
|
|
13
|
+
# ActiveSupport JSON formatter still passes, so any Grape 3 JSON request raises
|
|
14
|
+
# ArgumentError. Pin to the 2.x line until this matrix moves off Grape 3.
|
|
15
|
+
gem 'json', '< 3'
|
data/gems/typhoeus.gemfile
CHANGED
|
@@ -51,6 +51,36 @@ module ScoutApm
|
|
|
51
51
|
end
|
|
52
52
|
end
|
|
53
53
|
|
|
54
|
+
module GrapeEndpointNaming
|
|
55
|
+
# Grape >= 4 moved endpoint route metadata off the public `options`
|
|
56
|
+
# Hash and onto `Endpoint#config` (a Grape::Endpoint::Options Data
|
|
57
|
+
# object). Support both so the instrument works across Grape 3 and 4.
|
|
58
|
+
def self.name_for(endpoint)
|
|
59
|
+
# `config` is a protected reader on Grape::Endpoint, so a public
|
|
60
|
+
# respond_to? misses it: check with `true` and read via `send`.
|
|
61
|
+
if endpoint.respond_to?(:config, true) && endpoint.send(:config).respond_to?(:http_methods)
|
|
62
|
+
config = endpoint.send(:config)
|
|
63
|
+
method = config.http_methods && config.http_methods.first
|
|
64
|
+
api = config.api || (endpoint.respond_to?(:api) ? endpoint.api : nil)
|
|
65
|
+
path = config.path && config.path.first
|
|
66
|
+
else
|
|
67
|
+
method = endpoint.options[:method] && endpoint.options[:method].first
|
|
68
|
+
api = endpoint.options[:for]
|
|
69
|
+
path = endpoint.options[:path] && endpoint.options[:path].first
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
["Grape",
|
|
73
|
+
method,
|
|
74
|
+
api.to_s,
|
|
75
|
+
endpoint.namespace.sub(%r{\A/}, ''), # removing leading slashes
|
|
76
|
+
path,
|
|
77
|
+
].compact.map { |n| n.to_s }.join("/")
|
|
78
|
+
rescue => e
|
|
79
|
+
ScoutApm::Agent.instance.context.logger.info("Error getting Grape Endpoint Name. Error: #{e.message}. Options: #{endpoint.options.inspect}")
|
|
80
|
+
"Grape/Unknown"
|
|
81
|
+
end
|
|
82
|
+
end
|
|
83
|
+
|
|
54
84
|
module GrapeEndpointInstruments
|
|
55
85
|
def run_with_scout_instruments(*args)
|
|
56
86
|
request = ::Grape::Request.new(env || args.first)
|
|
@@ -64,17 +94,7 @@ module ScoutApm
|
|
|
64
94
|
|
|
65
95
|
req.set_headers(request.headers)
|
|
66
96
|
|
|
67
|
-
|
|
68
|
-
name = ["Grape",
|
|
69
|
-
self.options[:method].first,
|
|
70
|
-
self.options[:for].to_s,
|
|
71
|
-
self.namespace.sub(%r{\A/}, ''), # removing leading slashes
|
|
72
|
-
self.options[:path].first,
|
|
73
|
-
].compact.map{ |n| n.to_s }.join("/")
|
|
74
|
-
rescue => e
|
|
75
|
-
ScoutApm::Agent.instance.context.logger.info("Error getting Grape Endpoint Name. Error: #{e.message}. Options: #{self.options.inspect}")
|
|
76
|
-
name = "Grape/Unknown"
|
|
77
|
-
end
|
|
97
|
+
name = GrapeEndpointNaming.name_for(self)
|
|
78
98
|
|
|
79
99
|
req.start_layer( ScoutApm::Layer.new("Controller", name) )
|
|
80
100
|
begin
|
|
@@ -101,17 +121,7 @@ module ScoutApm
|
|
|
101
121
|
|
|
102
122
|
req.set_headers(request.headers)
|
|
103
123
|
|
|
104
|
-
|
|
105
|
-
name = ["Grape",
|
|
106
|
-
self.options[:method].first,
|
|
107
|
-
self.options[:for].to_s,
|
|
108
|
-
self.namespace.sub(%r{\A/}, ''), # removing leading slashes
|
|
109
|
-
self.options[:path].first,
|
|
110
|
-
].compact.map{ |n| n.to_s }.join("/")
|
|
111
|
-
rescue => e
|
|
112
|
-
ScoutApm::Agent.instance.context.logger.info("Error getting Grape Endpoint Name. Error: #{e.message}. Options: #{self.options.inspect}")
|
|
113
|
-
name = "Grape/Unknown"
|
|
114
|
-
end
|
|
124
|
+
name = GrapeEndpointNaming.name_for(self)
|
|
115
125
|
|
|
116
126
|
req.start_layer( ScoutApm::Layer.new("Controller", name) )
|
|
117
127
|
begin
|
|
@@ -7,11 +7,23 @@ module ScoutApm
|
|
|
7
7
|
find || create
|
|
8
8
|
end
|
|
9
9
|
|
|
10
|
-
# Get the current Thread local,
|
|
10
|
+
# Get the current Thread local, detecting and not returning a stale request,
|
|
11
|
+
# nor a request that belongs to a different thread.
|
|
11
12
|
def self.find
|
|
12
13
|
req = Thread.current[:scout_request]
|
|
14
|
+
return nil unless req
|
|
13
15
|
|
|
14
|
-
|
|
16
|
+
# ActionController::Live runs the controller action in a child thread and
|
|
17
|
+
# copies the parent thread's raw Thread.current locals (including
|
|
18
|
+
# :scout_request) into it. Without this check the parent (Rack) thread and
|
|
19
|
+
# the child (streaming) thread would share and concurrently mutate a single
|
|
20
|
+
# TrackedRequest -- racing on @layers/@stopping -- which corrupts the layer
|
|
21
|
+
# stack, drops the transaction, and leaks layers across requests. If the
|
|
22
|
+
# resident request was created on a different thread, treat it as absent so
|
|
23
|
+
# lookup creates a fresh one owned by this thread.
|
|
24
|
+
return nil if req.creating_thread_id != Thread.current.object_id
|
|
25
|
+
|
|
26
|
+
if req.stopping? || req.recorded?
|
|
15
27
|
nil
|
|
16
28
|
else
|
|
17
29
|
req
|
|
@@ -74,9 +74,20 @@ module ScoutApm
|
|
|
74
74
|
@recorder = agent_context.recorder
|
|
75
75
|
@real_request = false
|
|
76
76
|
@transaction_id = ScoutApm::Utils::TransactionId.new.to_s
|
|
77
|
+
# The thread this request was created on. ActionController::Live copies the
|
|
78
|
+
# raw Thread.current locals (including :scout_request) into its streaming
|
|
79
|
+
# child thread, which would otherwise cause two threads to share and race
|
|
80
|
+
# on one TrackedRequest. RequestManager uses this to detect an inherited
|
|
81
|
+
# request and give the child its own instead. See RequestManager.find.
|
|
82
|
+
@creating_thread_id = Thread.current.object_id
|
|
77
83
|
ignore_request! if @recorder.nil?
|
|
78
84
|
end
|
|
79
85
|
|
|
86
|
+
# The object_id of the Thread that created this request. Used to detect a
|
|
87
|
+
# TrackedRequest that was inherited by a different thread (e.g. an
|
|
88
|
+
# ActionController::Live streaming thread) rather than created for it.
|
|
89
|
+
attr_reader :creating_thread_id
|
|
90
|
+
|
|
80
91
|
def layer_count
|
|
81
92
|
@layers.size
|
|
82
93
|
end
|
|
@@ -368,14 +379,21 @@ module ScoutApm
|
|
|
368
379
|
end
|
|
369
380
|
end
|
|
370
381
|
|
|
371
|
-
#
|
|
372
|
-
#
|
|
382
|
+
# True iff this request has a 'Job' layer. Only reliable during recording --
|
|
383
|
+
# see #web? for why.
|
|
373
384
|
def job?
|
|
374
385
|
layer_finder.job != nil
|
|
375
386
|
end
|
|
376
387
|
|
|
377
|
-
#
|
|
378
|
-
#
|
|
388
|
+
# True iff this request has a 'Controller' layer.
|
|
389
|
+
#
|
|
390
|
+
# Only reliable during recording; returns false mid-request even for a real
|
|
391
|
+
# web request. #layer_finder walks root_layer's child *tree*, but layers are
|
|
392
|
+
# attached to that tree only in #stop_layer (via add_child), not in
|
|
393
|
+
# #start_layer. Controller/Job are top-level spans, so they stop last --
|
|
394
|
+
# meaning web?/job? only resolve as the request unwinds into recording.
|
|
395
|
+
# (Negative results aren't memoized, so an early false doesn't poison a
|
|
396
|
+
# later check.)
|
|
379
397
|
def web?
|
|
380
398
|
layer_finder.controller != nil
|
|
381
399
|
end
|
data/lib/scout_apm/version.rb
CHANGED
|
@@ -53,6 +53,31 @@ if (ENV["SCOUT_TEST_FEATURES"] || "").include?("instruments")
|
|
|
53
53
|
assert_includes body_content, 'hello'
|
|
54
54
|
end
|
|
55
55
|
|
|
56
|
+
# Grape >= 4 moved endpoint route metadata (method/path/api) off the
|
|
57
|
+
# public `options` Hash and onto the protected `Endpoint#config` object.
|
|
58
|
+
# Reading the old `options[...]` keys under Grape 4 silently returns nil,
|
|
59
|
+
# so every request gets named "Grape/Unknown" instead of the real
|
|
60
|
+
# endpoint name.
|
|
61
|
+
def test_endpoint_name_reflects_the_route
|
|
62
|
+
captured_names = []
|
|
63
|
+
original_new = ScoutApm::Layer.method(:new)
|
|
64
|
+
ScoutApm::Layer.define_singleton_method(:new) do |type, name, *rest|
|
|
65
|
+
captured_names << name if type == "Controller"
|
|
66
|
+
original_new.call(type, name, *rest)
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
begin
|
|
70
|
+
status, _headers, _body = HelloAPI.call(Rack::MockRequest.env_for('/hello'))
|
|
71
|
+
ensure
|
|
72
|
+
ScoutApm::Layer.define_singleton_method(:new, original_new)
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
assert_equal 200, status
|
|
76
|
+
refute_empty captured_names
|
|
77
|
+
refute_equal "Grape/Unknown", captured_names.first
|
|
78
|
+
assert_equal "Grape/GET/#{HelloAPI}//hello", captured_names.first
|
|
79
|
+
end
|
|
80
|
+
|
|
56
81
|
# Either instrumentation method is valid: alias_method is the default,
|
|
57
82
|
# prepend is used when configured (`use_prepend`/`prepend_instruments`)
|
|
58
83
|
# or when the instrument falls back to it for safety.
|
|
@@ -4,17 +4,34 @@ if (ENV["SCOUT_TEST_FEATURES"] || "").include?("instruments")
|
|
|
4
4
|
require 'scout_apm/instruments/httpx'
|
|
5
5
|
|
|
6
6
|
require 'httpx'
|
|
7
|
+
require 'webmock'
|
|
7
8
|
|
|
8
9
|
class HTTPXTest < Minitest::Test
|
|
10
|
+
include WebMock::API
|
|
11
|
+
|
|
9
12
|
def setup
|
|
13
|
+
super # clears Thread.current[:scout_request] so a stale request from a
|
|
14
|
+
# prior test can't swallow this test's layers
|
|
15
|
+
WebMock.enable!
|
|
16
|
+
WebMock.disable_net_connect!
|
|
17
|
+
|
|
10
18
|
@context = ScoutApm::AgentContext.new
|
|
11
19
|
@recorder = FakeRecorder.new
|
|
12
20
|
ScoutApm::Agent.instance.context.recorder = @recorder
|
|
13
21
|
ScoutApm::Instruments::HTTPX.new(@context).install(prepend: false)
|
|
14
22
|
end
|
|
15
23
|
|
|
24
|
+
def teardown
|
|
25
|
+
WebMock.reset!
|
|
26
|
+
WebMock.allow_net_connect!
|
|
27
|
+
WebMock.disable!
|
|
28
|
+
super
|
|
29
|
+
end
|
|
16
30
|
|
|
17
31
|
def test_httpx
|
|
32
|
+
stub_request(:get, /news\.ycombinator\.com/).to_return(status: 200, body: "")
|
|
33
|
+
stub_request(:get, /google\.com/).to_return(status: 200, body: "")
|
|
34
|
+
|
|
18
35
|
responses = HTTPX.get(
|
|
19
36
|
"https://news.ycombinator.com/news",
|
|
20
37
|
"https://news.ycombinator.com/news?p=2",
|
|
@@ -27,11 +44,18 @@ if (ENV["SCOUT_TEST_FEATURES"] || "").include?("instruments")
|
|
|
27
44
|
end
|
|
28
45
|
|
|
29
46
|
def test_httpx_post_request
|
|
47
|
+
stub_request(:post, /httpbin\.org\/post/).to_return(status: 200, body: "")
|
|
48
|
+
|
|
30
49
|
HTTPX.post("https://httpbin.org/post", json: { test: "data" })
|
|
31
50
|
assert_recorded(@recorder, "HTTP", "POST", "httpbin.org/post")
|
|
32
51
|
end
|
|
33
52
|
|
|
34
53
|
def test_instruments_httpx_error_handling
|
|
54
|
+
# An error response still flows through Session#request, so the layer is
|
|
55
|
+
# started and stopped, and the request is recorded exactly once.
|
|
56
|
+
stub_request(:get, /thisshouldnotexistatall12345\.com/)
|
|
57
|
+
.to_return(status: 500, body: "")
|
|
58
|
+
|
|
35
59
|
begin
|
|
36
60
|
HTTPX.get("https://thisshouldnotexistatall12345.com")
|
|
37
61
|
rescue
|
|
@@ -41,6 +65,10 @@ if (ENV["SCOUT_TEST_FEATURES"] || "").include?("instruments")
|
|
|
41
65
|
end
|
|
42
66
|
|
|
43
67
|
def test_httpx_request_retry
|
|
68
|
+
# A timed-out request is still dispatched through Session#request, so the
|
|
69
|
+
# layer is recorded even though the response errors.
|
|
70
|
+
stub_request(:get, /httpbin\.org\/delay/).to_timeout
|
|
71
|
+
|
|
44
72
|
begin
|
|
45
73
|
HTTPX.with(timeout: { connect_timeout: 0.25, request_timeout: 0.25 })
|
|
46
74
|
.get("https://httpbin.org/delay/5")
|
|
@@ -50,14 +78,17 @@ if (ENV["SCOUT_TEST_FEATURES"] || "").include?("instruments")
|
|
|
50
78
|
end
|
|
51
79
|
|
|
52
80
|
def test_multiple_plugins
|
|
81
|
+
stub_request(:get, /news\.ycombinator\.com/).to_return(status: 200, body: "")
|
|
82
|
+
stub_request(:get, "http://httpbin.org/redirect/2")
|
|
83
|
+
.to_return(status: 302, headers: { "Location" => "http://httpbin.org/redirect/1" })
|
|
84
|
+
stub_request(:get, "http://httpbin.org/redirect/1")
|
|
85
|
+
.to_return(status: 302, headers: { "Location" => "http://httpbin.org/get" })
|
|
86
|
+
stub_request(:get, "http://httpbin.org/get").to_return(status: 200, body: "")
|
|
87
|
+
|
|
53
88
|
session = HTTPX.plugin(:persistent).plugin(:follow_redirects)
|
|
54
89
|
|
|
55
90
|
session.get("https://news.ycombinator.com/news")
|
|
56
|
-
|
|
57
|
-
session.get("http://httpbin.org/redirect/2")
|
|
58
|
-
rescue
|
|
59
|
-
skip "httpbin.org not available"
|
|
60
|
-
end
|
|
91
|
+
session.get("http://httpbin.org/redirect/2")
|
|
61
92
|
|
|
62
93
|
assert_equal 2, @recorder.requests.length, "Expected 2 requests to be recorded"
|
|
63
94
|
end
|
|
@@ -4,15 +4,31 @@ if (ENV["SCOUT_TEST_FEATURES"] || "").include?("typhoeus")
|
|
|
4
4
|
require 'scout_apm/instruments/typhoeus'
|
|
5
5
|
|
|
6
6
|
require 'typhoeus'
|
|
7
|
+
require 'webmock'
|
|
7
8
|
|
|
8
9
|
class TyphoeusTest < Minitest::Test
|
|
10
|
+
include WebMock::API
|
|
11
|
+
|
|
9
12
|
def setup
|
|
13
|
+
super # clears Thread.current[:scout_request] so a stale request from a
|
|
14
|
+
# prior test can't swallow this test's layers
|
|
15
|
+
WebMock.enable!
|
|
16
|
+
WebMock.disable_net_connect!
|
|
17
|
+
stub_request(:any, /example\.com/).to_return(status: 200, body: "")
|
|
18
|
+
|
|
10
19
|
@context = ScoutApm::AgentContext.new
|
|
11
20
|
@recorder = FakeRecorder.new
|
|
12
21
|
ScoutApm::Agent.instance.context.recorder = @recorder
|
|
13
22
|
ScoutApm::Instruments::Typhoeus.new(@context).install(prepend: false)
|
|
14
23
|
end
|
|
15
24
|
|
|
25
|
+
def teardown
|
|
26
|
+
WebMock.reset!
|
|
27
|
+
WebMock.allow_net_connect!
|
|
28
|
+
WebMock.disable!
|
|
29
|
+
super
|
|
30
|
+
end
|
|
31
|
+
|
|
16
32
|
def test_instruments_typhoeus_hydra
|
|
17
33
|
hydra = Typhoeus::Hydra.new
|
|
18
34
|
2.times.map{ hydra.queue(Typhoeus::Request.new("example.com", followlocation: true)) }
|
|
@@ -39,4 +55,4 @@ if (ENV["SCOUT_TEST_FEATURES"] || "").include?("typhoeus")
|
|
|
39
55
|
assert_equal desc, req.root_layer.desc if !desc.nil?
|
|
40
56
|
end
|
|
41
57
|
end
|
|
42
|
-
end
|
|
58
|
+
end
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
require 'test_helper'
|
|
2
|
+
|
|
3
|
+
# Tests for RequestManager's thread-local handling, in particular that a
|
|
4
|
+
# TrackedRequest created on one thread is NOT shared with another thread.
|
|
5
|
+
#
|
|
6
|
+
# ActionController::Live runs the controller action in a child thread and copies
|
|
7
|
+
# the parent (Rack) thread's raw Thread.current locals -- including
|
|
8
|
+
# :scout_request -- into it. Without protection both threads would share and
|
|
9
|
+
# concurrently mutate a single TrackedRequest, racing on @layers/@stopping,
|
|
10
|
+
# which drops the transaction and leaks layers. RequestManager must give the
|
|
11
|
+
# inheriting thread its own request instead.
|
|
12
|
+
class RequestManagerTest < Minitest::Test
|
|
13
|
+
def teardown
|
|
14
|
+
# Never let a test leak the thread-local into sibling tests.
|
|
15
|
+
Thread.current[:scout_request] = nil
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def test_lookup_reuses_the_request_on_the_same_thread
|
|
19
|
+
first = ScoutApm::RequestManager.lookup
|
|
20
|
+
second = ScoutApm::RequestManager.lookup
|
|
21
|
+
|
|
22
|
+
assert_same first, second,
|
|
23
|
+
"lookup should return the same TrackedRequest on repeated calls within one thread"
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def test_lookup_in_a_child_thread_that_inherited_the_local_gets_its_own_request
|
|
27
|
+
parent_request = ScoutApm::RequestManager.lookup
|
|
28
|
+
inherited = Thread.current[:scout_request]
|
|
29
|
+
|
|
30
|
+
child_request = nil
|
|
31
|
+
child_local_before = nil
|
|
32
|
+
|
|
33
|
+
t = Thread.new do
|
|
34
|
+
# Simulate ActionController::Live copying the parent's raw thread local
|
|
35
|
+
# into the streaming child thread.
|
|
36
|
+
Thread.current[:scout_request] = inherited
|
|
37
|
+
child_local_before = Thread.current[:scout_request]
|
|
38
|
+
|
|
39
|
+
child_request = ScoutApm::RequestManager.lookup
|
|
40
|
+
end
|
|
41
|
+
t.join
|
|
42
|
+
|
|
43
|
+
# The child observed the inherited (parent's) request as its raw local...
|
|
44
|
+
assert_same parent_request, child_local_before
|
|
45
|
+
|
|
46
|
+
# ...but lookup must NOT hand it the parent's request to mutate.
|
|
47
|
+
refute_nil child_request
|
|
48
|
+
refute_same parent_request, child_request,
|
|
49
|
+
"a thread that inherited another thread's :scout_request must get its own TrackedRequest"
|
|
50
|
+
|
|
51
|
+
# And the parent's request must be left untouched (still the parent's).
|
|
52
|
+
assert_same parent_request, Thread.current[:scout_request]
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def test_find_treats_a_foreign_thread_request_as_absent
|
|
56
|
+
# A request that reports it was created on a different thread should not be
|
|
57
|
+
# returned by find (so lookup falls through to create).
|
|
58
|
+
foreign = ScoutApm::TrackedRequest.new(ScoutApm::Agent.instance.context, ScoutApm::FakeStore.new)
|
|
59
|
+
foreign.stubs(:creating_thread_id).returns(Thread.current.object_id + 1)
|
|
60
|
+
Thread.current[:scout_request] = foreign
|
|
61
|
+
|
|
62
|
+
assert_nil ScoutApm::RequestManager.find,
|
|
63
|
+
"find should treat a request created on another thread as absent"
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def test_find_returns_a_same_thread_request
|
|
67
|
+
own = ScoutApm::RequestManager.lookup
|
|
68
|
+
assert_same own, ScoutApm::RequestManager.find
|
|
69
|
+
end
|
|
70
|
+
end
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: scout_apm
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 6.
|
|
4
|
+
version: 6.3.1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Derek Haynes
|
|
@@ -486,6 +486,7 @@ files:
|
|
|
486
486
|
- test/unit/remote/route_test.rb
|
|
487
487
|
- test/unit/remote/server_test.rb
|
|
488
488
|
- test/unit/request_histograms_test.rb
|
|
489
|
+
- test/unit/request_manager_test.rb
|
|
489
490
|
- test/unit/sampling_test.rb
|
|
490
491
|
- test/unit/scored_item_set_test.rb
|
|
491
492
|
- test/unit/serializers/payload_serializer_test.rb
|
|
@@ -522,7 +523,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
522
523
|
- !ruby/object:Gem::Version
|
|
523
524
|
version: '0'
|
|
524
525
|
requirements: []
|
|
525
|
-
rubygems_version: 4.0.
|
|
526
|
+
rubygems_version: 4.0.20
|
|
526
527
|
specification_version: 4
|
|
527
528
|
summary: Ruby application performance monitoring
|
|
528
529
|
test_files: []
|