scout_apm 6.2.1 → 6.3.0
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/CHANGELOG.markdown +4 -0
- 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/request_manager_test.rb +70 -0
- metadata +2 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: fe18e4045997fdf6b731025ee98c13e2702096a0cfd13ac5f0f651d7a7f4a4db
|
|
4
|
+
data.tar.gz: d9b4593ef2b8d5df4983d7b93cb9eaf5cc3f7e6916d8288ae9814e59dd913007
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: a5b838d8e12286496038899c7dd006b6c3d6507061bcba1a963fc10664fa96e36c603f71e315430eda9eeaa84dabadb21f91dee9e9aa9baf7009f6e03d8f3d9d
|
|
7
|
+
data.tar.gz: bee9ed547611139d56eb6f88cf6c1e730dc77dbdf575e89a8b1cd95f86756687174c78b64cf00acd389a74e90198f9750aec334e2dfba195340a0a9753c280bd
|
data/CHANGELOG.markdown
CHANGED
|
@@ -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
|
@@ -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.0
|
|
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
|